相信大家在看到这篇文章的时候一定对WS2812芯片的时序有了一定的了解,这里对于WS2812硬件通信方面就不做过多的介绍了。驱动WS2812需要的实现纳秒级别的电平翻转,像一般主频较低的MCU很难实现这种级别的电平翻转。我在这里使用的MCU是STM32F103系列主频为72M,恰好可以通过延时翻转高低电平模拟WS2812的通信时序进而实现对WS2812灯珠的驱动。
STM32通过普通IO方式驱动WS2812灯珠首先我们要初始化IO端口。
/**
* @brief 初始化IO控制口
* @param
* @retval None
*/
void ws2811_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //WS2811-->PA.0 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.0
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA.0 输出低电平
}
接下来便要实现合适的延时函数:
/**
* @brief ws281x模块用到的延时函数
* @param delay_num :延时数 (示波器测量延时时间 = delay_num * 440ns )
* @retval None
*/
void ws281X_delay(unsigned int delay_num)
{
while(delay_num--);
}
通过示波器测量出的该延时函数的延时时间为delay_num x 440ns,恰好可以实现纳秒级别的延时。因此可用此来模拟WS281x的通信时序。
/**
* @brief 根据WS281x芯片时序图编写的发送0码,1码RESET码的函数
* @param
* @retval None
*/
void ws281x_sendLow(void) //发送0码
{
PAout(0) = 1;
ws281x_delay(1); //示波器测试约为440ns
PAout(0) = 0;
ws281x_delay(2);
}
void ws281x_sendHigh(void) //发送1码
{
PAout(0) = 1;
ws281x_delay(2);
PAout(0) = 0;
ws281x_delay(1);
}
void ws2811_Reset(void) //发送RESET码
{
PAout(0) = 0;
delay_us(60);
PAout(0) = 1;
PAout(0) = 0;
}
接下来根据这些通信时序我们便可以实现点亮WS2812灯珠的函数了
/**
* @brief 发送点亮一个灯的数据(即24bit)
* @param dat:颜 {MOD}的24位编码
* @retval None
*/
void ws281x_sendOne(uint32_t dat)
{
uint8_t i;
unsigned char byte;
for(i = 24; i > 0; i--)
{
byte = ((dat>>i) & 0x01); //位操作,读取dat数据的第i位
if(byte == 1)
{
ws281x_sendHigh();
}
else
{
ws281x_sendLow();
}
}
}
如在主函数中调用ws281x_sendOne(0XFF0000)即可点亮一个绿灯。
如果需要控制多个WS2812灯珠可以定义一个缓冲区pixelBuffer[PIXEL_NUM]的数组,首先将颜 {MOD}数据依次填入缓冲区,再通过发送函数将颜 {MOD}数据依次发出即可。若想实现多种炫酷效果还可以移植Adafruit_NeoPixel库函数。