第20章:WS2812 RGB 灯珠驱动
本章讲解 WS2812(NeoPixel)RGB 灯珠的工作原理、单总线数据协议与炫彩灯效编程。
20.1 WS2812 简介
20.1.1 特点
WS2812 是一种智能控制 LED:
- 内置驱动 IC
- 单线级联,无需寻址
- 24 位颜色(RGB 各 8 位)
- 低电压驱动(3.5-5.3V)
20.1.2 内部结构
┌───────────────────────────┐
│ WS2812B 内部结构 │
│ ┌─────┐ ┌─────────────┐ │
│ │ LED │ │ 控制芯片 │ │
│ │ R G │ │ 数据锁存 │ │
│ │ B │ │ PWM 驱动 │ │
│ └─────┘ └─────────────┘ │
│ │
│ DIN ─→ 控制芯片 ─→ DOUT │
└───────────────────────────┘
20.1.3 级联原理
MCU → LED1 → LED2 → LED3 → ...
│ │ │
DIN DOUT DIN DOUT
↓ ↓
接收 24 位后,剩余数据传递给下一个
20.2 数据协议
20.2.1 时序规范
WS2812 使用单总线协议,通过脉宽编码:
逻辑 "0":
┌────┐
│ │ 高: 220-380ns
│ └────────────
低: 580-1000ns
总计约 1.25μs
逻辑 "1":
┌──────────┐
│ │ 高: 580-1000ns
│ └────
低: 580-1000ns
总计约 1.25μs
复位信号:
────────────────────
低电平 > 50μs
20.2.2 数据格式
每个 LED 接收 24 位数据:
┌────────┬────────┬────────┐
│ G[7:0] │ R[7:0] │ B[7:0] │
└────────┴────────┴────────┘
绿色 红色 蓝色
高位在前
注意:WS2812 是 GRB 顺序,不是 RGB!
20.2.3 数据传输过程
时间线:
MCU 发送: [LED1 数据 24位] [LED2 数据 24位] [LED3 数据 24位] [复位]
│ │ │
LED1: 锁存 24位 │ │
│ │
LED2: 锁存 24位 │
│
LED3: 锁存 24位
20.3 MicroPython 驱动
20.3.1 使用 neopixel 库
'''
实验:WS2812 基础控制
接线:DIN 连接 GP16
'''
from machine import Pin
import neopixel
import time
# 创建 NeoPixel 对象
NUM_LEDS = 4
np = neopixel.NeoPixel(Pin(16), NUM_LEDS)
# 设置颜色 (R, G, B)
np[0] = (255, 0, 0) # 红色
np[1] = (0, 255, 0) # 绿色
np[2] = (0, 0, 255) # 蓝色
np[3] = (255, 255, 0) # 黄色
# 更新显示
np.write()
20.3.2 使用 PIO 状态机
Pico 可以使用 PIO 实现精确时序:
'''
实验:PIO 驱动 WS2812
'''
import array
import time
from machine import Pin
import rp2
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT,
autopull=True, pull_thresh=24)
def ws2812():
T1 = 2
T2 = 5
T3 = 3
wrap_target()
label("bitloop")
out(x, 1) .side(0) [T3 - 1]
jmp(not_x, "do_zero") .side(1) [T1 - 1]
jmp("bitloop") .side(1) [T2 - 1]
label("do_zero")
nop() .side(0) [T2 - 1]
wrap()
class WS2812:
def __init__(self, pin, num_leds, brightness=1.0):
self.num_leds = num_leds
self.brightness = brightness
self.ar = array.array("I", [0] * num_leds)
self.sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(pin))
self.sm.active(1)
def __setitem__(self, index, color):
r, g, b = color
r = int(r * self.brightness)
g = int(g * self.brightness)
b = int(b * self.brightness)
self.ar[index] = (g << 16) | (r << 8) | b
def __getitem__(self, index):
val = self.ar[index]
return ((val >> 8) & 0xFF, (val >> 16) & 0xFF, val & 0xFF)
def fill(self, color):
for i in range(self.num_leds):
self[i] = color
def write(self):
for i in range(self.num_leds):
self.sm.put(self.ar[i], 8)
time.sleep_us(60)
# 使用
strip = WS2812(16, 4, brightness=0.5)
strip[0] = (255, 0, 0)
strip.write()
20.4 灯光效果
20.4.1 彩虹效果
'''
实验:彩虹流水效果
'''
import neopixel
from machine import Pin
import time
np = neopixel.NeoPixel(Pin(16), 8)
def wheel(pos):
"""色轮函数,返回 0-255 位置的颜色"""
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
elif pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
else:
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
def rainbow_cycle(wait_ms=20):
"""彩虹循环"""
for j in range(256):
for i in range(len(np)):
rc_index = (i * 256 // len(np) + j) & 255
np[i] = wheel(rc_index)
np.write()
time.sleep_ms(wait_ms)
while True:
rainbow_cycle()
20.4.2 流水灯
'''
实验:流水灯效果
'''
def color_wipe(color, wait_ms=50):
"""逐个点亮"""
for i in range(len(np)):
np[i] = color
np.write()
time.sleep_ms(wait_ms)
# 逐个熄灭
for i in range(len(np)):
np[i] = (0, 0, 0)
np.write()
time.sleep_ms(wait_ms)
while True:
color_wipe((255, 0, 0), 100)
color_wipe((0, 255, 0), 100)
color_wipe((0, 0, 255), 100)
20.4.3 呼吸灯
'''
实验:全体呼吸效果
'''
import math
def breathing(color, cycles=3, speed=0.02):
"""呼吸灯"""
r, g, b = color
for _ in range(cycles):
# 渐亮
for i in range(100):
brightness = (math.sin(i * math.pi / 100) ** 2)
np.fill((int(r * brightness),
int(g * brightness),
int(b * brightness)))
np.write()
time.sleep(speed)
# 渐暗(已包含在正弦波中)
breathing((255, 100, 0))
20.4.4 跑马灯
'''
实验:跑马灯效果
'''
def chase(color, bg_color=(0,0,0), wait_ms=50, width=1):
"""跑马灯"""
for offset in range(len(np)):
for i in range(len(np)):
if i >= offset and i < offset + width:
np[i] = color
else:
np[i] = bg_color
np.write()
time.sleep_ms(wait_ms)
while True:
chase((255, 0, 0), width=2)
chase((0, 255, 0), width=2)
chase((0, 0, 255), width=2)
20.4.5 闪烁星星
'''
实验:随机闪烁效果
'''
import random
def twinkle(color, count=3, wait_ms=100):
"""随机闪烁"""
for _ in range(20):
# 关闭所有
np.fill((0, 0, 0))
# 随机点亮几个
for _ in range(count):
i = random.randint(0, len(np) - 1)
brightness = random.uniform(0.3, 1.0)
np[i] = (int(color[0] * brightness),
int(color[1] * brightness),
int(color[2] * brightness))
np.write()
time.sleep_ms(wait_ms)
twinkle((255, 255, 255))
20.5 颜色处理
20.5.1 HSV 颜色空间
def hsv_to_rgb(h, s, v):
"""
HSV 转 RGB
h: 色相 0-360
s: 饱和度 0-1
v: 明度 0-1
"""
if s == 0:
return (int(v * 255), int(v * 255), int(v * 255))
h = h / 60
i = int(h)
f = h - i
p = int(v * (1 - s) * 255)
q = int(v * (1 - s * f) * 255)
t = int(v * (1 - s * (1 - f)) * 255)
v = int(v * 255)
if i == 0:
return (v, t, p)
elif i == 1:
return (q, v, p)
elif i == 2:
return (p, v, t)
elif i == 3:
return (p, q, v)
elif i == 4:
return (t, p, v)
else:
return (v, p, q)
# 使用 HSV 创建平滑彩虹
for hue in range(0, 360, 45):
color = hsv_to_rgb(hue, 1, 0.5)
print(f"色相 {hue}: RGB {color}")
20.5.2 亮度控制
def apply_brightness(color, brightness):
"""应用亮度系数"""
return tuple(int(c * brightness) for c in color)
# 50% 亮度
dim_red = apply_brightness((255, 0, 0), 0.5) # (127, 0, 0)
20.5.3 颜色渐变
def interpolate_color(color1, color2, factor):
"""
颜色插值
factor: 0.0 = color1, 1.0 = color2
"""
return tuple(
int(c1 + (c2 - c1) * factor)
for c1, c2 in zip(color1, color2)
)
# 红到蓝渐变
for i in range(11):
color = interpolate_color((255, 0, 0), (0, 0, 255), i / 10)
print(f"{i * 10}%: {color}")
20.6 实用应用
20.6.1 状态指示器
'''
实验:传感器状态可视化
'''
from machine import ADC
temp_sensor = ADC(4) # 内部温度
def temperature_to_color(temp):
"""温度映射到颜色"""
if temp < 20:
return (0, 0, 255) # 冷 - 蓝色
elif temp < 25:
return (0, 255, 0) # 舒适 - 绿色
elif temp < 30:
return (255, 255, 0) # 温暖 - 黄色
else:
return (255, 0, 0) # 热 - 红色
while True:
raw = temp_sensor.read_u16()
voltage = raw * 3.3 / 65535
temp = 27 - (voltage - 0.706) / 0.001721
color = temperature_to_color(temp)
np.fill(color)
np.write()
print(f"温度: {temp:.1f}°C")
time.sleep(1)
20.6.2 音量指示条
'''
实验:LED 音量条
'''
from machine import ADC
sound = ADC(26)
NUM_LEDS = 8
def volume_bar(level):
"""音量条显示"""
# level: 0-100
lit_leds = int(level / 100 * NUM_LEDS)
for i in range(NUM_LEDS):
if i < lit_leds:
if i < NUM_LEDS * 0.6:
np[i] = (0, 255, 0) # 绿
elif i < NUM_LEDS * 0.85:
np[i] = (255, 255, 0) # 黄
else:
np[i] = (255, 0, 0) # 红
else:
np[i] = (0, 0, 0)
np.write()
baseline = sum(sound.read_u16() for _ in range(100)) // 100
while True:
raw = sound.read_u16()
level = min(100, abs(raw - baseline) / 500 * 100)
volume_bar(level)
time.sleep_ms(10)
20.7 本章小结
本章介绍了 WS2812 RGB 灯珠驱动:
- WS2812 特点:
- 内置驱动 IC,单线级联
- 24 位颜色(GRB 顺序)
- 数据协议:
- 脉宽编码(高电平宽度区分 0/1)
- 24 位/LED,自动传递
- 灯光效果:
- 彩虹、流水、呼吸、跑马灯
- HSV 颜色空间便于编程
- 实用应用:
- 状态指示器
- 音量可视化
下一章将学习超声波测距系统。