Assembly 在PyBoard上使用MicroPython内联汇编程序对MHz协议进行Bitbang
我正在尝试使用PyBoard v1.1来驱动Adafruit的NeoPixel LED协议,使用Micropython的内联汇编程序 协议 如链接数据表中所示,单个LED通过组合4个8位rgbw值来驱动。每个高位由0.6 us模拟高位和0.6 us数字低位组成,低位的比率为0.3 us高至0.9 us低。这使得4字节LED颜色值输出中使用的每个数据位都是4个模拟位,每个0.3 us,或38.4 us上的总128位。发送到第一个LED的字节流也包含所有后续LED的值,它将自己的值传递到下一个LED,以此类推 实施 使用pyboard的SPI接口可以很容易地实现该协议。生成数据流(每个LED有效16个字节)并计算波特率(1s/0.3 us=ca 3333333)后,只需创建一个pyb.SPI实例并使用字节作为参数调用其发送方法 任务 现在开始手头的任务:我想用一块PyBoard驱动三个不同的LED条。然而,只有2条SPI总线可用。 因此,在尝试使用pyb.Pin和loops对协议进行bitbang操作后,我很快意识到这是行不通的,最小切换速度是54 us,比我需要的0.3 us稍差一点 实现V2 在尝试了一些优化步骤之后,我转向了Micropython的内联汇编程序。几个小时后,我用示波器测得,在23纳秒的微风中切换给定的引脚。这很好,但我不需要无意识地切换管脚,我需要按照一个精确的协议根据比特流切换管脚。因此,再过几个小时,我完成了以下实现:Assembly 在PyBoard上使用MicroPython内联汇编程序对MHz协议进行Bitbang,assembly,micropython,adafruit,neopixel,Assembly,Micropython,Adafruit,Neopixel,我正在尝试使用PyBoard v1.1来驱动Adafruit的NeoPixel LED协议,使用Micropython的内联汇编程序 协议 如链接数据表中所示,单个LED通过组合4个8位rgbw值来驱动。每个高位由0.6 us模拟高位和0.6 us数字低位组成,低位的比率为0.3 us高至0.9 us低。这使得4字节LED颜色值输出中使用的每个数据位都是4个模拟位,每个0.3 us,或38.4 us上的总128位。发送到第一个LED的字节流也包含所有后续LED的值,它将自己的值传递到下一个LED
@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)
问题
然而,当用它代替SPI拖缆时,它工作得很好,通过观察LED,每隔几次执行都会看到偶尔的瑕疵。以下是我用示波器查看时的图像:
可以看出,由于某种原因,它似乎停止切换正好2个值位:
这似乎是随机发生的,在比特流的任何部分,有时从上升的侧面开始,有时从下降的侧面开始
问题:
很明显,我的问题是为什么会发生这种情况。SPI不会发生这种情况,尽管我假设C实现会注意不让任何东西中断流。
在调用\u x9上的send\u bits\u并在之后重新启用之前,我尝试禁用垃圾回收器,但没有帮助。我还改变了延迟周期的数量,这也没有改变任何东西
我注意到的第二件事是,当有许多尾随的零字节(根据协议定义的80 us重置周期)时,该周期的执行时间似乎是预期的四分之一。将尾随字节更改为0xff时,它们保留了预期的持续时间,并且LED似乎不介意
现在,如果有人能给我提供官方内联汇编程序文档以外的资源,甚至提供一些见解,我将不胜感激。干杯 不幸的是,这些部件对时间非常敏感。位碰撞是可能的,但非常敏感,可能性很低,如果使用汇编语言,python根本不应该成为这个对话的一部分。最好的方法是使用像spi这样的片上硬件,您应该根据硬件能够生成无限的spi流,该流由硬件精确计时,这是不可能的吗?这些工作的方式是你应该能够驱动它们的长链,这不起作用,你需要三个吗?内联汇编程序将是你正在使用的工具的一部分,它是特定于语言/工具的。该工具是开源的吗?您可以查看该工具的源代码,了解这个(非常有趣的)内联asm是如何工作的。如果可能的话,也可以编写真实的程序集(因为这显然不是在虚拟机上运行的),不要期望延迟循环是一致的,它可能会随着向项目添加/删除代码而改变。但是你有一个范围,所以这是好的,如果没有这些部分的范围,你就没有机会这样做。你读过吗?不幸的是,这些部分对时间非常敏感。位碰撞是可能的,但非常敏感,可能性很低,如果使用汇编语言,python根本不应该成为这个对话的一部分。最好的方法是使用像spi这样的片上硬件,您应该根据硬件能够生成无限的spi流,该流由硬件精确计时,这是不可能的吗?这些工作的方式是你应该能够驱动它们的长链,这不起作用,你需要三个吗?内联汇编程序将是你正在使用的工具的一部分,它是特定于语言/工具的。该工具是开源的吗?您可以查看该工具的源代码,了解这个(非常有趣的)内联asm是如何工作的。如果可能的话,也可以编写真实的程序集(因为这显然不是在虚拟机上运行的),不要期望延迟循环是一致的,它可能会随着向项目添加/删除代码而改变。但是你有一个范围,所以这是好的,如果没有这些部分的范围,你就没有机会这样做。你读过吗?
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)]))