Arduino入门教程:12、WS2812B
飞书文档https://x509p6c8to.feishu.cn/docx/B5EhdBBego2mDXxesvfctQnDnKd
WS2812B是一款全彩LED控制IC,单总线控制。
接线说明:
注意线的颜色 | “三角形”符号代表方向 接反点不亮 | 接上TypeC烧录接口 基于手册时序实现WS2812B控制: |
由手册可知 WS2812B的供电电压在3.7V~5.3V,一般5V供电就可以了。
信号时序如下图,要注意高低电平的时间一定要控制在规格书的要求范围内。WS2812B可以级联,每个灯接收的是24bit数据(GRB颜色值),D1灯在收到24bit数据后,会把数据保存,如果还收到数据,会通过DO脚传给D2。
- 0码:高电平持续200ns - 380ns后跳变为低电平,低电平持续580ns - 1us;
- 1码:高电平持续580 - 1us后跳变为低电平,低电平持续580ns - 1us;
- Resset码:低电平保持时间大于280us
驱动WS2812B需要纳秒级别的延时,arduino 的函数delay(xx);或者Microsecond();只能实现毫秒或者微秒级别的延时,所以我们需要根据自己写延时函数满足纳秒延时。
一般单片机执行NOP();指令都有固定的指令周期,但是ESP32的arduino sdk可能由于封装多层的原因,实测并不线性,所以我们用示波器测试了337ns 600ns 280us几个延时函数。
void delay_337_ns()
{for (int i = 0; i < 1; i++){NOP();}
}void delay_600_ns()
{uint32_t count = 10;for (int i = 0; i < count; i++){NOP();}
}void delay_280_us()
{uint32_t count = 280 * 40;for (uint32_t i = 0; i < count; i++){NOP();}
}
根据手册时序要求,我们可以实现0码、1码、RESET码
/*** @brief 发送0 Code**/
void set_0_num()
{digitalWrite(PIN_LED, HIGH);delay_337_ns();digitalWrite(PIN_LED, LOW);delay_600_ns();
}/*** @brief 发送1 Code**/
void set_1_num()
{digitalWrite(PIN_LED, HIGH);delay_600_ns();digitalWrite(PIN_LED, LOW);delay_600_ns();
}/*** @brief 发送reset**/
void set_reset()
{digitalWrite(PIN_LED, LOW);delay_280_us();
}
最后,我们就可以按照标准色卡的RGB值,实现数据的传输啦.
#include <Arduino.h>// 信号IO引脚
#define PIN_LED 16// 颜色
typedef struct
{uint8_t R;uint8_t G;uint8_t B;
} t_color;void delay_337_ns()
{for (int i = 0; i < 1; i++){NOP();}
}void delay_600_ns()
{uint32_t count = 10;for (int i = 0; i < count; i++){NOP();}
}void delay_280_us()
{uint32_t count = 280 * 40;for (uint32_t i = 0; i < count; i++){NOP();}
}/*** @brief 发送0 Code**/
void set_0_num()
{digitalWrite(PIN_LED, HIGH);delay_337_ns();digitalWrite(PIN_LED, LOW);delay_600_ns();
}/*** @brief 发送1 Code**/
void set_1_num()
{digitalWrite(PIN_LED, HIGH);delay_600_ns();digitalWrite(PIN_LED, LOW);delay_600_ns();
}/*** @brief 发送reset**/
void set_reset()
{digitalWrite(PIN_LED, LOW);delay_280_us();
}/*** @brief 根据每Bit数值设置发送0Code / 1Code** @param dat*/
void ws2812b_writebyte(uint8_t dat)
{for (uint8_t i = 0; i < 8; i++){// 先发送高位if (dat & 0x80) // 该位为1set_1_num();else // 该位为0set_0_num();dat <<= 1;}
}/*** @brief 按手册写入G R B 24bit数据** @param pColor*/
void ws2812b_writecolor(t_color *pColor)
{ws2812b_writebyte(pColor->G);ws2812b_writebyte(pColor->R);ws2812b_writebyte(pColor->B);
}/*** @brief 多LED控制 // BL_Color 0x000000 //关---清空颜色// W_Color 0xFFFFFF //白光// R_Color 0xFF0000 //红色// G_Color 0x00FF00 //绿色// B_Color 0x0000FF //蓝色**/
void ws2812b_more_led_test()
{t_color temp;temp.R = 0x00;temp.B = 0xFF;temp.G = 0x00;const int PIXEL_NUM = 10;while (true){for (uint8_t i = 0; i < PIXEL_NUM; i++){ws2812b_writecolor(&temp);}set_reset();}
}/*** @brief 单颗LED控制**/
void ws2812b_one_led_test()
{t_color temp;temp.R = 0x00;temp.B = 0xFF;temp.G = 0x00;while (true){ws2812b_writecolor(&temp);set_reset();}
}void setup()
{Serial.begin(9600);Serial.printf("setup\n");pinMode(PIN_LED, OUTPUT);
}void loop()
{ws2812b_one_led_test();// ws2812b_more_led_test();
}
到这里,你已经可以自己手写WB2812B的控制代码了,但是你会发现,整体代码实现还是有点多的,那Arduino有没有一些封装好的库可以让我们快速使用呢?答案当然是有的,那就是FastLED。
基于FastLED实现灯珠控制
1、添加FastLED库依赖
2、编写代码
#include <FastLED.h>// 一共有多少个LED灯?
#define NUM_LEDS 60
// 绿色的数据线接在几号端口?
#define DATA_PIN 5
// 初始化LED灯数组
CRGB leds[NUM_LEDS];void setup()
{/*NEOPIXEL:控制灯珠的类型LED_PIN:控制板上使用的IO端口leds:灯带NUM_LEDS:灯珠的总数量*/FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);// 初始化LEDS.showColor(CRGB(255, 0, 0));delay(500);LEDS.showColor(CRGB(0, 255, 0));delay(500);LEDS.showColor(CRGB(0, 0, 255));delay(500);LEDS.showColor(CRGB(0, 0, 0));
}void loop()
{
}
光随屏动效果-串口版本
代码实现
实现原理:
- LED灯板上电后,发送Ada字符串到电脑,电脑端的软件读取到Ada字符后,初始化成功。
- 电脑端软件读取屏幕颜色值,拼凑为指令Ada+hi+lo+checksum+rgb数组,下发给LED灯板。
- LED灯板接收到Ada字符后,读取hi lo checksum校验码信息,验证正确后,读取rgb值实现灯颜色控制。
#include <FastLED.h>//一共有多少个LED灯?
#define NUM_LEDS 60
//绿色的数据线接在几号端口?
#define DATA_PIN 5
//通讯口波特率
#define serialRate 115200//口令,可以理解为通讯协议,对口号,芝麻开门的意思
uint8_t prefix[] = {'A', 'd', 'a'}, hi, lo, chk, i;
//初始化LED灯数组
CRGB leds[NUM_LEDS];void setup() {//NEO像素色彩 IO引脚 led数组 led灯数量FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);//初始化LEDS.showColor(CRGB(255, 0, 0));delay(500);LEDS.showColor(CRGB(0, 255, 0));delay(500);LEDS.showColor(CRGB(0, 0, 255));delay(500);LEDS.showColor(CRGB(0, 0, 0));Serial.begin(serialRate);//发送对口号Serial.print("Ada\n");
}void loop() {//等待串口返回口号for(i = 0; i < sizeof prefix; ++i) {waitLoop: while (!Serial.available()) ;;//读取串口数据,直至收到Ada口令词退出if(prefix[i] == Serial.read()) continue;// otherwise, start overi = 0;goto waitLoop;}// 读取Hi, Lo, Checksum while (!Serial.available()) ;;hi=Serial.read();while (!Serial.available()) ;;lo=Serial.read();while (!Serial.available()) ;;chk=Serial.read();//如果校验和不匹配,返回等待if (chk != (hi ^ lo ^ 0x55)) {i=0;goto waitLoop;}memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));//读取传输数据并设置 LED 值for (uint8_t i = 0; i < NUM_LEDS; i++) {byte r, g, b; while(!Serial.available());r = Serial.read();while(!Serial.available());g = Serial.read();while(!Serial.available());b = Serial.read();leds[i].r = r;leds[i].g = g;leds[i].b = b;}//刷新点亮FastLED.show();
}
电脑端配置
1、电脑一台+流光溢彩PCBA一套+USB线(有线连接)
使用USB线连接电脑与板卡,板卡上电,然后在设备管理器中找到对应COM口,记得烧录代码实现部分的固件哦。
2、把灯带贴到显示器或支撑板后
3、安装Prismatik软件
安装过程比较简单,全部默认点击下一步,安装完成即可。参考飞书文档
4、配置
安装完毕后,会直接弹出配置向导,如果没有弹出,就去点桌面右下角,有个月亮图标,右击它,Settings!
下面是点 Setting后弹出的界面,在Device页面,点“Run Configuration wizard”,打开设置向导!
在这里,无线连接和有线连接唯一的区别是,无线选择局域网方式,有线选择时需选择COM口方式。
有线连接:需要设置COM口,电脑连接板卡后,打开系统设备管理器查看
进入下一步,设置好名称后,就是配置灯与显示器的位置关系了:
1、选择显示器,多个显示器请选择贴LED灯的那个
2、设置LED数量
3、配置LED灯位置,我们可以看到屏幕边上的1-60就是实际要采集的LED颜色位置,可以根据我们贴LED的位置进行拖拽修改,直接按默认不修改也可以
配置完成后按下一步,按默认完成配置即可,完成配置后,我们可以在屏幕菜单栏右击点击Turn on即可完成流光溢彩。