ws2812做为RGB灯,在很多场合都是非常有用的,我准备使用STM32H755的M4内核驱动时,遇到了一些问题,因此先用STM32F103来验证一下,找到库存的STM32103的开发板,来实现驱动,特记录如下:
1.使用STM32CubeMX 新建基于STM32F103C8Tx的工程:

2.打开外部时钟,因为内部时钟最高只能跑到64MHz。

3.在时钟配置界面,配置72MHz的总线时钟。

从上面我们得到Timer的时钟总线为72MHz。
4.打开TIM1的配置界面配置如下:

这里我们需要将GPIO 的输出速度选择为高速模式。
5.配置pwm

在定时器配置中,我们根据WS2812 的最大传输速率800kbps,设置定时器不分频和计数周期为89+1,这样下来波形的频率为72 M /(89+1) = 800 K,并且一个波形的周期为1 / 800 = 1.25 us
6.配置DMA

我们打开DMA的界面,添加一个DMA,选择方向为从内存到外设,内存为递增,数据宽度为半字节,即16 bit。
到此界面配置结束,生成工程后,使用mdk 打开工程。
【代码添加】
1.新建RGB.c 添加代码如下:
view plaincopy to clipboardprint?
1.#include “RGB.h”
2.#include “main.h”
3.#include “tim.h”
4.
5.uint16_t RGB_buffur[Reste_Data + WS2812_Data_Len] = { 0 }; //数据缓存数组
6.
7.void WS2812_Display_1(uint32_t Color, uint16_t num)
8.{
9.
10.//指针偏移:需要跳过复位信号的N 个0
11.uint16_t* p = (RGB_buffur + Reste_Data) +(num * Led_Data_Len);
12.
13.for (uint8_t i = 0; i < 8; ++i)
14.p[i+8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
15.for (uint8_t i = 8; i < 16; ++i)
16.p[i-8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
17.for (uint8_t i = 16; i < 24; ++i)
18.p[i]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
19.
20.}
21.
22.
23.void WS2812_Display_2( uint8_t red, uint8_t green, uint8_t blue,uint16_t num)
24.{
25.
26.uint8_t i;
27.uint32_t Color=(green << 16 | red << 8 | blue);// 将2 个8 位数据合并转化为32 位数据类型
28.
29.//指针偏移:需要跳过复位信号的N 个0
30.uint16_t* p = (RGB_buffur + Reste_Data) + (num * Led_Data_Len);
31.
32.for (i = 0; i < 24; ++i) // 对数组进行编辑
33.p[i]= (((Color << i) & 0X800000) ? Hight_Data : Low_Data);
34.
35.}
36.
37.
38.void WS2812_Number_4(uint32_t Color1,uint32_t Color2,uint32_t Color3,uint32_t Color4)
39.{
40.
41.uint16_t RGB_Buff_4[Reste_Data + 4 *WS2812_Data_Len] = { 0 };
42.uint16_t* p;
43.uint32_t Color;
44.
45.for( uint8_t k=0;k<4;k++)
46.{
47.switch (k) // 进行指针偏移
48.{
49.case 0: p= (RGB_Buff_4 + Reste_Data) +(0 * Led_Data_Len),Color=Color1;break;
50.case 1: p= (RGB_Buff_4 + Reste_Data) +(1 * Led_Data_Len),Color=Color2;break;
51.case 2: p= (RGB_Buff_4 + Reste_Data) +(2 * Led_Data_Len),Color=Color3;break;
52.case 3: p= (RGB_Buff_4 + Reste_Data) +(3 * Led_Data_Len),Color=Color4;break;
53.default : ;break;
54.}
55.
56.for (uint8_t i = 0; i < 8; ++i) // 对数组进行编辑
57.{
58. for (uint8_t i = 0; i < 8; ++i)
59.p[i+8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
60.for (uint8_t i = 8; i < 16; ++i)
61.p[i-8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
62.for (uint8_t i = 16; i < 24; ++i)
63.p[i]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data);
64.}
65.
66.}
67.
68.HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t *)RGB_Buff_4,(176));//启动DMA传输
69.
70.}
71.
72.//DMA传输完成回调函数
73.void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
74.{
75.HAL_TIM_PWM_Stop_DMA(&htim1,TIM_CHANNEL_1);
76.}
2.添加RGB.h 代码如下:
view plaincopy to clipboardprint?
1.#ifndef __RGB_H
2.#define __RGB_H
3.#include “main.h”
4.#define Hight_Data ( 64 ) //1码相对计数值
5. #define Low_Data ( 36 ) //0码相对计数值
6. #define Reste_Data ( 80 ) //80复位电平相对计数值
7.#define Led_Num ( 4 ) //WS2812灯个数
8.#define Led_Data_Len ( 24 )//WS2812数据长度,单个需要24个字节
9.#define WS2812_Data_Len (Led_Num * Led_Data_Len) //ws2812级联后需要的数组长度
10.
11.//uint16_t RGB_buffur[Reste_Data + WS2812_Data_Len] = { 0 }; //数据缓存数组
12.
13.
14.void WS2812_Display_1(uint32_t Color,uint16_t num);
15.void WS2812_Display_2( uint8_t red, uint8_t green, uint8_t blue,uint16_t num);
16.
17.void WS2812_Number_4(uint32_t Color1,uint32_t Color2,uint32_t Color3,uint32_t Color4);//封装好的四个灯函数,只需要分别输入四个灯的颜色即可
18.
19.
20.#endif
3.Main
首先引RGB.h头文件
再使用extern一下数组
view plaincopy to clipboardprint?
1.extern uint16_t RGB_buffur[Reste_Data +WS2812_Data_Len];
在while中添加周期的亮灯:
view plaincopy to clipboardprint?
1.WS2812_Number_4(0x180000,0x001800,0x000018,0);
2.HAL_Delay(500);
3.WS2812_Number_4(0,0×180000,0x001800,0x000018);
4.HAL_Delay(500);
5.WS2812_Number_4(0x000018,0,0×180000,0x001800);
6.HAL_Delay(500);
7.WS2812_Number_4(0x001800,0x000018,0,0×180000);
8.HAL_Delay(500);
【验证】
将ws2812 的DI 引脚接到PA8,VCC 与GND 接到开发板的电源上,将程序下载到开发板后,可以看到如期点亮了WB2812。
<!-- 非定向300*250按钮 17/09 wenjing begin -->
<!-- 非定向300*250按钮 end -->
</div>