Assembly 在PyBoard上使用MicroPython内联汇编程序对MHz协议进行Bitbang

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

我正在尝试使用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纳秒的微风中切换给定的引脚。这很好,但我不需要无意识地切换管脚,我需要按照一个精确的协议根据比特流切换管脚。因此,再过几个小时,我完成了以下实现:

@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)]))