[学习资料] 如何使用PWM驱动WS2812B

[复制链接]
3005|10
 楼主| yiy 发表于 2025-2-26 19:11 | 显示全部楼层 |阅读模式
WS2812B是一种常见的RGB LED灯带,每个LED都可以独立控制颜色和亮度。使用PWM(脉宽调制)驱动WS2812B时,通常通过微控制器(如Arduino、ESP32等)生成特定的PWM信号来控制LED的颜色和亮度。

1. 硬件连接
VCC:接5V电源

GND:接地

DIN:接微控制器的PWM输出引脚

2. PWM信号要求
WS2812B的通信协议基于特定的时序,每个bit的数据通过PWM信号的占空比来表示:

0:高电平时间约0.35µs,低电平时间约0.80µs

1:高电平时间约0.70µs,低电平时间约0.60µs

3. 代码实现
以Arduino为例,使用Adafruit_NeoPixel库可以方便地控制WS2812B。

安装库
在Arduino IDE中,通过库管理器安装Adafruit_NeoPixel库。

示例代码
  1. #include <Adafruit_NeoPixel.h>

  2. #define PIN 6  // PWM输出引脚
  3. #define NUM_LEDS 30  // LED数量

  4. Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);

  5. void setup() {
  6.   strip.begin();
  7.   strip.show();  // 初始化所有LED为关闭状态
  8. }

  9. void loop() {
  10.   // 设置第一个LED为红色
  11.   strip.setPixelColor(0, strip.Color(255, 0, 0));
  12.   strip.show();
  13.   delay(500);

  14.   // 设置第二个LED为绿色
  15.   strip.setPixelColor(1, strip.Color(0, 255, 0));
  16.   strip.show();
  17.   delay(500);

  18.   // 设置第三个LED为蓝色
  19.   strip.setPixelColor(2, strip.Color(0, 0, 255));
  20.   strip.show();
  21.   delay(500);

  22.   // 关闭所有LED
  23.   for (int i = 0; i < NUM_LEDS; i++) {
  24.     strip.setPixelColor(i, 0);
  25.   }
  26.   strip.show();
  27.   delay(500);
  28. }
4. 注意事项
电源:确保电源足够稳定,避免电压波动影响LED显示效果。

信号线:信号线长度不宜过长,避免信号衰减。

刷新率:WS2812B的刷新率较高,确保微控制器的处理能力足够。

通过以上步骤,你可以使用PWM信号驱动WS2812B LED灯带,实现丰富的灯光效果。

 楼主| yiy 发表于 2025-2-26 19:13 | 显示全部楼层
使用PIC16F877A的PWM模块驱动WS2812B LED灯带需要手动生成符合WS2812B通信协议的信号。由于WS2812B的通信协议对时序要求非常严格,通常需要使用定时器和手动控制GPIO引脚来生成所需的PWM信号。
以下是使用PIC16F877A驱动WS2812B的步骤和示例代码:
1. 硬件连接
VCC:接5V电源

GND:接地

DIN:接PIC16F877A的GPIO引脚(如RC2)

2. WS2812B通信协议
WS2812B的通信协议基于特定的时序:

0:高电平时间约0.35µs,低电平时间约0.80µs

1:高电平时间约0.70µs,低电平时间约0.60µs

每个LED需要24位数据(8位绿色 + 8位红色 + 8位蓝色)

数据发送完毕后,需要至少50µs的低电平复位信号。

3. PIC16F877A配置
PIC16F877A的PWM模块无法直接生成WS2812B所需的信号,因此需要通过定时器和手动控制GPIO引脚来实现。

定时器配置
使用定时器(如Timer0或Timer1)来生成精确的延时。

根据PIC16F877A的时钟频率(如4MHz、8MHz等),计算定时器的预分频和计数值,以生成0.35µs和0.70µs的高电平时间。

GPIO配置
配置一个GPIO引脚(如RC2)为输出模式,用于发送数据信号。

4. 代码实现
以下是一个简单的示例代码,使用PIC16F877A的GPIO和定时器来驱动WS2812B。

初始化代码
  1. #include <xc.h>

  2. #define _XTAL_FREQ 4000000  // 4MHz时钟频率
  3. #define DIN_PIN RC2         // 数据引脚连接到RC2

  4. void WS2812B_SendBit(uint8_t bit) {
  5.     if (bit) {
  6.         DIN_PIN = 1;        // 高电平
  7.         __delay_us(0.7);    // 0.7µs高电平
  8.         DIN_PIN = 0;        // 低电平
  9.         __delay_us(0.6);    // 0.6µs低电平
  10.     } else {
  11.         DIN_PIN = 1;        // 高电平
  12.         __delay_us(0.35);   // 0.35µs高电平
  13.         DIN_PIN = 0;        // 低电平
  14.         __delay_us(0.8);    // 0.8µs低电平
  15.     }
  16. }

  17. void WS2812B_SendByte(uint8_t byte) {
  18.     for (uint8_t i = 0; i < 8; i++) {
  19.         WS2812B_SendBit(byte & 0x80);  // 从最高位开始发送
  20.         byte <<= 1;
  21.     }
  22. }

  23. void WS2812B_SendColor(uint8_t green, uint8_t red, uint8_t blue) {
  24.     WS2812B_SendByte(green);
  25.     WS2812B_SendByte(red);
  26.     WS2812B_SendByte(blue);
  27. }

  28. void WS2812B_Reset() {
  29.     DIN_PIN = 0;            // 低电平
  30.     __delay_us(50);         // 50µs复位信号
  31. }

  32. void main() {
  33.     TRISC2 = 0;             // 设置RC2为输出
  34.     DIN_PIN = 0;            // 初始化为低电平

  35.     while (1) {
  36.         // 设置第一个LED为红色
  37.         WS2812B_SendColor(0, 255, 0);
  38.         WS2812B_Reset();
  39.         __delay_ms(500);

  40.         // 设置第二个LED为绿色
  41.         WS2812B_SendColor(255, 0, 0);
  42.         WS2812B_Reset();
  43.         __delay_ms(500);

  44.         // 设置第三个LED为蓝色
  45.         WS2812B_SendColor(0, 0, 255);
  46.         WS2812B_Reset();
  47.         __delay_ms(500);
  48.     }
  49. }
5. 代码说明
WS2812B_SendBit:发送一个bit(0或1),根据WS2812B的时序要求控制高电平和低电平的时间。

WS2812B_SendByte:发送一个字节(8位),从最高位开始发送。

WS2812B_SendColor:发送24位颜色数据(绿色、红色、蓝色)。

WS2812B_Reset:发送复位信号(50µs低电平)。

main:主循环中依次设置LED的颜色。

6. 注意事项
时钟频率:代码中的延时基于4MHz时钟频率。如果使用其他频率,需要调整延时函数。

信号精度:WS2812B对时序要求非常严格,确保延时函数的精度。

电源稳定性:确保5V电源稳定,避免电压波动影响LED显示效果。

通过以上方法,你可以使用PIC16F877A的GPIO和定时器来驱动WS2812B LED灯带。如果需要驱动多个LED,可以将颜色数据存储在数组中,并依次发送。


 楼主| yiy 发表于 2025-2-26 19:15 | 显示全部楼层
或者这样做
  1. #include <xc.h>

  2. #define _XTAL_FREQ 20000000  // 20MHz时钟频率
  3. #define DIN_PIN RC2           // 数据引脚连接到RC2(PWM输出)

  4. void PWM_Init() {
  5.     // 配置PWM模块
  6.     PR2 = 0xFF;               // PWM周期寄存器
  7.     CCP1CON = 0x0C;           // 配置CCP1为PWM模式
  8.     T2CON = 0x04;             // 启用Timer2,预分频为1
  9.     CCPR1L = 0x00;            // 初始占空比为0
  10.     TRISC2 = 0;               // 设置RC2为输出
  11. }

  12. void PWM_SetDuty(uint8_t duty) {
  13.     CCPR1L = duty >> 2;       // 设置占空比高8位
  14.     CCP1CONbits.DC1B = duty & 0x03;  // 设置占空比低2位
  15. }

  16. void WS2812B_SendBit(uint8_t bit) {
  17.     if (bit) {
  18.         PWM_SetDuty(180);     // 占空比约为70%(模拟“1”信号)
  19.         __delay_us(0.7);     // 高电平时间0.7µs
  20.     } else {
  21.         PWM_SetDuty(90);      // 占空比约为35%(模拟“0”信号)
  22.         __delay_us(0.35);     // 高电平时间0.35µs
  23.     }
  24.     PWM_SetDuty(0);           // 低电平
  25.     __delay_us(0.6);          // 低电平时间
  26. }

  27. void WS2812B_SendByte(uint8_t byte) {
  28.     for (uint8_t i = 0; i < 8; i++) {
  29.         WS2812B_SendBit(byte & 0x80);  // 从最高位开始发送
  30.         byte <<= 1;
  31.     }
  32. }

  33. void WS2812B_SendColor(uint8_t green, uint8_t red, uint8_t blue) {
  34.     WS2812B_SendByte(green);
  35.     WS2812B_SendByte(red);
  36.     WS2812B_SendByte(blue);
  37. }

  38. void WS2812B_Reset() {
  39.     PWM_SetDuty(0);           // 低电平
  40.     __delay_us(50);          // 50µs复位信号
  41. }

  42. void main() {
  43.     PWM_Init();              // 初始化PWM模块

  44.     while (1) {
  45.         // 设置第一个LED为红色
  46.         WS2812B_SendColor(0, 255, 0);
  47.         WS2812B_Reset();
  48.         __delay_ms(500);

  49.         // 设置第二个LED为绿色
  50.         WS2812B_SendColor(255, 0, 0);
  51.         WS2812B_Reset();
  52.         __delay_ms(500);

  53.         // 设置第三个LED为蓝色
  54.         WS2812B_SendColor(0, 0, 255);
  55.         WS2812B_Reset();
  56.         __delay_ms(500);
  57.     }
  58. }
 楼主| yiy 发表于 2025-2-27 19:36 | 显示全部楼层
我觉得应该是按照一定的字节,比如一次更新多少。定义一个缓存空间,然后按照一定的格式写入PWM,完成后,关闭PWM。
gejigeji521 发表于 2025-2-28 15:02 | 显示全部楼层
目前看SPI是最适合的。可以之间当作数据发送。
kmnqhaha 发表于 2025-4-25 15:33 | 显示全部楼层
WS2812B的通信基于特定的时序协议,并通过PWM信号来控制LED的亮度和颜色。每个LED的颜色通过三个值(红、绿、蓝)进行表示,每个颜色由8位(即一个字节)来控制。

0的表示方式:高电平时间为0.35µs,低电平时间为0.80µs

1的表示方式:高电平时间为0.70µs,低电平时间为0.60µs

这些时间窗口会被嵌入在一个比特流中,控制每个LED的RGB颜色。
更多更合适ii 发表于 2025-4-25 17:18 | 显示全部楼层
可以使用 Adafruit_NeoPixel 库,这使得控制WS2812B变得非常简单。
桃乐丝 发表于 2025-8-29 12:28 | 显示全部楼层
用 PWM 驱动 WS2812B 需严格遵循其时序:0 码为 0.4μs 高电平 + 0.85μs 低电平,1 码为 0.8μs 高电平 + 0.45μs 低电平,复位需 > 50μs 低电平。配置 MCU 的 PWM 定时器,按 GRB 顺序输出对应占空比的脉冲串,通过精准控制高低电平时长模拟信号,需注意 PWM 频率与精度匹配时序要求。
四十四次日落 发表于 2025-9-27 15:26 | 显示全部楼层
使用 PWM 驱动 WS2812B,需按其时序:0 码为高电平 0.4μs + 低电平 0.85μs,1 码为高电平 0.8μs + 低电平 0.45μs,复位信号≥50μs 低电平。配置 MCU 的 PWM 模块到合适频率(约 800kHz),通过精确控制占空比生成对应码型,依次发送 GRB 格式数据,实现 LED 颜色控制。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

yiy

114

主题

1954

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部