本帖最后由 HonestQiao 于 2022-11-20 00:14 编辑
#申请原创#
我有一个宝贝WS2812B灯环,玩过的板子,我都要给点亮一番。
一、WS2812B控制信号了解:
WS2812B的具体介绍,这里就不详细说明了,在网上,有大量的文章资料进行说明。
要点亮WS2812B,重点在于理解以下要点:
- WS2812B是通过单总线控制的,也就是一根信号线,控制所有的LED
- WS2812B每收到一个特定的信号时,会认为收到了0码或者1码:信号总线上,高电平保持1s,低电平保持0.25s,则认为收到了1码;反之,如果高电平保持0.25s,低电平保持1s,则认为收到了0码。
- 每8个0/1码,对应一种颜色控制,我们可以理解为1个8位二进制,所以范围为0~255;一颗LED,共有三种颜色,接受顺序为GRB,每种颜色取值范围在0~255,十六进制0x00~0xFF;也就是控制一颗灯珠,需要3组8个的0/1码,一个24个0/1码
- 如果要控制多颗灯珠,则连续发送多组上述数据即可;及时某颗灯珠熄灭,也需要发送对应的0码,使其不亮。
- 如果要多次发送控制数据,则两次之间,需要低电平保持至少50us
了解了以上信息,我们就可以考虑使用NBK-RD8x3x核心开发板来进行控制。
二、NBK-RD8x3x核心开发板能否满足要求:
从上面的说明,可以看到WS2812B对信号时序有特定的要求,这就需要我们的控制板能够满足这些要求。
进过一番测试,确认在NBK-RD8x3x核心开发板上,直接通过控制IO接口的高低电平,即可满足要求。
通过官方资料了解到:RD8T36 系列具有超高速 1T 8051 CPU 内核,运行频率高达 32MHz。
那么,如果运行在32MHz,则每一个时钟周期的时间为:T0=1/fsoc=1/32MHz=0.03125us
WS2812B要求的最小控制时间细腻度为0.25us,也就是8个T0,因为 0.25us / T0 = 0.25us / 0.03125us = 8.
在C51上,使用1个nop(),就表示一次时钟周期,那么使用8次nop(),就能够实现0.25us的时间控制了。
做好了以上前期准备工作,我们就可以开始正式的工作了。
三、连线:
在NBK-RD8x3x核心开发板的核心开发板上,有两排共4组Arduino管脚,具体如下:
我使用的WS2812B灯环,有DIN和OUT两个接口,控制信号从DIN接入,OUT用于级联下级。
DIN上有三根线,就接到Arduino管脚上,包括5V、GND和信号控制引脚。信号控制引脚,选择了D8。
从官方提供的电路原理图,可以了解到D8具体对应的IO口:
从上图中,可以了解到D8对应的IO口为P06。
最终实物连接如下:
四、代码编写:
万事俱备,只欠代码。
具体的代码如下:WS2812B_Init.c
#include "H/Function_Init.H"
#include <intrins.h>
#define NOP \
{ \
_nop_(); \
_nop_(); \
_nop_(); \
_nop_(); \
_nop_(); \
_nop_(); \
_nop_(); \
_nop_(); \
}
#define LOW 0
#define HIGH 1
#define DIN P06
#define NUM 24
//拉低DIN保持50us以上
void ws2812_init() {
uchar i;
DIN = LOW;
for (i = 0; i <= 200; i++) {
NOP;
}
}
//高电平0.25us,低电平1us
void ws2812_write_0() {
DIN = HIGH;
NOP;
DIN = LOW;
NOP;
NOP;
NOP;
NOP;
}
//高电平1us,低电0.25us
void ws2812_write_1() {
DIN = HIGH;
NOP;
NOP;
NOP;
NOP;
DIN = LOW;
NOP;
}
void ws2812_write_24bits(unsigned long dat) {
uchar t[NUM] = {0};
uchar i;
for (i = 0; i < NUM; i++) {
if (dat >> i & 1) {
t[i] = HIGH;
} else {
t[i] = LOW;
}
}
for (i = 0; i < NUM; i++) {
if (t[i]) {
ws2812_write_1();
} else {
ws2812_write_0();
}
}
}
void WS2812B_Test() {
unsigned int i = 0;
unsigned int j = 0;
unsigned int index = 1;
unsigned long colors[8] = {0x000000, 0xff0000, 0x00ff00, 0x0000ff,
0xffff00, 0xff00ff, 0x00ffff, 0xffffff};
ws2812_init();
while (1) {
for (i = 0; i < NUM; i++) {
if (index % NUM == i) {
ws2812_write_24bits(colors[index % 7 + 1]);
} else {
ws2812_write_24bits(colors[0]);
}
}
index++;
if (index >= NUM) {
index = 0;
}
for (j = 0; j <= 5; j++) {
for (i = 0; i <= 4000; i++) {
NOP;
}
}
}
}
上述代码关键说明:
- #define NOP:用于定义8个nop()调用,获得0.25us时间占用
- ws2812_init():提供50us以上低电平保持
- ws2812_write_0():输出0码
- ws2812_write_1():输出1码
- ws2812_write_24bits():根据颜色值,输出24位的0/1码
- WS2812B_Test():测试主逻辑程序
- colors: 使用到的颜色值定义
代码中,还用到了index变量,通过其控制当前应该点亮第几颗灯珠,并使用其对7取余,从而则colors中选用对应的颜色。
然后,在main.c中调用即可,具体如下:
最后,编译下载,WS2812B灯环,就会欢快的跳跃起来:
|