嵌入式开发笔记-写一个WS2812驱动
嵌入式开发笔记 - 写一个WS2812驱动
一、引言
在嵌入式开发中,WS2812是一种广泛使用的LED灯珠。它具有内置的驱动芯片,可以方便地控制颜色和亮度,因此在许多应用中都得到了广泛的应用。本文将介绍如何为WS2812编写一个驱动程序,以实现对其颜色和亮度的控制。
二、硬件连接
WS2812引脚定义
WS2812典型应用电路
在开始编写驱动程序之前,需要将WS2812连接到开发板上。通常,WS2812使用SPI接口进行通信,因此需要将WS2812的DIN引脚连接到开发板的SPI接口上。此外,还需要将WS2812的VCC和GND引脚连接到开发板的电源和地线。
特别注意WS2812电源电源为3.5v~5.3v,最好不要用单片机的3.3v
三、驱动程序编写
WS2812协议
级联和编码方法
WS2812协议编码
由于控制WS2812的脉冲高低电平在0.85us,0.4us,时间间隔,为了产生这样的脉冲,使用普通的软件控制IO口是无法完成的,一般使用SPI或者PWM硬件控制,这里采用SPI控制。
当发送波形高电平(T0H)在220ns~380ns且低电平(TOL)在580ns~1us时表示编码 0,同理 1码和复位码也是如此。总结:一个编码的时间1.25us±300ns之间都能识别到,主要是识别高电平持续的时间;时间段固定,高电平短为0高电平时间长为1,280ns时间无高电平就表示这一个信号结束。
一颗ws2812的RGB灯里面有三颗灯珠,有红、绿、蓝三个颜色的LED灯。每一个灯使用PWM驱动,发送的数据即为PWM的宽度,一颗LED灯的数据宽度为 8 bits(1 byte) ,所以一颗 ws2812 RGB灯共需要24 bits(3 bytes)的数据。
一颗RGB灯发送数据的时序如下,高位先行:
G7 … G0 | R7 … R0 | B7 … B0 |
借助 SPI 来控制 WS2812,我们用 SPI 的 MOSI 接口的一个 Byte(8位)模拟 WS2812 的一个位。比如 SPI 设置的 7M 速率,使用SPI发送一个字节(byte)来模拟WS2812的一个编码(0或1)。比如用SPI发送的 0xF8(1111 1000)表示WS2812编码的1,发送的数据是 0xC0 (1100 0000)时这代表编码 0;这是因为在SPI发送一个字节的时间符合WS2812的一个编码的时间,发送0XF8时高电平持续时间在580ns~1us内,发送0xC0时高电平持续220ns~380ns内。即
SPI发送 0xF8 = 1 ,SPI发送 0xC0 = 0。
SPI发送24个字节可以控制一颗RGB灯,打成一个包,控制n个灯则需要:n x 包 +RET码。
SPI的速度在5.52——9.41MHz范围内,SPI发送一个字节时间长度等于一个编码的时间长度。
在编写WS2812驱动程序时,需要考虑以下几个方面:
初始化WS2812
初始化WS2812最重要的就是初始化控制的灯珠数量和SPI的发送函数。初始化时创建一个大小为控制灯珠数量*24的缓冲区用来操作WS2812。控制WS2812的颜色和亮度
在控制WS2812前首先调用cleanBuffer()
清空缓冲区,然后再对缓冲区进行修改,然后调用sendBuffer()
,将缓冲区的内容发送至WS2812完成显示。控制函数如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46/// @brief 设置一颗灯珠的颜色
/// @param LedId 该灯珠的id,第一个灯珠id为0,下一个id加一,以此类推
/// @param rgb_t 设置的rgb值,如RGB(255,255,255)
void setColor(uint8_t LedId, const RGBColor_TypeDef *rgb_t)
{
if (LedId < num)
{
uint8_t dat_r = rgb_t->R;
uint8_t dat_g = rgb_t->G;
uint8_t dat_b = rgb_t->B;
for (int j = 0; j < 8; j++)
{
*(pbuf+LedId*24+7-j) = code[dat_g & 0x01];
*(pbuf+LedId*24+15 - j) = code[dat_r & 0x01];
*(pbuf+LedId*24+23 - j) = code[dat_b & 0x01];
dat_g >>= 1;
dat_r >>= 1;
dat_b >>= 1;
}
}
}
/// @brief 设置一定数目的灯珠颜色
/// @param LedId 起始灯珠的id,第一个灯珠id为0,下一个id加一,以此类推
/// @param rgb_t 设置的rgb值,如RGB(255,255,255)
/// @param num 灯珠数量
void setColor(uint8_t LedId, const RGBColor_TypeDef *rgb_t, uint8_t num)
{
while (LedId < this->num && LedId < num)
{
setColor(LedId, rgb_t);
LedId++;
}
}
/// @brief 把全部灯珠设置为某个颜色
/// @param rgb_t 设置的rgb值,如RGB(255,255,255)
void setColor(const RGBColor_TypeDef *rgb_t)
{
uint8_t LedId = 0;
while (LedId < this->num)
{
setColor(LedId, rgb_t);
LedId++;
}
}
四、测试与调试
完成驱动程序编写后,需要进行测试和调试。可以使用串口输出调试信息,以便快速定位问题。此外,还可以使用示波器、逻辑分析仪等工具测试WS2812的GPIO引脚信号,以确保驱动程序正确控制了WS2812的颜色和亮度。
五、总结
本文介绍了如何为WS2812编写一个驱动程序,以实现对其颜色和亮度的控制。通过掌握WS2812的硬件连接、驱动程序编写、测试与调试等方面的知识,可以更好地应用WS2812,并为其应用提供有力的支持。