本次主要参考:
https://blog.51cto.com/xfxuezhang/5873175
MCU:STM32F411CEU6,主频96M
外设:SPI2(引脚为PB12、PB13、PB14、PB15,波特率为3M),DMA1(数据流4,通道0)
WS2812B:接收波特率为750Kbps
说明:如果SPI2上挂有多个设备,需要用CS信号控制MOSI的锁存电路。DMA是防止发送相邻两个Byte时中间间隔过大。SPI的MOSI向WS2812B发送数据,每4个SPI的bit表示一个WS2812B的bit码。因为WS2812B要求先传输高位,SPI配置为MSB模式,于是有0b’1100表示WS2812B的1码;0b’1000表示0码。(如果所用主控支持,将SPI的波特率设置在3M~3.2M之间都可以)。每个WS2812B灯珠需要SPI发送12个字节。
相关代码大致如下(已全部测试通过):
1、使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
2、初始化SPI2和DMA1,执行SPI2_DMA_Init(),即可完成初始化
#define SENDBUFF_SIZE (1024*2) // 缓存长度
static uint8_t TX_Buff[SENDBUFF_SIZE]; // 发送缓存
static void SPI2_PortInit(void){
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, 13, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, 14, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, 15, GPIO_AF_SPI2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI2_BusEnd();
}
static void SPI2_TX_DMA_Init(void)
{
/* 通道0,数据流4 */
// DMA结构体
DMA_InitTypeDef DMA_InitStructure;
/* 使能DMA时钟 */
/* 复位初始化DMA数据流 */
DMA_DeInit(DMA1_Stream4);
/* 确保DMA数据流复位完成 */
while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
/* 外设地址 */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(SPI2->DR));
/* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;
/* 方向:存储器到外设 */
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
/* 数据传输量 ,可设置为0, 实际发送时会重新设置*/
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
/* 外设非增量模式 */
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/* 存储器增量模式 */
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/* 外设数据长度:8位 */
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
/* 内存数据长度:8位 */
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/* DMA模式:正常模式 */
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
/* 优先级:高 */
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
/* 禁用FIFO */
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
/* 外设突发单次传输 */
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/* 存储器突发单次传输 */
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/* 初始化DMA Stream */
DMA_Init(DMA1_Stream4, &DMA_InitStructure);
/* 开启传输完成中断 */
DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Stream4, ENABLE);
}
static void SPI2_TX_DMA_NVICInit(void){
// 中断初始化
// 中断结构体
NVIC_InitTypeDef NVIC_InitStructure;
/* DMA发送中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;
/* 抢断优先级 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 响应优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
/* 使能外部中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void SPI2_DMA_Init(void){
SPI_InitTypeDef spi_init_structure;
SPI2_PortInit();
spi_init_structure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi_init_structure.SPI_Mode = SPI_Mode_Master;
spi_init_structure.SPI_DataSize = SPI_DataSize_8b;
spi_init_structure.SPI_CPOL = SPI_CPOL_High;
spi_init_structure.SPI_CPHA = SPI_CPHA_2Edge;
spi_init_structure.SPI_NSS = SPI_NSS_Soft;
spi_init_structure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
spi_init_structure.SPI_FirstBit = SPI_FirstBit_MSB;
spi_init_structure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &spi_init_structure);
SPI_Cmd(SPI2, ENABLE);
SPI2_TX_DMA_Init();
SPI2_TX_DMA_NVICInit();
}
void SPI2_BusStart(void){
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
void SPI2_BusEnd(void){
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void SPI2_DMA_SendBuf(uint32_t SizeLen)
{
//SPI向DMA发出请求,DMA会通过仲裁器自动回应请求
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
// 关闭发送 DMA
DMA_Cmd(DMA1_Stream4, DISABLE);
// 设置发送的数据量
DMA_SetCurrDataCounter(DMA1_Stream4, SizeLen);
// 清空数据
SPI_I2S_ReceiveData(SPI2);
// 擦除DMA标志位
DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
// 片选拉低,接收数据
SPI2_BusStart();
// 开启发送 DMA
DMA_Cmd(DMA1_Stream4, ENABLE);
}
uint8_t *Get_SPI2DMATxBuff(void){
return TX_Buff;
}
void DMA1_Stream4_IRQHandler(void)
{
// DMA 发送完成
if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4))
{
// 清除DMA发送完成标志
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
// 片选拉高,数据发送完毕
SPI2_BusEnd();
}
}
3、WS2812B处理,执行WS2812_Init()完成初始化,WS2812_CloseAll()设置所有WS2812B熄灭,Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data)设置对应灯珠的颜色(也就是第Num灯珠的RGB,Num是从0开始)。
#define SINGLEWS2812_BYTELEN 12//单个灯珠需要12个字节,勿动
#define WS2812_NUM 64//总共有多少灯珠
#define BIT_BLOCK 0x88//灯珠熄灭bit值
#define BIT_RESET 0x00//复位bit值
#define BIT_TEST 0x55
typedef union {
uint32_t RGB;
struct{
uint8_t B;
uint8_t G;
uint8_t R;
uint8_t Empty;
};
}Color_T;
const uint8_t reset_bytelen = 120;
static uint16_t Send_AllByteLen = 0;
static uint8_t *tx_data;
static uint8_t *reset_data;
static uint8_t *rgb_data;
static const uint32_t DataTable[] = {
0x88888888, 0x8c888888, 0xc8888888, 0xcc888888, 0x888c8888, 0x8c8c8888, 0xc88c8888, 0xcc8c8888,
0x88c88888, 0x8cc88888, 0xc8c88888, 0xccc88888, 0x88cc8888, 0x8ccc8888, 0xc8cc8888, 0xcccc8888,
0x88888c88, 0x8c888c88, 0xc8888c88, 0xcc888c88, 0x888c8c88, 0x8c8c8c88, 0xc88c8c88, 0xcc8c8c88,
0x88c88c88, 0x8cc88c88, 0xc8c88c88, 0xccc88c88, 0x88cc8c88, 0x8ccc8c88, 0xc8cc8c88, 0xcccc8c88,
0x8888c888, 0x8c88c888, 0xc888c888, 0xcc88c888, 0x888cc888, 0x8c8cc888, 0xc88cc888, 0xcc8cc888,
0x88c8c888, 0x8cc8c888, 0xc8c8c888, 0xccc8c888, 0x88ccc888, 0x8cccc888, 0xc8ccc888, 0xccccc888,
0x8888cc88, 0x8c88cc88, 0xc888cc88, 0xcc88cc88, 0x888ccc88, 0x8c8ccc88, 0xc88ccc88, 0xcc8ccc88,
0x88c8cc88, 0x8cc8cc88, 0xc8c8cc88, 0xccc8cc88, 0x88cccc88, 0x8ccccc88, 0xc8cccc88, 0xcccccc88,
0x8888888c, 0x8c88888c, 0xc888888c, 0xcc88888c, 0x888c888c, 0x8c8c888c, 0xc88c888c, 0xcc8c888c,
0x88c8888c, 0x8cc8888c, 0xc8c8888c, 0xccc8888c, 0x88cc888c, 0x8ccc888c, 0xc8cc888c, 0xcccc888c,
0x88888c8c, 0x8c888c8c, 0xc8888c8c, 0xcc888c8c, 0x888c8c8c, 0x8c8c8c8c, 0xc88c8c8c, 0xcc8c8c8c,
0x88c88c8c, 0x8cc88c8c, 0xc8c88c8c, 0xccc88c8c, 0x88cc8c8c, 0x8ccc8c8c, 0xc8cc8c8c, 0xcccc8c8c,
0x8888c88c, 0x8c88c88c, 0xc888c88c, 0xcc88c88c, 0x888cc88c, 0x8c8cc88c, 0xc88cc88c, 0xcc8cc88c,
0x88c8c88c, 0x8cc8c88c, 0xc8c8c88c, 0xccc8c88c, 0x88ccc88c, 0x8cccc88c, 0xc8ccc88c, 0xccccc88c,
0x8888cc8c, 0x8c88cc8c, 0xc888cc8c, 0xcc88cc8c, 0x888ccc8c, 0x8c8ccc8c, 0xc88ccc8c, 0xcc8ccc8c,
0x88c8cc8c, 0x8cc8cc8c, 0xc8c8cc8c, 0xccc8cc8c, 0x88cccc8c, 0x8ccccc8c, 0xc8cccc8c, 0xcccccc8c,
0x888888c8, 0x8c8888c8, 0xc88888c8, 0xcc8888c8, 0x888c88c8, 0x8c8c88c8, 0xc88c88c8, 0xcc8c88c8,
0x88c888c8, 0x8cc888c8, 0xc8c888c8, 0xccc888c8, 0x88cc88c8, 0x8ccc88c8, 0xc8cc88c8, 0xcccc88c8,
0x88888cc8, 0x8c888cc8, 0xc8888cc8, 0xcc888cc8, 0x888c8cc8, 0x8c8c8cc8, 0xc88c8cc8, 0xcc8c8cc8,
0x88c88cc8, 0x8cc88cc8, 0xc8c88cc8, 0xccc88cc8, 0x88cc8cc8, 0x8ccc8cc8, 0xc8cc8cc8, 0xcccc8cc8,
0x8888c8c8, 0x8c88c8c8, 0xc888c8c8, 0xcc88c8c8, 0x888cc8c8, 0x8c8cc8c8, 0xc88cc8c8, 0xcc8cc8c8,
0x88c8c8c8, 0x8cc8c8c8, 0xc8c8c8c8, 0xccc8c8c8, 0x88ccc8c8, 0x8cccc8c8, 0xc8ccc8c8, 0xccccc8c8,
0x8888ccc8, 0x8c88ccc8, 0xc888ccc8, 0xcc88ccc8, 0x888cccc8, 0x8c8cccc8, 0xc88cccc8, 0xcc8cccc8,
0x88c8ccc8, 0x8cc8ccc8, 0xc8c8ccc8, 0xccc8ccc8, 0x88ccccc8, 0x8cccccc8, 0xc8ccccc8, 0xccccccc8,
0x888888cc, 0x8c8888cc, 0xc88888cc, 0xcc8888cc, 0x888c88cc, 0x8c8c88cc, 0xc88c88cc, 0xcc8c88cc,
0x88c888cc, 0x8cc888cc, 0xc8c888cc, 0xccc888cc, 0x88cc88cc, 0x8ccc88cc, 0xc8cc88cc, 0xcccc88cc,
0x88888ccc, 0x8c888ccc, 0xc8888ccc, 0xcc888ccc, 0x888c8ccc, 0x8c8c8ccc, 0xc88c8ccc, 0xcc8c8ccc,
0x88c88ccc, 0x8cc88ccc, 0xc8c88ccc, 0xccc88ccc, 0x88cc8ccc, 0x8ccc8ccc, 0xc8cc8ccc, 0xcccc8ccc,
0x8888c8cc, 0x8c88c8cc, 0xc888c8cc, 0xcc88c8cc, 0x888cc8cc, 0x8c8cc8cc, 0xc88cc8cc, 0xcc8cc8cc,
0x88c8c8cc, 0x8cc8c8cc, 0xc8c8c8cc, 0xccc8c8cc, 0x88ccc8cc, 0x8cccc8cc, 0xc8ccc8cc, 0xccccc8cc,
0x8888cccc, 0x8c88cccc, 0xc888cccc, 0xcc88cccc, 0x888ccccc, 0x8c8ccccc, 0xc88ccccc, 0xcc8ccccc,
0x88c8cccc, 0x8cc8cccc, 0xc8c8cccc, 0xccc8cccc, 0x88cccccc, 0x8ccccccc, 0xc8cccccc, 0xcccccccc,
};
//为了提高执行效率用查表实现
static uint8_t CheckTableAnalyticDataBits(uint8_t Data, uint8_t *DataList_4S){
*(uint32_t *)DataList_4S = DataTable[Data];
return 0;
}
static uint8_t AnalyticRGBData(uint32_t RGB_Data, uint8_t *DataList_12S){//
Color_T a;
a.RGB = RGB_Data;
CheckTableAnalyticDataBits(a.G, DataList_12S);
CheckTableAnalyticDataBits(a.R, DataList_12S+4);
CheckTableAnalyticDataBits(a.B, DataList_12S+8);
return 0;
}
uint8_t Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data){
AnalyticRGBData(RGB_Data, &rgb_data[Num * SINGLEWS2812_BYTELEN]);
return 0;
}
void WS2812_Init(void){
tx_data = Get_SPI2DMATxBuff();
reset_data = tx_data;
rgb_data = &tx_data[reset_bytelen];
if(reset_bytelen > 0){//设置复位,也就是全部发送0。每次发数据之前都发送。
memset(reset_data, BIT_RESET, reset_bytelen);
}
Send_AllByteLen = reset_bytelen + WS2812_NUM * SINGLEWS2812_BYTELEN;
}
//设置所有WS2812B熄灭
void WS2812_CloseAll(void){
memset(rgb_data, BIT_BLOCK, WS2812_NUM * SINGLEWS2812_BYTELEN);
}
4、全部设置完相应位灯珠的颜色之后,执行以下语句,即可点亮所有设置的灯珠
SPI2_DMA_SendBuf(Send_AllByteLen);
————————————————
版权声明:本文为CSDN博主「GJF712」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/GJF712/article/details/131989518
|
|