10. 内联汇编

此章节中,您将学习如何在MicroPython中写入内联汇编。

Note: 此章节是为已对微控制器和汇编语言有所了解的用户的进阶级教程。

MicroPython可以内联汇编,其允许您将汇编程序作为Python函数写入,其命名方式类似于常规Python函数的命名。

10.1. 返回一个值

内联汇编函数由一个特殊的函数装饰器表示。我们从最简单的示例开始:

@micropython.asm_thumb
def fun():
    movw(r0, 42)

您可在脚本或REPL中键入此。此函数无需任何参数,并返回数字42。 r0 为一个寄存器,且函数返回的此寄存器中的值即为返回值。 MicroPython通常将 r0 解译为一个整数,并为调用方将其转换为一个整数对象。

若您运行 print(fun()) ,您将看到其打印出42。

10.2. 访问外设

对于略微复杂的内容,我们先打开一个LED:

@micropython.asm_thumb
def led_on():
    movwt(r0, stm.GPIOA)
    movw(r1, 1 << 13)
    strh(r1, [r0, stm.GPIO_BSRRL])

这一代码使用了一些新概念:

  • stm 是一个为轻松访问pyboard的微控制器的寄存器提供了一系列常量的模块。 尝试运行``import stm`` ,然后在REPL中 运行 help(stm) 。其功能为提供所有可用常量的列表。

  • stm.GPIOA 是GPIOA外设的地址。在pyboard中,红色LED在端口A、引脚PA13。

  • movwt 将一个32位的数字移入寄存器。 这是一个可轻松转换为2个thumb指令的函数:先是 movw , 然后是 movt. movt 也会将当前值右移16位。

  • strh 储存了一个半字(16位)。 上述指令将 r1 较低的16位存入内存中的 r0 + stm.GPIO_BSRRL 位置。其作用为将所有端口A上的引脚设置为高, 即 r0 中的相应位所设置的。在上述示例中, r0 中的第13位已设置,因此PA13被拉高。这就点亮了红色LED。

10.3. 接受参数

内联汇编函数可接受多达4个参数。若使用这4个参数,则须将之命名为 r0, r1, r2r3 ,以反映寄存器和调用惯例。

这是一个添加参数的函数:

@micropython.asm_thumb
def asm_add(r0, r1):
    add(r0, r0, r1)

此即执行计算 r0 = r0 + r1。由于结果 写作 r0,此即返回的结果。尝试 asm_add(1, 2),其应返回3。

10.4. 循环

我们可用 label(my_label) 为标签赋值,并使用 b(my_label) 或 类似于 bgt(my_label) 的条件分支将其分化为支。

以下示例使绿色LED闪烁 r0 次。

@micropython.asm_thumb
def flash_led(r0):
    # get the GPIOA address in r1 获取r1中的GPIOA地址
    movwt(r1, stm.GPIOA)

    # get the bit mask for PA14 (the pin LED #2 is on) 获取PA14的位掩码(LED #2所在的引脚)
    movw(r2, 1 << 14)

    b(loop_entry)

    label(loop1)

    # turn LED on 开启LED
    strh(r2, [r1, stm.GPIO_BSRRL])

    # delay for a bit
    movwt(r4, 5599900)
    label(delay_on)
    sub(r4, r4, 1)
    cmp(r4, 0)
    bgt(delay_on)

    # turn LED off
    strh(r2, [r1, stm.GPIO_BSRRH])

    # delay for a bit 延迟1位
    movwt(r4, 5599900)
    label(delay_off)
    sub(r4, r4, 1)
    cmp(r4, 0)
    bgt(delay_off)

    # loop r0 times
    sub(r0, r0, 1)
    label(loop_entry)
    cmp(r0, 0)
    bgt(loop1)

10.5. 扩展阅读

更多关于内联汇编支持的指令的信息,请参考 reference documentation.