1
电子说
利用零知增强版的GPIO 模拟时序
在本教程中,我们将探讨如何使用 零知增强版的 GPIO 接口来模拟 WS2812B LED 灯带的信号传输时序,从而实现对单色或多彩 LED 灯带的控制。这种技术允许我们避开专用驱动库,直接与硬件进行交互,理解并掌握 WS2812B 的通信机制。
一、工具原料
电脑、Windows系统
零知增强版开发板
Micro-usb线
WS2812RGB灯
WS2812B 是一款内含控制器芯片的全彩 LED 灯珠,每个灯珠可以独立显示红、绿、蓝三色。它通过单一数据线接收命令,实现高精度颜色控制。
二、硬件连接
零知增强版 | WS2812B |
5V | VCC |
GND | GND |
51 | Din |
1、硬件连接示意图
2、实际效果
三、传输时序和颜色控制
1、信号传输时序
WS2812B 的数据传输遵循特定的时间序列:
高电平持续时间决定比特值:T1H 和 T0H 分别代表比特 1 和比特 0 的高电平持续时间。
低电平持续时间:T1L 和 T0L。
注:T1H 为 800ns,T1L 为 450ns 表示 1 比特。
T0H 为 400ns,T0L 为 850ns 表示 0 比特。
2、颜色控制
控制全局亮度和遵循WS2812B发送的时序:
通过brightness参数调节RGB灯的全局亮度
WS2812B协议发送时序为G -> R -> B
四、代码驱动
1、相关定义和初始化
// WS2812B相关定义 #define WS2812B_PIN 51 // WS2812B数据引脚 #define NUM_LEDS 8 // 灯珠数量 #define MAX_BRIGHTNESS 0.5 // 全局亮度调节(范围:0.0 - 1.0) // WS2812B控制协议时间(根据各自的时序进行修改该定义) #define T1H 800 #define T1L 450 #define T0H 400 #define T0L 850 // 初始化WS2812B引脚 void setupWS2812B() { pinMode(WS2812B_PIN, OUTPUT); digitalWrite(WS2812B_PIN, LOW); } // 更精确的纳秒延时函数(这里只是示例,实际可能需要更复杂的实现) // 假设使用了支持纳秒级延时的定时器库 // 这里暂时使用简单的微秒级延时近似 void delayNanoseconds(unsigned long ns) { delayMicroseconds(ns / 1000); }
2、控制颜色和发送相关数据
// 发送一个比特 void WS2812B_SendBit(bool bitVal) { if (bitVal) { // 发送逻辑1 digitalWrite(WS2812B_PIN, HIGH); delayNanoseconds(T1H); digitalWrite(WS2812B_PIN, LOW); delayNanoseconds(T1L); } else { // 发送逻辑0 digitalWrite(WS2812B_PIN, HIGH); delayNanoseconds(T0H); digitalWrite(WS2812B_PIN, LOW); delayNanoseconds(T0L); } } // 发送一个字节 void WS2812B_SendByte(uint8_t byte) { for (int i = 7; i >= 0; i--) { WS2812B_SendBit(byte & (1 < < i)); } } // 发送RGB颜色数据(带亮度调节) void WS2812B_SendColor(uint8_t red, uint8_t green, uint8_t blue, float brightness) { // 应用全局亮度调节 red = (uint8_t)(red * brightness * MAX_BRIGHTNESS); green = (uint8_t)(green * brightness * MAX_BRIGHTNESS); blue = (uint8_t)(blue * brightness * MAX_BRIGHTNESS); // WS2812B协议发送顺序:G - > R - > B WS2812B_SendByte(green); WS2812B_SendByte(red); WS2812B_SendByte(blue); }
3、实现流水灯、呼吸灯等功能
// 效果:彩虹追逐 void rainbowChaseEffect(uint8_t wait) { for (int offset = 0; offset < 255; offset++) { for (int i = 0; i < NUM_LEDS; i++) { int hue = (i * 255 / NUM_LEDS + offset) % 255; uint8_t r = 0, g = 0, b = 0; if (hue < 85) { r = 255 - hue * 3; g = hue * 3; b = 0; } else if (hue < 170) { hue -= 85; r = 0; g = 255 - hue * 3; b = hue * 3; } else { hue -= 170; r = hue * 3; g = 0; b = 255 - hue * 3; } WS2812B_SendColor(r, g, b, MAX_BRIGHTNESS); } delay(wait); } } // 呼吸灯效果 void breathAndFlow(uint8_t red, uint8_t green, uint8_t blue, uint8_t steps, uint16_t period, uint8_t wait, uint8_t iterations) { int ledStep[NUM_LEDS]; // 为每个 LED 创建一个步骤计数器 for (int i = 0; i < NUM_LEDS; i++) { ledStep[i] = 0; // 初始化每个LED的步进 } uint8_t cycleCounter = 0; // 添加循环计数器 while (cycleCounter < iterations) { // 有限循环,迭代指定次数 for (int i = 0; i < NUM_LEDS; i++) { // 计算当前 LED 的亮度比例 float brightness = (sin(ledStep[i] * (M_PI / (steps))) + 1) / 2; WS2812B_SendColor(red, green, blue, brightness); // 使用计算出的亮度 // 更新 LED 的步骤计数器,模拟呼吸效果 ledStep[i] = (ledStep[i] + 1) % (steps * 2); // 确保计数器在达到两倍步骤后重置 // 计算每个步骤的时间间隔 delayMicroseconds(period / steps); } // 在一轮呼吸之后关闭所有灯 clearAllLeds(); // 增加循环计数器 cycleCounter++; // 根据需要添加延迟,虽然这不是必须的 delay(wait); } } // 增加一个状态变量来记录是否有颜色覆盖 bool isCovered = false; // 流水灯 void ShampEffect(uint8_t red, uint8_t green, uint8_t blue, uint8_t trailDecay, uint8_t wait) { // 特殊处理第一个灯 WS2812B_SendColor(red, green, blue, MAX_BRIGHTNESS); // 从第二个灯开始的索引为1 for (int i = 0; i < NUM_LEDS; i++) { for (int j = 0; j <= NUM_LEDS; j++) { if (i - j == 0) { if (!isCovered) { // 如果没有被覆盖,设置为绿色 WS2812B_SendColor(0, 0, 0, MAX_BRIGHTNESS); } else { WS2812B_SendColor(red, green, blue, MAX_BRIGHTNESS); } } else { WS2812B_SendColor(0, 0, 0, MAX_BRIGHTNESS * trailDecay / 255.0); } } if (i == NUM_LEDS - 1) { // 当到达最后一个灯时,标记为已覆盖 isCovered = true; } delay(wait); } }
4、控制灯的状态
// 设置特定位置灯珠颜色 void setLedColor(uint8_t pos, uint8_t red, uint8_t green, uint8_t blue, float brightness) { if (pos < NUM_LEDS) { // 只发送前面灯珠的关闭信号,直到要设置颜色的灯珠位置 for (int i = 0; i < pos; i++) { WS2812B_SendColor(0, 0, 0, 0); } // 设置目标灯珠颜色 WS2812B_SendColor(red, green, blue, brightness); // 发送后面灯珠的关闭信号,从目标灯珠的下一个位置开始 for (int i = pos + 1; i < NUM_LEDS; i++) { WS2812B_SendColor(0, 0, 0, 0); } } } // 设置所有灯珠颜色 void setAllLeds(uint8_t red, uint8_t green, uint8_t blue, float brightness) { clearAllLeds();// 先清除所有灯珠,确保没有杂色 for (int i = 0; i < NUM_LEDS; i++) { WS2812B_SendColor(red, green, blue, brightness); } } // 清除所有灯珠 void clearAllLeds() { for (int i = 0; i < NUM_LEDS * 3; i++) { WS2812B_SendByte(0); } }
5、主循环
// 初始化 void setup() { setupWS2812B(); clearAllLeds(); // 确保灯带初始状态关闭 } // 主循环 void loop() { uint8_t Count = 0; //clearAllLeds(); // 设置第六个灯珠为蓝色 //setLedColor(5, 0, 0, 255, MAX_BRIGHTNESS); //delay(500); while(Count < 10) { ShampEffect(0, 0, random(255), 256, 200); Count ++; } //rainbowChaseEffect(1000); breathAndFlow(0,255,0,5,50,100,100); }
五、成果展示
将上诉代码验证后上传到零知板,可以看到以下流水灯、呼吸灯等测试结果。
https://live.csdn.net/v/437153?spm=1001.2014.3001.5501
使用 GPIO 模拟时序驱动 WS2812B LED 灯带
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !