创客孙老师
创客孙老师
发布于 2024-05-21 / 19 阅读
0
0

使用MicroPython和pyboard开发板(12):内联汇编

内联汇编

是不是有点意外,MicroPython也支持内联汇编,就是在Python代码中可以直接嵌入汇编代码。这在很多语言中都是支持的,比如C、C++、Pascal、Delphi等等,主要的目的是为了性能,在一些函数或内存操作的时候,可以有效的减少指令和指令周期,从而提高性能。但是,获得性能的代价就是牺牲部分可移植性。在一些编译语言中,内嵌的汇编代码使用的就是汇编指令,而在Python或Java这种虚拟机环境中,实际上是经过了封装后的指令。

MicroPython包含一个内联汇编器,支持汇编代码编写的函数,并且可以像调用普通python函数一样去调用它们。下面是具体的一些用法。
inline_assembler.png

返回一个值

MicroPython的内联汇编函数需要一个特殊的函数装饰器micropython.asm_thumb来装饰:

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

要快速尝试一下,可以在脚本或REPL中输入上面的内容。该函数不带参数,返回一个数字42。r0是一个寄存器,函数返回时该寄存器中的值就是返回的值。 MicroPython将r0解释为整数,并将其转换为调用者的整数对象。

运行 print(fun()) 会看到打印出42。

访问外围设备

下面是一个稍复杂些的示例,打开 LED:

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

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

  • stm是一个模块,它提供一组常量,可以访问pyboard的寄存器。可以在REPL中尝试执行import stm,然后执行​​help(stm),会打印出所有可用常量的列表。
  • stm.GPIOA是GPIOA外设在内存中的地址。在pyboard上,红色LED位于端口A、引脚PA13上。
  • movwt将32位数字移入寄存器。这是一个方便的函数,包含2个thumb指令:movwmovtmovt将立即值右移16位。
  • strh存储半个字(16位)。上面的指令将r1的低16位存储到内存位置r0 + stm.GPIO_BSRRL中,这会将端口A上r0中相应位设置的所有引脚设置为高电平的效果。在上面的示例中,r0 中的第13位被设置,因此PA13被拉高。这将打开红色 LED。

接受参数

内联汇编函数最多可接受4个参数。这四个参数的名称分别为r0r1r2r3,这是调用的约定,必须要用这四个名称。

下面是一个具有参数的函数:

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

这个例子实现了r0 = r0 + r1的计算。结果放入r0,也就是返回值是r0。尝试调用下asm_add(1,2),返回值应该是3。

循环

可以使用label(my_label)为标签赋值,并使用b(my_label)bgt(my_label)条件分支为标签赋值。

下面的示例让绿色LED灯闪烁r0次。

@micropython.asm_thumb
def flash_led(r0):
    # 将GPIOA的地址放到r1
    movwt(r1, stm.GPIOA)

    # 获取 PA14 的位掩码(引脚 LED #2 亮起)
    movw(r2, 1 << 14)

    b(loop_entry)

    label(loop1)

    # 打开LED
    strh(r2, [r1, stm.GPIO_BSRRL])

    # 延迟一下下
    movwt(r4, 5599900)
    label(delay_on)
    sub(r4, r4, 1)
    cmp(r4, 0)
    bgt(delay_on)

    # 关闭LED
    strh(r2, [r1, stm.GPIO_BSRRH])

    # 再延迟一下下
    movwt(r4, 5599900)
    label(delay_off)
    sub(r4, r4, 1)
    cmp(r4, 0)
    bgt(delay_off)

    # 循环r0次
    sub(r0, r0, 1)
    label(loop_entry)
    cmp(r0, 0)
    bgt(loop1)

评论