WS2812E是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同,每个 元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内部 振荡器和可编程定电流控制部分,有效保证了像素点光的颜色高度一致。
一、模块来源
二、规格参数
工作电压:3.7-5.3V
工作电流:16MA
控制方式:单总线
管脚数量:4 Pin(2.54mm间距排针)
以上信息见厂家资料文件
三、移植过程
我们的目标是将例程移植至开发板上【能够实现设置彩灯颜色的功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
1、查看资料
WS2812的数据协议采用单线归零码的通讯方式,支持串行级联接口,能通过一根信号线完成数据的接收与解码。每个灯就是一个像素点,每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。
像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整 形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求。
控制方式
因为使用的是单总线,一根线完成一个灯要显示的24位颜色数据,是通过高低电平的时间长度来确定发送的是什么数据。24位的数据结构见下图。
其中G代表三色中的绿色,R代表三色中的红色,B表示三色中的蓝色。例如想要只显示红色则发送 0X00FF00即可。
控制时序
发送24位颜色数据,是通过高低电平的时间长度来确定发送的是0还是1。
发送一位数据0,需要总线拉高T0H的时间再拉低T0L的时间,WS2812才会自动识别该数据是0。
发送一位数据1,需要总线拉高T1H的时间再拉低T1L的时间,WS2812才会自动识别该数据是1
WS2812的控制时序是如此的短暂,有各种各样的干扰稍不注意时序就会乱掉,我们应该用什么样的硬件去帮我们实现控制呢?
我们接下来使用SPI的硬件进行时序控制,我们只需要使用MOSI这个引脚即可。
2、引脚选择
GND | GND |
DIN | PA9(SPI-MOSI) |
VCC | 5V0 |
GND | GND |
3、代码编写
新建两个文件 bsp_ws2812.c
和 bsp_ws2812.h
,并且将头文件路径添加到编译器中。
在文件 bsp_ws2812.c
和 bsp_ws2812.h
中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef BSP_CODE_BSP_WS2812_H_
#define BSP_CODE_BSP_WS2812_H_
#include "gd32vw55x.h"
#include "systick.h"
#ifndef u8
#define u8 uint8_t
#endif
#ifndef u16
#define u16 uint16_t
#endif
#ifndef u32
#define u32 uint32_t
#endif
#ifndef delay_ms
#define delay_ms(x) delay_1ms(x)
#endif
#ifndef delay_us
#define delay_us(x) delay_1us(x)
#endif
/* * * * * * * WS2812颜色和数量定义 * * * * * */
#define WS2812_MAX 8 // 彩灯最大个数
#define WS2812_NUMBERS 8 // 彩灯个数
#define WS2812_RED 0xff0000 // 红色
#define WS2812_GREEN 0x00ff00 // 绿色
#define WS2812_BLUE 0x0000ff // 蓝色
#define WS2812_BLACK 0x000000 // 熄灭
#define WS2812_WHITE 0xffffff // 白色
/* * * * * * * 引脚和SPI定义 * * * * * */
#define Module_RCU_Enable() \
rcu_periph_clock_enable(RCU_GPIOA); \
rcu_periph_clock_enable(RCU_SPI);
#define WS2812_MOSI_GPIO_PORT GPIOA
#define WS2812_MOSI_GPIO_PIN GPIO_PIN_9
#define WS2812_MOSI_GPIO_AF GPIO_AF_0
void WS2812_Init(void);// 初始化
int WS2812_RESET(void);
int WS2812_Set_Color(uint8_t LED_NUM, uint64_t Color); // 设置彩灯颜色
int WS2812_Send_Array(void); // 发送彩灯数据
#endif /* BSP_CODE_BSP_WS2812_H_ */
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
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include "bsp_ws2812.h"
#define WS2812_0_CODE 0xC0 // 0码
#define WS2812_1_CODE 0xFC // 1码
#define WS2812_RESET_CODE 0x00 // 复位码
static uint8_t LedsArray[WS2812_MAX * 3]; // 定义颜色数据存储数组
static uint32_t ledsCount = WS2812_NUMBERS; // 定义实际彩灯默认个数
static uint32_t nbLedsBytes = WS2812_NUMBERS*3; // 定义实际彩灯颜色数据个数
static uint8_t spi_read_write_byte(uint8_t dat)
{
uint8_t data = 0;
//等待发送缓冲区为空
while(RESET == spi_flag_get(SPI_FLAG_TBE) );
//通过SPI发送一个字节数据
spi_data_transmit(dat);
//等待接收缓冲区不空标志
while(RESET == spi_flag_get(SPI_FLAG_RBNE) );
data = spi_data_receive();
return data;
}
/******************************************************************
* 函 数 名 称:WS2812_WriteData
* 函 数 说 明:向WS2812写入len长度的字节
* 函 数 形 参:send_buff数据地址 len字节长度
* 函 数 返 回:0成功 -1失败
* 作 者:LCKFB
* 备 注:1码的时序 = 高电平580ns~1us 再低电平220ns~420ns
* 0码的时序 = 高电平220ns~380ns 再低电平580ns~1us
******************************************************************/
static int WS2812_WriteData(uint8_t *send_buff, uint32_t len)
{
int i, bit;
// 熄灭信号
spi_read_write_byte(0x00);
for (i = 0; i < len; i++)
{
for (bit = 0; bit < 8; bit++)
{
if (send_buff[i] & (0x80 >> bit)) // 当前位为1
{
//发送1码
spi_read_write_byte(WS2812_1_CODE);
}
else // 当前位为0
{
//发送0码
spi_read_write_byte(WS2812_0_CODE);
}
}
}
return 0;
}
/******************************************************************
* 函 数 名 称:WS2812_RESET
* 函 数 说 明:复位ws2812
* 函 数 形 参:无
* 函 数 返 回:0成功 -1失败
* 作 者:LCKFB
* 备 注:低电平280us以上
******************************************************************/
int WS2812_RESET(void)
{
int i;
for(i = 0; i < 20; i++)
{
//发送复位码
spi_read_write_byte(WS2812_RESET_CODE);
}
return 0;
}
/******************************************************************
* 函 数 名 称:WS2812_Set_Color
* 函 数 说 明:设置彩灯颜色
* 函 数 形 参:LED_NUM控制的第几个灯 Color颜色数据
* 函 数 返 回:0成功 -1失败
* 作 者:LCKFB
* 备 注:
******************************************************************/
int WS2812_Set_Color(uint8_t LED_NUM, uint64_t Color)
{
if( LED_NUM >= ledsCount )
{
return -1; //to avoid overflow
}
LedsArray[LED_NUM * 3] = 0;
LedsArray[LED_NUM * 3 + 1] = 0;
LedsArray[LED_NUM * 3 + 2] = 0;
LedsArray[LED_NUM * 3] = (Color>>8) & 0xff;
LedsArray[LED_NUM * 3 + 1] = (Color>>16) & 0xff;
LedsArray[LED_NUM * 3 + 2] = (Color>>0) & 0xff;
return 0;
}
/******************************************************************
* 函 数 名 称:WS2812_Send_Array
* 函 数 说 明:发送彩灯数据
* 函 数 形 参:无
* 函 数 返 回:0成功 -1失败
* 作 者:LCKFB
* 备 注:
******************************************************************/
int WS2812_Send_Array(void)
{
WS2812_RESET();
delay_ms(10);
int ret = WS2812_WriteData(LedsArray, nbLedsBytes);
if(ret != 0)
{
return -1;
}
return 0;
}
/******************************************************************
* 函 数 名 称:WS2812_Init
* 函 数 说 明:初始化WS2812
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LCKFB
* 备 注:
******************************************************************/
void WS2812_Init(void)
{
//使能时钟
Module_RCU_Enable();
//引脚复用
gpio_af_set(WS2812_MOSI_GPIO_PORT, WS2812_MOSI_GPIO_AF, WS2812_MOSI_GPIO_PIN);
//引脚模式
gpio_mode_set(WS2812_MOSI_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, WS2812_MOSI_GPIO_PIN);
//输出模式
gpio_output_options_set(WS2812_MOSI_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, WS2812_MOSI_GPIO_PIN);
//SPI参数定义结构体
spi_parameter_struct spi_init_struct;
/* 配置 SPI 参数 */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 传输模式全双工
spi_init_struct.device_mode = SPI_MASTER; // 配置为主机
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT; // 软件cs
spi_init_struct.prescale = SPI_PSC_16;//16分频,频率=160MHz/16=10MHz
spi_init_struct.endian = SPI_ENDIAN_MSB;//高位在前
//将参数填入SPI
spi_init(&spi_init_struct);
/* 使能 SPI */
spi_enable();
}
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
四、移植验证
在 src\main.c
中输入代码如下:
#include "gd32vw55x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32vw553h_eval.h"
#include "bsp_ws2812.h"
/*!
\brief toggle the led every 500ms
\param[in] none
\param[out] none
\retval none
*/
void led_spark(void)
{
static __IO uint32_t timingdelaylocal = 0U;
if(timingdelaylocal) {
if(timingdelaylocal < 500U) {
gd_eval_led_on(LED1);
} else {
gd_eval_led_off(LED1);
}
timingdelaylocal--;
} else {
timingdelaylocal = 1000U;
}
}
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
#ifdef __FIRMWARE_VERSION_DEFINE
uint32_t fw_ver = 0;
#endif /* __FIRMWARE_VERSION_DEFINE */
/* configure systick */
systick_config();
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
/* initilize the LEDs, USART and key */
gd_eval_led_init(LED1);
gd_eval_com_init(EVAL_COM0);
gd_eval_key_init(KEY_TAMPER_WAKEUP, KEY_MODE_GPIO);
#ifdef __FIRMWARE_VERSION_DEFINE
fw_ver = gd32vw55x_firmware_version_get();
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n\n");
printf("\r\n=== Welcome to use the LC-GD32VW553-HMQ6 development board ====\r\n\n");
printf("\r\n======================= www.lckfb.com =========================\r\n\n");
printf("\r\n======================= wiki.lckfb.com ========================\r\n\n");
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
/* print firmware version */
printf("\r\nGD32VW55X series firmware version: V%d.%d.%d", (uint8_t)(fw_ver >> 24), (uint8_t)(fw_ver >> 16), (uint8_t)(fw_ver >> 8));
#endif /* __FIRMWARE_VERSION_DEFINE */
/* print out the clock frequency of system, AHB, APB1 and APB2 */
printf("\r\nCK_SYS is %d\r\n", rcu_clock_freq_get(CK_SYS));
printf("\r\nCK_AHB is %d\r\n", rcu_clock_freq_get(CK_AHB));
printf("\r\nCK_APB1 is %d\r\n", rcu_clock_freq_get(CK_APB1));
printf("\r\nCK_APB2 is %d\r\n", rcu_clock_freq_get(CK_APB2));
/* initialize WS2812 */
WS2812_Init();
printf("\r\nWS2812 initialized successfully!\r\n");
int i;
int while_count = 1;
uint32_t buff[4] = {WS2812_RED, WS2812_GREEN, WS2812_BLUE, WS2812_WHITE};
for (i = 0; i < 4; i++)
{
WS2812_Set_Color(0, buff[i]);
WS2812_Set_Color(1, buff[i]);
WS2812_Set_Color(2, buff[i]);
WS2812_Set_Color(3, buff[i]);
WS2812_Set_Color(4, buff[i]);
WS2812_Set_Color(5, buff[i]);
WS2812_Set_Color(6, buff[i]);
WS2812_Set_Color(7, buff[i]);
WS2812_Send_Array(); // 立即发送更新
delay_1ms(1000);
}
delay_1ms(1000);
i = 0;
while(1)
{
WS2812_Set_Color((i + 0) % 8, buff[0]);
WS2812_Set_Color((i + 1) % 8, buff[1]);
WS2812_Set_Color((i + 2) % 8, buff[2]);
WS2812_Set_Color((i + 3) % 8, buff[3]);
WS2812_Send_Array(); // 发送更新
// 清除其他LED
WS2812_Set_Color((i + 4) % 8, WS2812_BLACK);
WS2812_Set_Color((i + 5) % 8, WS2812_BLACK);
WS2812_Set_Color((i + 6) % 8, WS2812_BLACK);
WS2812_Set_Color((i + 7) % 8, WS2812_BLACK);
WS2812_Send_Array(); // 发送更新
i++;
while_count++;
delay_1ms(100);
// 清除
if(while_count >= 100)
{
while_count = 1;
i = 0;
}
}
}
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
编译烧录。
【代码下载】
- 跳转到
下载中心
去下载模块移植代码:【点击跳转🚀】