【雕爷学编程】MicroPython手册之 RP2040 特定端口库 rp2.StateMachine.irq()

在这里插入图片描述

MicroPython是为了在嵌入式系统中运行Python 3编程语言而设计的轻量级版本解释器。与常规Python相比,MicroPython解释器体积小(仅100KB左右),通过编译成二进制Executable文件运行,执行效率较高。它使用了轻量级的垃圾回收机制并移除了大部分Python标准库,以适应资源限制的微控制器。

MicroPython主要特点包括:
1、语法和功能与标准Python兼容,易学易用。支持Python大多数核心语法。
2、对硬件直接访问和控制,像Arduino一样控制GPIO、I2C、SPI等。
3、强大的模块系统,提供文件系统、网络、图形界面等功能。
4、支持交叉编译生成高效的原生代码,速度比解释器快10-100倍。
5、代码量少,内存占用小,适合运行在MCU和内存小的开发板上。
6、开源许可,免费使用。Shell交互环境为开发测试提供便利。
7、内置I/O驱动支持大量微控制器平台,如ESP8266、ESP32、STM32、micro:bit、掌控板和PyBoard等。有活跃的社区。

MicroPython的应用场景包括:
1、为嵌入式产品快速构建原型和用户交互。
2、制作一些小型的可 programmable 硬件项目。
3、作为教育工具,帮助初学者学习Python和物联网编程。
4、构建智能设备固件,实现高级控制和云连接。
5、各种微控制器应用如物联网、嵌入式智能、机器人等。

使用MicroPython需要注意:
1、内存和Flash空间有限。
2、解释执行效率不如C语言。
3、部分库函数与标准版有差异。
4、针对平台优化语法,订正与标准Python的差异。
5、合理使用内存资源,避免频繁分配大内存块。
6、利用原生代码提升速度关键部位的性能。
7、适当使用抽象来封装底层硬件操作。

总体来说,MicroPython让Python进入了微控制器领域,是一项重要的创新,既降低了编程门槛,又提供了良好的硬件控制能力。非常适合各类物联网和智能硬件的开发。
在这里插入图片描述

RP2040 是一款由树莓派公司设计的 32 位双核 ARM Cortex-M0+ 微控制器芯片,于 2021 年 1 月发布,作为树莓派 Pico 开发板的核心部件。RP2040 的特点是高性能、低成本、小封装、灵活的 I/O 和独特的可编程 I/O(PIO)子系统。RP2040 还支持 MicroPython、C/C++ 和 TensorFlow Lite 等编程语言和框架,适用于各种创意项目和机器学习应用。

MicroPython 的特定于 RP2040 的库包括以下模块:

rp2:包含特定于 RP2040 的函数和类,如 PIO 相关功能、Flash 类和 StateMachine 类。
machine:包含通用的硬件控制功能,如 Pin、Timer、UART、PWM、ADC 等。
utime:包含时间相关的函数,如 sleep、ticks_ms、ticks_diff 等。
uos:包含操作系统相关的函数,如 uname、mount、remove 等。

这些模块使得 MicroPython 可以充分利用 RP2040 的硬件特性,实现各种创意项目。

在这里插入图片描述
MicroPython 的 rp2.StateMachine.irq() 是一个用于返回或配置状态机的中断请求 (IRQ) 对象的方法。状态机是 RP2040 芯片的一种特殊功能,它可以实现自定义的 I/O 行为,例如模拟 SPI、I2C、UART 等协议,或者实现位摇摆 (bit banging) 技术。状态机有两个实例,每个实例有四个编号为 0 到 3 的状态机,共计八个状态机。

rp2.StateMachine.irq() 的主要特点是:

它可以在任何时候调用,无论状态机是否已经初始化或启动。如果状态机未初始化,则该方法无效。
它可以返回一个 IRQ 对象,该对象可以用于注册或注销中断处理函数,以及使能或禁止中断。
它可以接受一个可选的 handler 参数,表示要注册的中断处理函数。如果省略该参数,则返回当前已注册的中断处理函数,如果没有则返回 None。

它可以接受一个可选的 trigger 参数,表示要触发中断的条件。该参数可以是以下值的按位或:

0:表示不触发中断。
1:表示当 RX FIFO 非空时触发中断。
2:表示当 TX FIFO 非满时触发中断。
4:表示当 SM0 请求时触发中断。
8:表示当 SM1 请求时触发中断。
16:表示当 SM2 请求时触发中断。
32:表示当 SM3 请求时触发中断。
它可以接受一个可选的 hard 参数,表示是否使用硬件中断。如果为 True,则使用硬件中断;如果为 False,则使用软件中断。默认为 False。

rp2.StateMachine.irq() 的应用场景是:

当需要在程序中响应状态机的某些事件时,例如在使用状态机模拟 UART、SPI、I2C 等协议时,根据 RX FIFO 或 TX FIFO 的状态来执行相应的操作。
当需要在程序中与其他设备或状态机通信时,例如在使用状态机模拟多主或多从的通信协议时,根据 SMx 请求信号来进行协调或同步。
当需要在程序中优化状态机的性能或效率时,例如在使用状态机模拟高速或高频的 I/O 行为时,根据硬件或软件中断的选择来平衡响应速度和资源占用。

rp2.StateMachine.irq() 的注意事项是:

使用 rp2.StateMachine.irq() 时,需要注意 IRQ 对象的生命周期和作用域。如果 IRQ 对象被垃圾回收或超出作用域,则相应的中断处理函数也会失效。
使用 rp2.StateMachine.irq() 时,需要注意中断处理函数的编写和执行。如果中断处理函数过长或过于复杂,则可能会影响程序的正常运行或导致死锁;如果中断处理函数出现错误或异常,则可能会导致程序崩溃或不可预知的行为。
使用 rp2.StateMachine.irq() 时,需要注意中断的使能和禁止。如果不及时禁止不需要的中断,则可能会造成不必要的开销或干扰;如果不及时使能需要的中断,则可能会错过重要的事件或信号。

以下是 MicroPython 的 rp2.StateMachine.irq() 的几个实际运用程序参考代码案例:

案例一:使用状态机 0 实现一个简单的 UART (通用异步收发器) 接收器,并在每次接收到一个字节时打印出来,并使用软件中断来响应 RX FIFO 非空事件。

from machine import Pin
import rp2

# 定义一个 UART 接收程序
@rp2.asm_pio(set_init=rp2.PIO.IN_LOW)
def uart_rx_prog():
    # 设置初始波特率为 9600
    set(x, 103) [31]
    # 设置初始数据位为 8
    set(y, 7) [31]
    label("uartloop")
    # 等待起始位(低电平)
    wait(0, pin, 0)
    # 延迟半个周期
    jmp(x_dec, "bitloop")
    label("bitloop")
    # 从输入引脚读取一位数据到输入移位寄存器
    in_(pins, 1)
    # 等待一个周期
    mov(x, isr)
    label("waitbit")
    jmp(x_dec, "waitbit")
    # 判断是否读完所有数据位
    jmp(y_dec, "bitloop")
    # 将输入移位寄存器的内容推送到 RX FIFO
    push()
    # 跳转到循环开始处
    jmp("uartloop")

# 创建状态机 0,从 Pin(16) 接收 UART 数据
sm = rp2.StateMachine(0, uart_rx_prog, freq=1000000, in_base=Pin(16))

# 定义一个中断处理函数,用于打印接收到的字节数据
def on_rx():
    # 从 RX FIFO 中读取一个字节数据
    data = sm.get()
    # 打印接收到的字节数据
    print("UART received:", data)

# 获取状态机 0 的 IRQ 对象,并注册中断处理函数,触发条件为 RX FIFO 非空,使用软件中断
irq = sm.irq(handler=on_rx, trigger=1)

# 启动状态机 0
sm.active(1)

案例二:使用状态机 1 实现一个简单的 ADC (模数转换器) 功能,并在每次读取到一个 10 位的模拟值时将其转换为电压值,并使用硬件中断来响应 RX FIFO 非空事件。

from machine import Pin
import rp2

# 定义一个 DAC 程序
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def dac_prog():
    # 设置初始输出周期为 1000
    set(x, 999) [31]
    # 设置初始数据位为 10
    set(y, 9) [31]
    label("dacloop")
    # 判断是否有数据要输出(从 TX FIFO 中读取数据)
    pull(block)
    mov(isr, osr)
    # 输出一位数据到输出引脚
    out(pins, 1)
    # 判断是否输出完所有数据位
    jmp(y_dec, "dacloop")
    # 跳转到循环开始处
    jmp("dacloop")

# 创建状态机 1,输出 DAC 数据到 Pin(25)
sm = rp2.StateMachine(1, dac_prog, freq=1000000, out_base=Pin(25))

# 定义一个中断处理函数,用于将接收到的模拟值转换为电压值(假设参考电压为 3.3V)
def on_rx():
    # 从 RX FIFO 中读取一个 10 位的模拟值,并右移 6 位,将其转换为 4 位的数字值
    value = sm.get(shift=6)
    # 将数字值映射到电压范围(0-3.3V)
    voltage = value * 3.3 / 15
    # 打印模拟值和电压值
    print("ADC value:", value, "voltage:", voltage)

# 获取状态机 1 的 IRQ 对象,并注册中断处理函数,触发条件为 RX FIFO 非空,使用硬件中断
irq = sm.irq(handler=on_rx, trigger=1, hard=True)

# 启动状态机 1
sm.active(1)

案例三:使用状态机 2 和状态机 3 实现一个简单的 SPI (串行外设接口) 主从模式,并在每次发送或接收一个字节时打印出来,并使用软件中断来响应 SMx 请求信号。

from machine import Pin
import rp2

# 定义一个 SPI 主机程序
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW, sideset_init=(rp2.PIO.OUT_LOW,))
def spi_master_prog():
    # 设置初始时钟分频为 250(4 MHz)
    set(x, 249) [31]
    # 设置初始数据位为 8
    set(y, 7) [31]
    label("spiloop")
    # 拉低片选引脚(side set)
    set(pindirs, 1) .side(0)
    # 判断是否有数据要发送(从 TX FIFO 中读取数据)
    pull(block)
    mov(isr, osr)
    label("outbit")
    # 输出一位数据到输出引脚(side set)
    out(x, 1) .side(1)
    # 等待半个周期
    nop() [31]
    # 输入一位数据到输入移位寄存器
    in_(pins, 1)
    # 输出另一位数据到输出引脚(side set)
    out(x, 1) .side(0)
    # 等待半个周期
    nop()
    # 判断是否输出完所有数据位
    jmp(y_dec, "outbit")
    # 拉高片选引脚(side set)
    set(pindirs, 1) .side(1)
    # 将输入移位寄存器的内容推送到 RX FIFO
    push()
    # 跳转到循环开始处
    jmp("spiloop")

# 定义一个 SPI 从机程序
@rp2.asm_pio(set_init=rp2.PIO.IN_LOW, sideset_init=(rp2.PIO.OUT_LOW,))
def spi_slave_prog():
    # 设置初始时钟分频为 250(4 MHz)
    set(x, 249) [31]
    # 设置初始数据位为 8
    set(y, 7) [31]
    label("spiloop")
    # 等待片选引脚为低电平
    wait(0, pin, 0)
    label("inbit")
    # 输入一位数据到输入移位寄存器
    in_(pins, 1)

案例四:配置状态机中断触发时的回调函数:

import rp2

# 创建 PIO 对象
pio = rp2.PIO(0)

# 设置 PIO 引脚 0 和 1
pio.set_pins(0, 1 << 0)
pio.set_pins(1, 1 << 1)

# 创建 PIO 状态机
sm = rp2.StateMachine(0, pio, freq=1000000, in_base=pio.base)

# 中断触发时的回调函数
def irq_callback(sm):
    print("中断触发!")

# 配置状态机中断
sm.irq(irq_callback)

# 启动状态机
sm.active(1)

# 等待一段时间

这个示例创建了一个 PIO 对象,并使用 pio.set_pins() 方法设置 PIO 引脚 0 和 1。然后,使用 rp2.StateMachine() 创建了一个 PIO 状态机对象 sm。接下来,定义了一个名为 irq_callback 的回调函数,它在中断触发时被调用,并打印出一条消息。通过调用 sm.irq(irq_callback) 方法,将中断触发时的回调函数配置到状态机中断中。最后,通过调用 sm.active(1) 启动状态机。

案例五:配置状态机中断触发时的优先级:

import rp2

# 创建 PIO 对象
pio = rp2.PIO(0)

# 设置 PIO 引脚 0 和 1
pio.set_pins(0, 1 << 0)
pio.set_pins(1, 1 << 1)

# 创建 PIO 状态机
sm = rp2.StateMachine(0, pio, freq=1000000, in_base=pio.base)

# 中断触发时的回调函数
def irq_callback(sm):
    print("中断触发!")

# 配置状态机中断
sm.irq(irq_callback, priority=1)

# 启动状态机
sm.active(1)

# 等待一段时间

这个示例与前一个示例类似,不同之处在于通过在 sm.irq() 方法中传递 priority 参数,可以配置状态机中断触发时的优先级。较高的优先级值表示较高的优先级。默认情况下,优先级为 0。在这个示例中,将优先级设置为 1。

案例六:等待状态机中断触发的示例:

import rp2
import time

# 创建 PIO 对象
pio = rp2.PIO(0)

# 设置 PIO 引脚 0 和 1
pio.set_pins(0, 1 << 0)
pio.set_pins(1, 1 << 1)

# 创建 PIO 状态机
sm = rp2.StateMachine(0, pio, freq=1000000, in_base=pio.base)

# 中断触发时的回调函数
def irq_callback(sm):
    print("中断触发!")

# 配置状态机中断
sm.irq(irq_callback)

# 启动状态机
sm.active(1)

# 等待状态机中断触发
while True:
    time.sleep(1)

这个示例创建了一个 PIO 对象,并使用 pio.set_pins() 方法设置 PIO 引脚 0 和 1。然后,使用 rp2.StateMachine() 创建了一个 PIO 状态机对象 sm。接下来,定义了一个名为 irq_callback 的回调函数,它在中断触发时被调用,并打印出一条消息。通过调用 sm.irq(irq_callback) 方法,将中断触发时的回调函数配置到状态机中断中。最后,通过一个无限循环和 time.sleep(1) 等待状态机中断的触发。在实际应用中,可以根据需要设置适当的等待时间或条件来等待中断的触发。请注意,以上示例中的代码只是为了演示 rp2.StateMachine.irq() 的使用。实际应用中,您需要根据具体的需求和硬件配置进行适当的调整和扩展。

请注意,以上示例仅供参考,具体的使用方法可能因不同的硬件平台和MicroPython版本而有所差异。在实际编程中,你需要根据你所使用的硬件和具体需求进行适当的调整。

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值