STM32SPI驱动WS2812

STM32SPI驱动WS2812

HeWenXuan Lv1

前言

本文将介绍怎么使用STM32的SPI接口来驱动WS2812模块。如果有说明不完全或者有错误的地方,欢迎指正,一切请以官方手册为准。

WS2812简介

在学习单片机控制LED灯过程中,如果我要控制LED发出不同颜色的光,通常需要使用RGB LED灯。按正常逻辑就需要三个PWM通道来控制红绿蓝三种颜色的亮度,但是如果我要控制多个RGB LED灯,使用的PWM通道数量就会成倍增加,对于灯条等需要控制大量LED的场景来说,这种方式显然不太现实。WS2812就是一款集成了控制芯片的RGB LED灯,每个LED灯都可以通过单线串行通信来控制,这样就大大减少了需要的PWM通道数量,非常适合需要控制大量LED的场景。

WS2812的通信协议

WS2812使用单线串行通信协议,每个LED灯需要24位数据来控制颜色,其中8位控制红色亮度,8位控制绿色亮度,8位控制蓝色亮度。数据传输的时序非常严格,具体如下:
其中1和0的区别在于高电平持续的时间

这张图非常重要,理解了这张图才能看懂接下来的SPI模拟WS2812协议的内容

使用SPI模拟WS2812协议

由于WS2812的通信协议时序非常严格,直接使用GPIO模拟可能会比较麻烦,尤其是在需要控制大量LED的情况下。幸运的是,我们可以使用STM32的SPI的MOSI来模拟WS2812的通信协议。结论先行,我们目的是用SPI一个数据位(8bits)来模拟WS2812的一个码元(1bit)

计算SPI的时钟频率

  1. 根据WS2812的通信协议,0的高电平持续时间为220ns380ns,1的高电平持续时间为580ns1us,低电平持续时间均为580ns1us。且一个码元至少持续1250ns。综上,一个WS2812码元的周期范围为1250ns2000ns,频率为500kHz~800kHz。
  2. 前面有提到,我们是用SPI一个数据位(8bits)来模拟WS2812的1个码元,故一个SPI数据位的周期为1250ns2000ns,频率为500kHz800kHz。
  3. SPI的时钟频率是数据位频率的8倍,所以SPI的时钟频率范围为4MHz~6.4MHz,理论来说在这个范围内都可以正常工作,但建议不要太接近上限,以免出现时序问题。本文中我选择参考达妙MC-02开发板例程,将SPI时钟频率设置为6MHz

SPI数据位编码

根据WS2812的通信协议,00的高电平持续时间为220ns380ns,1的高电平持续时间为580ns1us,低电平持续时间均为580ns~1us。
根据上述SPI的时钟频率选择6MHz,SPI1个bit周期为166.67ns,可以算出:

  • 0码元:高电平持续2位,低电平持续6位
  • 1码元:高电平持续4位,低电平持续4位
    但是由于WS2812只是借用了SPI的MOSI线来传输数据,并不是真正的SPI通信,所以在发送数据第一个bit时,数据可能会粗糙不稳定,为了保证第一个bit的稳定性,我们可以把所有要发送的数据右移1位,这样就相当于在数据前面添加了一个0码元,保证了第一个bit的稳定性。

最终的SPI数据位编码如下:

  • 0码元:0b0110 0000 = 0x60
  • 1码元:0b0111 1000 = 0x78
  • RES:0b0000 0000 = 0x00

程序

我使用的是达妙MC-02开发板,时钟树及SPI配置如下:


注意:

  • SPI的时钟频率必须在4MHz~6.4MHz之间
  • 空闲时要为低电平

控制一个WS2812的的参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define WS2812_LowLevel    0x60     // 0
#define WS2812_HighLevel 0x78 // 1

void WS2812_Ctrl(uint8_t r, uint8_t g, uint8_t b)
{
uint8_t txbuf[24];
uint8_t res = 0;
for (int i = 0; i < 8; i++)
{
txbuf[7-i] = (((g>>i)&0x01) ? WS2812_HighLevel : WS2812_LowLevel)>>1;
txbuf[15-i] = (((r>>i)&0x01) ? WS2812_HighLevel : WS2812_LowLevel)>>1;
txbuf[23-i] = (((b>>i)&0x01) ? WS2812_HighLevel : WS2812_LowLevel)>>1;
}
HAL_SPI_Transmit(&hspi6, &res, 0, 0xFFFF);
while (HAL_SPI_Transmit(&hspi6, txbuf, 24, 0xFFFF)!= HAL_OK);
HAL_SPI_Transmit(&hspi6, &res, 1, 0xFFFF);
}

其中三个参数分别为RGB三种颜色的亮度,范围为0~255

控制多个WS2812

如果需要控制多个WS2812,只需要把每个LED灯的数据依次发送出去即可,WS2812会自动把数据传递给下一个LED灯,最后在一起发送RES码元来完成数据的刷新,上述代码加入循环即可解决,比较简单,不过多赘述。

结语

通过上述方法,我们就可以使用STM32的SPI接口来驱动WS2812模块了。需要注意的是,WS2812的通信协议时序非常严格,如果出现闪烁或者颜色不正确的情况,可能是时序问题导致的,可以尝试调整SPI的时钟频率或者数据位编码来解决。另外,如果需要控制多个LED灯,可以按照WS2812的通信协议,把每个LED灯的数据依次发送出去即可,WS2812会自动把数据传递给下一个LED灯,非常方便。

  • 标题: STM32SPI驱动WS2812
  • 作者: HeWenXuan
  • 创建于 : 2026-02-18 22:12:47
  • 更新于 : 2026-02-18 22:12:47
  • 链接: https://redefine.ohevan.com/2026/02/18/ws2812/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论