树莓派48/100 - 深入研究WS2812彩灯PIO汇编程序
2021/11/11 14:10:36
本文主要是介绍树莓派48/100 - 深入研究WS2812彩灯PIO汇编程序,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
关于树莓派Pico里的PIO(Programmed I/O)编程,前面写过4篇文章:
-
初识Pico里的PIO,更高级的办法让小灯闪烁
-
深入研究PIO中的指令周期
-
看懂PWM里的汇编代码
-
用PIO的out指令控制七段数码管
以前的WS2812彩灯程序感觉像天书,根本看不懂,现在可以揭开它神秘的面纱了。
import machine import utime import array 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() # 建立状态机,设置输出针的编号 sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=machine.Pin(15)) sm.active(1) NUM_LEDS = 30 ar = array.array("I", [0 for _ in range(NUM_LEDS)]) while True: for j in range(NUM_LEDS): (r, g, b) = (0, 0, 0) if(j%3 == 0): r = 11 if(j%3 == 1): g = 11 if(j%3 == 2): b = 11 ar[j] = g << 16 | r << 8 | b sm.put(ar, 8) utime.sleep_ms(20)
为了研究pioasm程序的汇编代码,我简化了主程序,只让30个灯依次显示红、绿、蓝三色。
先要了解WS2812特殊的信号机制,与平常的高电位为1、低电位为0的方式不一样,它的协议按高、低电位持续时间的长短来表示0和1,见下图:
如果高电平持续时间小于低电平持续时间,表示0;低电平持续时间长,表示1。持续时间也有严格的范围,以微秒来计。我查到的WS2812B中文手册与国外论坛里看到了指标参数有点差别。
主程序里pioasm状态机的频率为8M赫兹,这样一个汇编指令周期为1/8000000=0.125 us(微秒),看代码里的T1、T2和T3分别为2、5、3,也就对应着0.25 us、0.625 us和0.375 us。
这三个时间的含义如下图:
T0L=T2+T3=1.0us,在指标范围(0.58us ~ 1.6us)内,T1H=T1+T2=0.875,也在指标范围(0.58us ~ 1.6us)内。在T1时间段里永远是高电位,在T3段永远是低电位,T2时间内的电位对应着表示的数据:0或1。
现在可以看汇编代码了,out_shiftdir=rp2.PIO.SHIFT_LEFT 表示OSR里的数据向左移位,autopull=True, pull_thresh=24 表示自动从OSR里取数,而且只取24位(对应着RGB值)。
out(x, 1) 表示从OSR里移出1位数据,放入X寄存器里,后面的.side(0)设置低电位,[T3-1]补满T3指令周期。
jmp(not_x, “do_zero”)的意思是当X为0时跳到 do_zero 位置的代码,当X不为0时,继续执行下面的语句。
x不为0时,执行到 jmp(“bitloop”) 这一句,准备跳转到程序的最开始,跳转前捎带着完成 side(1),因为x不为0,所以side(1),逻辑正确,总延迟时间为T2。
x为0的时候,执行 nop().side(0) 指令,总延迟时间也是T2。
主程序用 array.array(“I”, list) 初始化一个数组,现代计算机的 int 类型一般都是4字节(32位)的整数,用来设置灯组的RGB值(24位)。
sm.put(ar, 8) 用于快速把数组中的所有数据送到状态机的FIFO队列里,第二个参数8表示数据先左移8位,再送到FIFO里。一组彩灯刷新周期里,24位的GRB值(注意顺序是:绿、红、蓝)要一个紧接着一个快速发出去,如果稍有延迟,会被认为是下一组彩灯刷新周期。
我看了一下WS2812B的规格参数,T1=T2=T3=0.375时,也符合各项指标要求,应该也是可以工作的,所以我把程序简化了一下,状态机仍工作在8M Hz频率下,运行完全没问题:
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) def ws2812(): label("bitloop") out(x, 1) .side(0) [2] jmp(not_x, "do_zero") .side(1) [2] jmp("bitloop") .side(1) [2] label("do_zero") nop() .side(0) [2]
如果让状态机工作在2.7M频率下,那么延迟参数也可以省了:
@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) def ws2812(): label("bitloop") out(x, 1) .side(0) jmp(not_x, "do_zero") .side(1) jmp("bitloop") .side(1) label("do_zero") nop() .side(0) sm = rp2.StateMachine(0, ws2812, freq=2_700_000, sideset_base=machine.Pin(15)) sm.active(1)
修改的代码在我的WS2812B工作正常,但不知道在旧款的2812上能否正常工作。
这篇关于树莓派48/100 - 深入研究WS2812彩灯PIO汇编程序的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-28MQ底层原理资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:入门与初级用户指南
- 2024-11-28MQ消息队列资料入门教程
- 2024-11-28MQ消息队列资料:新手入门详解
- 2024-11-28MQ消息中间件资料详解与应用教程
- 2024-11-28MQ消息中间件资料入门教程
- 2024-11-28MQ源码资料详解与入门教程
- 2024-11-28MQ源码资料入门教程
- 2024-11-28RocketMQ底层原理资料详解