内联汇编
是不是有点意外,MicroPython也支持内联汇编,就是在Python代码中可以直接嵌入汇编代码。这在很多语言中都是支持的,比如C、C++、Pascal、Delphi等等,主要的目的是为了性能,在一些函数或内存操作的时候,可以有效的减少指令和指令周期,从而提高性能。但是,获得性能的代价就是牺牲部分可移植性。在一些编译语言中,内嵌的汇编代码使用的就是汇编指令,而在Python或Java这种虚拟机环境中,实际上是经过了封装后的指令。
MicroPython包含一个内联汇编器,支持汇编代码编写的函数,并且可以像调用普通python函数一样去调用它们。下面是具体的一些用法。
返回一个值
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指令:movw
和movt
。movt
将立即值右移16位。strh
存储半个字(16位)。上面的指令将r1
的低16位存储到内存位置r0 + stm.GPIO_BSRRL
中,这会将端口A上r0
中相应位设置的所有引脚设置为高电平的效果。在上面的示例中,r0
中的第13位被设置,因此PA13被拉高。这将打开红色 LED。
接受参数
内联汇编函数最多可接受4个参数。这四个参数的名称分别为r0
、r1
、r2
和r3
,这是调用的约定,必须要用这四个名称。
下面是一个具有参数的函数:
@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)