零知开源——STM32F407VET6使用 GPIO 模拟时序驱动 WS2812B LED 灯带
在本教程中,我们将探讨如何使用 零知增强版的 GPIO 接口来模拟 WS2812B LED 灯带的信号传输时序,从而实现对单色或多彩 LED 灯带的控制。这种技术允许我们避开专用驱动库,直接与硬件进行交互,理解并掌握 WS2812B 的通信机制。WS2812B 是一款内含控制器芯片的全彩 LED 灯珠,每个灯珠可以独立显示红、绿、蓝三色。它通过单一数据线接收命令,实现高精度颜色控制。
利用零知增强版的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);
}
五、成果展示
将上诉代码验证后上传到零知板,可以看到以下流水灯、呼吸灯等测试结果。
使用 GPIO 模拟时序驱动 WS2812B LED 灯带

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)