WS2812驱动 SPI+DMA 无需降频 一个灯占用9Byte的RAM

WS2812驱动 STM32F103 SPI+DMA 无需降频 一个灯占用9Byte的RAM

对于WS2812不了解的朋友可以先看一下这些
【STM32】WS2812介绍、使用SPI+DMA发送数据
ws812

WS2812的驱动我想到的主要有三种

  • 延时
  • SPI
  • UART
    三者区分:
  1. 延时,占用资源,需要用到ASM nop不然容易被优化,需要用示波器抓时间。
  2. UART 起始位低,结束位高,如果要强行实现也是可以但是实现起来过于复杂,不够优美。
  3. 没有片选的功能,会占用一整个硬件SPI,觉得较为优美。

在网上看了很多SPI例子都是使用 SPI的8bit数据来模拟WS2812的一个bit颜色的数据,但是由于STM32F103主频72M,要凑出来这个SPI的频率我看某些做法是进行降频,吾不喜,要么还有就是和协议时序差异挺大但是有些WS2812能运行,但是考虑到可能有些ws2812会不能兼容,吾不喜。

STM32F103 设主频72M,SPI分频数设置为32,则SPI的通信频率为2.25M,传输一位数据的时间约为444纳秒(ns)444ns 888ns 符合WS281X芯片的通信时序。

1
2
3
4
//  __
// | |_| 0b110 high level
// _
// | |__| 0b100 low level

这个方式和协议时序更加接近,占用RAM应该是笔者认为最小的了
一个灯24bit颜色,只需要24*3/8 = 9Byte的Buff。

写代码的时候需要注意一下大小端的问题,尽量保持大小端的兼容性

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
/**
* @brief 设置某一个WS2812
*
* @param num
* @param RGB
*/
void WS2812_OneSet( uint8_t num, uint32_t RGB )
{
uint8_t i;
uint32_t TempR = 0, TempG = 0, TempB = 0;

//MSB First
for( i = 0; i < 8; ++i,RGB>>=1 )
{
(RGB & 0x00010000) != 0 ? (TempR |= (WS2812_HIG<<(i*3))) : (TempR |= (WS2812_LOW<<(i*3)));
(RGB & 0x00000100) != 0 ? (TempG |= (WS2812_HIG<<(i*3))) : (TempG |= (WS2812_LOW<<(i*3)));
(RGB & 0x00000001) != 0 ? (TempB |= (WS2812_HIG<<(i*3))) : (TempB |= (WS2812_LOW<<(i*3)));
}

for( i = 0; i < 3; ++i )
{
g_ws2812.Col[num].RGB.R[i] = TempR >> (16-8*i);
g_ws2812.Col[num].RGB.G[i] = TempG >> (16-8*i);
g_ws2812.Col[num].RGB.B[i] = TempB >> (16-8*i);
}
}

这里有参考的代码,有积分的给点积分用吧,没积分的可以去Github下。
Github
CSDN下载

参考资料
stm32和ws2812B相对较好的方案。SPI和UART


WS2812驱动 SPI+DMA 无需降频 一个灯占用9Byte的RAM
https://www.oikiou.top/2021/aaef73f3/
作者
Oikiou
发布于
2021年1月31日
许可协议