WS2812B是一种集成了电流控制芯片的低功耗的RGB三色灯,
关于这个灯的一些硬件分析我就不多说,百度一堆一堆的;
今天主要讲讲使用SPI的MOSI+DMA实现控制的方式;
主MCU:灵动微的 MMF031,关于这个MCU就是直接仿造的ST同系列的,可直接百度吧;
主频:#define SYSCLK_HSI_48MHz 48000000
思路:利用SPI发送不同的byte实现WS2812B规格书需求的高低电平;
基础实现高低电平如下:
void LED_SPI_SendBits(uint8_t bits)
{
int zero = 0X1F00;//0x0700; //0001 1111 0000 0000
int one = 0XFFC0;//0x07E0; //1111 1111 1100 0000
int i = 0x00;
for (i = 0x80; i >= 0x01; i >>= 1)
{
LED_SPI_WriteByte((bits & i) ? one : zero);
}
}
实现RGB三个字段共计24BIT的代码如下(这里需要定义一个结构体实现数据转存):
struct Pixel {
uint8_t red;
uint8_t green;
uint8_t blue;
};
void LED_SPI_SendPixel(struct Pixel pixel)
{
LED_SPI_SendBits(pixel.green);
LED_SPI_SendBits(pixel.red);
LED_SPI_SendBits(pixel.blue);
}
需要一个实现发送的代码,传参length 是WS2812级联的个数:
ErrorStatus LED_SPI_Update(struct Pixel buffer[], uint32_t length)
{
uint8_t i = 0;
uint16_t delay_break = 0 ;
if(DMA_GetCurrDataCounter(DMA1_Channel3) == 0)//检查DMA是否完成传输任务
{
for (i = 0; i < length; i++)
{
LED_SPI_SendPixel(buffer[i]);///一个buffer 一个灯
}
PixelPointer = 0;
DMA1_Channel3->CNDTR = TX_RX_LEN;
DMA1_Channel3->CMAR = (uint32_t)PixelBuffer;
DMA_ClearFlag(DMA1_FLAG_TC3);//清除通道3数据传输完成标志位
SPI_DMACmd(SPI1,SPI_DMAReq_EN,ENABLE);//使能SPI DMA
DMA_Cmd(DMA1_Channel3, ENABLE); //使能SPI TX DMA1 所指示的通道
SPI_Cmd(SPI1, ENABLE); //Enables the specified SPI peripheral SPI使能、主机模式 8位数据模式 SPI 的波特率
while(!DMA_GetFlagStatus(DMA1_FLAG_TC3))
{//通道3数据发送传输完成标志位
delay_break ++ ;
if(delay_break >2000)
break ;
}
DMA_ClearFlag(DMA1_FLAG_TC3);//清除通道3数据传输完成标志位
return SUCCESS;
}
else
{
return ERROR;
}
}
这里一个例子是5个数据的代码(实现2+2+1的亮法)可根据实际应用更改了:
void PixelRound_color(u8 left_r ,u8 left_g ,u8 left_b ,u8 right_r,u8 right_g,u8 right_b, u8 front_r,u8 front_g,u8 front_b )//RGB????
{
LEDPixel[0].red = left_r;///红
LEDPixel[0].blue = left_g;
LEDPixel[0].green = left_b;
LEDPixel[1].red = left_r;
LEDPixel[1].blue= left_g;
LEDPixel[1].green = left_b;//绿
LEDPixel[2].red = right_r;//蓝
LEDPixel[2].blue= right_g;
LEDPixel[2].green = right_b;
LEDPixel[3].red = right_r;///黄
LEDPixel[3].blue= right_g;
LEDPixel[3].green = right_b;
LEDPixel[4].red = front_r;///青
LEDPixel[4].blue= front_g;
LEDPixel[4].green = front_b;
LEDPixel[5].red = 0;///紫
LEDPixel[5].blue= 0;
LEDPixel[5].green = 0;
LED_SPI_Update(LEDPixel,5);
}
配置代码(有点混乱,没有时间整理,直接发出来了):
/********************************************************************************************************
**函数信息 :SPIM_Init(unsigned short spi_baud_div)
**功能描述 :可修改参数初始化SPI
**输入参数 :spi_baud_div
**输出参数 :无
********************************************************************************************************/
void SPIM_Init(SPI_TypeDef* SPIx,unsigned short spi_baud_div)
{
// SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1 clk enable
SPIM_CSHigh(SPIx);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB|RCC_AHBPeriph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //spi1_mosi PA7 //spi1_sck PA5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//GPIO_Mode_AF_PP; // 推免复用输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //spi1_cs PA4
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//
// GPIO_Init(GPIOA, &GPIO_InitStructure);
//
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //spi1_miso PA6
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
// GPIO_Init(GPIOA, &GPIO_InitStructure);
// SPIM_CSHigh(SPI1); //CS high
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_0); //开复用时钟
// GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_0);
// GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_0);
SPI1->GCTL|=SPI_GCTL_MM;//主机模式
SPI1->CCTL|=SPI_CCTL_SPILEN;//8位数据宽度
// SPI1->GCTL|= SPI_GCTL_DATA_SEL;//32 bit 有效
SPI1->CCTL&=~SPI_CCTL_CPOL;//SPI_CPOL_Low // //spi MODE0 CLK 空闲低电平 上升沿采样下降沿送数据
SPI1->CCTL|=SPI_CCTL_CPHA;//SPI_CPHA_1Edge
SPI1->GCTL&=~SPI_GCTL_NSS_SEL; //软件控制NSS
SPI1->CCTL |=0x10; //开启高速模式
SPI1->SPBRG =spi_baud_div;//4分频,波特率应该为72M/4=18M
// SPI1->CCTL&=~SPI_CCTL_LSBFE;//MSB
SPIx->CCTL|=SPI_CCTL_LSBFE; //LSB
//
SPI1->EXTCTL = 0x10; //16bit 数据长度
SPIM_TXEn(SPI1);//SPI发送使能
SPIM_RXEn(SPI1);//SPI接收使能
// SPI_Cmd(SPI1, ENABLE); //Enables the specified SPI peripheral SPI使能、从机模式 8位数据模式 SPI 的波特率
}
/********************************************************************************************************
**函数信息 :SPI_DMA_TX_Init
**功能描述 : SPI发送DMA初始化
**输入参数 :
**输出参数 :无
********************************************************************************************************/
void SPI_DMA_TX_Init(u16 *TX_buffer)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->TXREG);//SPI发送数据寄存器
DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)&(*TX_buffer);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//数据从memory到外设
DMA_InitStructure.DMA_BufferSize = TX_RX_LEN;//发送数据个数
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不改变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址递增开启
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//DMA_PeripheralDataSize_Byte;/内存地址增量(变化)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//DMA_MemoryDataSize_Byte;/外设传输宽度(字节)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);//SPI TX为DMA通道3
}
工程代码过几天附上。