STM32F1与STM32CubeIDE编程实例-WS2812B全彩LED驱动(基于SPI+DMA)

WS2812B全彩LED驱动(基于SPI+DMA)

1、WS2812介绍

WS2812/WS2812B LED 使用 24 位来表示绿色、红色和蓝色值。

在这里插入图片描述

WS2812采用单线通信的设计,通信协议为非归零编码,每个LED需要24个bit的数据,数据依次经过串联的LED时,第一个LED截取数据开头的24bit,并将剩下的数据流传给下一个LED,以此类推。数据线上的位由高脉冲编码,然后是低脉冲。时序如下:

在这里插入图片描述

WS2812支持高速数据传输并且其数据传输时序与SPI的通信时序类似,因此可以使用STM32的SPI外设模拟WS2812的通信时序。

在前面的文章中,对WS2812做了详细的介绍,请参考:

2、WS2812B配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

关于STM32的SPI,请参考前面文章:

本次驱动WS2812B的SPI配置如下:

在这里插入图片描述

系统时钟设置为72MHz,SPI分频数设置为8,则SPI的速度为9M,传输一位数据需要的时间约为 1s / 9M≈111纳秒,




3





111

333


n


s


5





111

555

n

s

3 * 111 = 333ns 5*111 = 555ns

3∗111=333ns5∗111=555ns 符合WS281X芯片的通信时序。

SPI的DMA配置如下:

在这里插入图片描述

保存配置,并生成代码。

3、WS2812驱动实现

1)基本定义

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/\*
\* ws2812b.h
\*
\* Created on: Apr 23, 2022
\* Author: jenson
\*/

#ifndef \_\_WS2812B\_H\_\_
#define \_\_WS2812B\_H\_\_

#include <stdio.h>
#include <stm32f1xx\_hal.h>
#include "main.h"

// SPI数据为0的时序
#define SPI\_NEO0 ((uint8\_t) 0b11000000)
// SPI数据为1的时序
#define SPI\_NEO1 ((uint8\_t) 0b11111100)

/\*\*
\* @brief RGB颜色定义
\*/
typedef struct {
uint8\_t G;
uint8\_t R;
uint8\_t B;
} color\_t;

typedef struct {
uint16\_t id; // 编号
SPI_HandleTypeDef \*spi; // SPI句柄
uint16\_t led_counts; // LED数量
uint8\_t \*color_datas; // 24位颜色数据
uint16\_t color_data_size; // 颜色数据长度
color\_t \*led_colors; // LED颜色
uint8\_t inited; // 是否初始化,1表示已经初始化,0则表示未初始化
} ws2812b\_t;

/\*\*
\* @brief 初始化
\* @param ws2812b WS2812对象
\*/
void ws2812b\_init(ws2812b\_t \*ws2812b);

/\*\*\*
\* @brief 释放WS2812占用内存
\*/
void ws2812b\_release(ws2812b\_t\* ws2812b);

/\*\*
\* @brief 设置指定位置WS2812B灯珠颜色
\* @param ws2812b WS2812对象
\* @param index WS2812灯珠位置
\* @param color 颜色
\*/
void ws2812b\_set\_color(ws2812b\_t \*ws2812b, uint16\_t index, color\_t color);

/\*\*
\* @brief 打开WS2812B显示颜色
\* @param ws2812b WS2812B对象
\*/
void ws2812b\_show(ws2812b\_t \*ws2812b);

#endif /\* \_\_WS2812B\_H\_\_ \*/



2)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
/\*
\* ws2812b.c
\*
\* Created on: Apr 23, 2022
\* Author: jenson
\*/

#include "ws2812b.h"
#include <stdlib.h>

#define BITS\_PER\_LED\_COLOR (sizeof(color\_t) \* 8)

void ws2812b\_init(ws2812b\_t \*ws2812b) {

// 分配24位颜色数据内存
ws2812b->color_datas = (uint8\_t\*) malloc(
ws2812b->led_counts \* BITS_PER_LED_COLOR + 32);
// 分配LED颜色数据内存
ws2812b->led_colors = (color\_t\*) malloc(
sizeof(color\_t) \* ws2812b->led_counts);
if (ws2812b->color_datas && ws2812b->led_colors) {
ws2812b->inited = 1;
ws2812b->color_data_size = ws2812b->led_counts \* BITS_PER_LED_COLOR
+ 32;
} else {
ws2812b->inited = 0;
}
}

void ws2812b\_release(ws2812b\_t\* ws2812b){
if(!ws2812b->inited){
return;
}
// 释放内存
free(ws2812b->led_colors);
ws2812b->led_colors = NULL;
free(ws2812b->color_datas);
ws2812b->color_datas = NULL;
ws2812b->spi = NULL;
ws2812b->led_counts = 0;
ws2812b->inited = 0;
}

3)颜色设置与显示

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
// 生成24位颜色数据序列
void \_\_build\_led\_color\_data(ws2812b\_t \*ws2812b) {
for (int i = 0; i < ws2812b->led_counts; i++) {

uint8\_t m = 0b10000000;
for (int b = 0; b < 8; b++) {
ws2812b->color_datas[BITS_PER_LED_COLOR \* i + b] =
ws2812b->led_colors[i].G & m ? SPI_NEO1 : SPI_NEO0;
m >>= 1u;
}

m = 0b10000000;
for (int b = 0; b < 8; b++) {
ws2812b->color_datas[BITS_PER_LED_COLOR \* i + b + 8] =
ws2812b->led_colors[i].R & m ? SPI_NEO1 : SPI_NEO0;
m >>= 1u;
}

m = 0b10000000;
for (int b = 0; b < 8; b++) {
ws2812b->color_datas[BITS_PER_LED_COLOR \* i + b + 16] =
ws2812b->led_colors[i].B & m ? SPI_NEO1 : SPI_NEO0;
m >>= 1u;
}
}
}

void ws2812b\_set\_color(ws2812b\_t \*ws2812b, uint16\_t index, color\_t color) {
if (index >= ws2812b->led_counts && ws2812b->inited) {
return;
}
ws2812b->led_colors[index] = color;

}

void ws2812b\_show(ws2812b\_t \*ws2812b) {
if (!ws2812b->inited) {
return;
}
\_\_build\_led\_color\_data(ws2812b);
// 发送数据
HAL\_SPI\_Transmit\_DMA(ws2812b->spi, ws2812b->color_datas,
ws2812b->color_data_size);
}


4)主程序

main.c文件中添加如下代码:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include "ws2812b/ws2812b.h"
/\* USER CODE END Includes \*/

/\* Private typedef -----------------------------------------------------------\*/
/\* USER CODE BEGIN PTD \*/
color\_t RED = { 255, 0, 0 };
color\_t GREEN = { 0, 255, 0 };
color\_t BLUE = { 0, 0, 255 };
/\* USER CODE END PTD \*/

/\* Private variables ---------------------------------------------------------\*/

/\* USER CODE BEGIN PV \*/
ws2812b\_t ws2812b;
/\* USER CODE END PV \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_DMA\_Init();
MX\_SPI1\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
ws2812b.id = 1;
ws2812b.inited = 0;
ws2812b.led_counts = 8;
ws2812b.spi = &hspi1;
// ws2812b.spi\_dm\_tx = &hdma\_spi1\_tx;

ws2812b\_init(&ws2812b);
if (!ws2812b.inited) {
printf("ws2812b init failed\r\n");
while (1) {

}
}

/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
ws2812b\_set\_color(&ws2812b, 0, RED);
ws2812b\_set\_color(&ws2812b, 1, RED);
ws2812b\_set\_color(&ws2812b, 2, RED);
ws2812b\_set\_color(&ws2812b, 3, RED);
ws2812b\_set\_color(&ws2812b, 4, RED);
ws2812b\_set\_color(&ws2812b, 5, RED);
ws2812b\_set\_color(&ws2812b, 6, RED);
ws2812b\_set\_color(&ws2812b, 7, RED);
ws2812b\_show(&ws2812b);

HAL\_Delay(500);

ws2812b\_set\_color(&ws2812b, 0, GREEN);
ws2812b\_set\_color(&ws2812b, 1, GREEN);
ws2812b\_set\_color(&ws2812b, 2, GREEN);
ws2812b\_set\_color(&ws2812b, 3, GREEN);
ws2812b\_set\_color(&ws2812b, 4, GREEN);
ws2812b\_set\_color(&ws2812b, 5, GREEN);
ws2812b\_set\_color(&ws2812b, 6, GREEN);
ws2812b\_set\_color(&ws2812b, 7, GREEN);
ws2812b\_show(&ws2812b);

HAL\_Delay(500);

ws2812b\_set\_color(&ws2812b, 0, BLUE);
ws2812b\_set\_color(&ws2812b, 1, BLUE);
ws2812b\_set\_color(&ws2812b, 2, BLUE);
ws2812b\_set\_color(&ws2812b, 3, BLUE);
ws2812b\_set\_color(&ws2812b, 4, BLUE);
ws2812b\_set\_color(&ws2812b, 5, BLUE);
ws2812b\_set\_color(&ws2812b, 6, BLUE);
ws2812b\_set\_color(&ws2812b, 7, BLUE);
ws2812b\_show(&ws2812b);

HAL\_Delay(500);

}
/\* USER CODE END 3 \*/
}

文章来源: https://iotsmart.blog.csdn.net/article/details/125438805