从某多平台花了15元钱买了一米长的WS2812B彩灯,用于我的Pico编程试验,这种灯的神奇之处在于只需一根信号线,能够控制串联在一起的30颗LED灯珠(好像能长达1024颗灯),实现各种彩灯效果。
接线非常简单,正极接5V(我的灯带只有1米长,用树莓派Pico供电并不吃力,如果比较长的灯带,需要额外电源),一端接地,树莓派GP15接信号线Din。灯带里面的Do不用管它,它实际上是Dout的意思,信号经过Din处理后,点亮那个灯,吃掉一些字节,再经过Dout出来,从而可以串联更多的灯。
官方文档《Raspberry Pi Pico Python SDK》里有一节介绍PIO控制WS2812灯的代码,直接抄过来,修改一下灯的个数,引脚编号PIN_NUM,代码虽然不理解,但程序可以马上运行。
# Example using PIO to drive a set of WS2812 LEDs.
import array, time
from machine import Pin
import rp2
# Configure the number of WS2812 LEDs.
NUM_LEDS = 30
PIN_NUM = 15
brightness = 0.2
@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()
# Create the StateMachine with the ws2812 program, outputting on pin
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(PIN_NUM))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
##########################################################################
def pixels_show():
dimmer_ar = array.array("I", [0 for _ in range(NUM_LEDS)])
for i,c in enumerate(ar):
r = int(((c >> 8) & 0xFF) * brightness)
g = int(((c >> 16) & 0xFF) * brightness)
b = int((c & 0xFF) * brightness)
dimmer_ar[i] = (g<<16) + (r<<8) + b
sm.put(dimmer_ar, 8)
time.sleep_ms(10)
def pixels_set(i, color):
ar[i] = (color[1]<<16) + (color[0]<<8) + color[2]
def pixels_fill(color):
for i in range(len(ar)):
pixels_set(i, color)
def color_chase(color, wait):
for i in range(NUM_LEDS):
pixels_set(i, color)
time.sleep(wait)
pixels_show()
time.sleep(0.2)
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
def rainbow_cycle(wait):
for j in range(255):
for i in range(NUM_LEDS):
rc_index = (i * 256 // NUM_LEDS) + j
pixels_set(i, wheel(rc_index & 255))
pixels_show()
time.sleep(wait)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
COLORS = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE)
print("fills")
for color in COLORS:
pixels_fill(color)
pixels_show()
time.sleep(0.2)
print("chases")
for color in COLORS:
color_chase(color, 0.01)
print("rainbow")
rainbow_cycle(0)
网上还有一个更精简的版本,便于慢慢理解程序的主要逻辑。
import array
import utime
import machine
import rp2
NUM_LEDS = 30
@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()
# 建立状态机,设置输出针的编号
sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=machine.Pin(15))
# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)
# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])
# Cycle colours.
for i in range(4 * NUM_LEDS):
for j in range(NUM_LEDS):
r = j * 100 // (NUM_LEDS - 1)
b = 100 - j * 100 // (NUM_LEDS - 1)
if j != i % NUM_LEDS:
r >>= 3
b >>= 3
ar[j] = r << 16 | b
sm.put(ar, 8)
utime.sleep_ms(20)
# Fade out.
for _ in range(24):
for j in range(NUM_LEDS):
ar[j] >>= 1
sm.put(ar, 8)
utime.sleep_ms(50)
看看视频效果:
https://v.qq.com/x/page/o33047uvo1e.html
树莓派Pico控制WS2812b彩色灯珠
这里用到了PIO(Programmable IO )的概念,可以嵌入汇编语句,细节还有待后面进一步的学习。
还有一个状态机的概念,暂时也不太理解,没关系,先抄着慢慢学。
再来学习一下WS2812B的数据通讯协议,看用户手册,有这样一段话:
数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求。
可以看出,每经过一个像素点,吃掉24个二进制位,信号就是这样从头传到尾的。
这24个二进制位就是颜色的RGB值,不对,是GRB值,与电脑上常见的红绿蓝顺序有点不一样,所以可以在源代码里看到这样的写法:
(g<<16) + (r<<8) + b
说明书中提到的主要特点:
- IC控制电路与LED点光源共用一个电源。
- 控制电路与RGB芯片集成在一个5050封装的元器件中,构成一个完整的外控像素点。
- 内置信号整形电路,任何一个像素点收到信号后经过波形整形再输出,保证线路波形畸变不会累加。
- 内置上电复位和掉电复位电路。
- 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。
- 端口扫描频率2KHz/s。
- 串行级联接口,能通过一根信号线完成数据的接收与解码。
- 任意两点传输距离在不超过5米时无需增加任何电路。
- 当刷新速率30帧/秒时,级联数不小于1024点。
- 数据发送速度可达800Kbps。
- 光的颜色高度一致,性价比高。
后记:
WS2812B里的PIO汇编代码,后来也读懂了,详细的解释说明在这里。
推荐阅读:
树莓派Pico开发系列文章