我正在尝试使用 PyBoard v1.1使用 Micropython 的内联汇编器来驱动 Adafruit 的 NeoPixel LED SK6812RGBW的协议。
协议
从链接的数据表中可以看出,单个 LED 是通过组合 4 个 8 位 rgbw 值来驱动的。每个高位由 0.6us 的模拟高,然后是 0.6us 的数字低组成,低位的比例为 0.3us 高和 0.9us 低。这使得在 4 字节 LED 颜色值中使用的每个数据位都是 4 个模拟位,每个 0.3 us,或者总共 128 个位超过 38.4 us。发送到第一个 LED 的字节流也包含所有后续 LED 的值,它会将除自己之外的所有 LED 传递到下一个 LED,依此类推。
执行
使用 pyboard 的 SPI 接口可以很容易地实现该协议。一旦生成了数据流(每个 LED 有效 16 个字节)并计算了波特率(1s / 0.3 us = ca 3333333),只需创建一个 pyb.SPI 实例并使用字节作为参数调用其发送方法.
任务
现在到手头的任务:我想用一个 PyBoard 驱动三个不同的 LED 灯条。但是只有 2 条 SPI 总线可用。因此,在尝试使用 pyb.Pin 和循环对协议进行 bitbang 后,我很快意识到这不起作用,最小切换速度为 54 us,这比我需要的 0.3 us 略低...
实施 V2
在尝试了一些优化步骤后,我转向了 Micropython 的内联汇编器。几个小时后,我设法在用示波器测量的 23 ns 轻松切换给定引脚。这很好,但我不需要盲目地切换引脚,我需要根据比特流遵循精确的协议来切换引脚。又过了几个小时,我完成了以下实现:
@micropython.asm_thumb
def send_bits_on_x9(r0):
# r0 0th word contains the data array address
# r0 1st word contains length of data array
# Store the GPIOB address in r3
movwt(r3, stm.GPIOB)
# Store the bit mask for PB6 (the pin X9)
movw(r4, 1 << 6)
# Load address into r5
ldr(r5, [r0, 0])
# Load array length into r6
ldr(r6, [r0, 4])
# Jump to condition evaluation
b(loop_entry)
# Main loop
label(loop)
# Get current "bit" word
ldr(r0, [r5, 0])
# Shift address to next "bit" for next time
add(r5, r5, 4)
# Evaluating the bit and toggling accordingly
cmp(r0, 1)
ite(eq)
strh(r4, [r3, stm.GPIO_BSRRL]) # Turn LED on
strh(r4, [r3, stm.GPIO_BSRRH]) # Turn LED off
# Delay for a bit
movwt(r7, 8) # 20948000 cycles is about 1s
label(delay)
sub(r7, r7, 1)
cmp(r7, 0)
bgt(delay)
# Eval loop; using data array length as initial counter value
sub(r6, r6, 1)
label(loop_entry)
cmp(r6, 0)
bgt(loop)
B6 是引脚 X9 的 CPU 名称,我将其用作与 LED 的数据连接。
为了运行,我将它嵌入到一个演示 python 脚本中:
import array
import uctypes
import micropython
import stm
@micropython.asm_thumb
def send_bits_on_x9(r0):
...
send_buffer = array.array("i", [1, 0, 1, 1, 0, 0, 1, 0])
send_bits_on_x9(array.array("i", [uctypes.addressof(send_buffer), len(send_buffer)]))
问题
这工作得很好,但是当使用它代替 SPI 流媒体时,每隔几次执行就可以看到 LED 偶尔出现的伪影。以下是我用示波器查看时的图像: 带有工件的示波器日志 可以看出,有一个地方由于某种原因它似乎停止切换恰好 2 个值位:缺少铅笔的侧面 这似乎发生随机地,在比特流的任何部分,有时从上升的边沿开始,有时从下降的边沿开始。
问题
现在显然我的问题是为什么会发生这种情况。SPI 不会发生这种情况,尽管我假设 C 实现注意不要让任何东西中断流。我尝试在调用 send_bits_on_x9 之前禁用垃圾收集器并在之后重新启用,但这没有帮助。我还改变了延迟周期的数量,也没有改变任何东西。
我注意到的第二件事是,当有多个尾随零字节时(根据协议定义的 80 us 重置周期),该周期似乎将在大约四分之一的时间内执行。将尾随字节更改为 0xff 时,它们保留了预期的持续时间,LED 似乎并不介意。
现在,如果有人可以向我指出官方内联汇编程序文档以外的资源,甚至提供一些见解,我将不胜感激。干杯!