MicroPython RP2040 PIO中断仅在状态机0触发及替代方案咨询
问题分析与解决方案
你的代码仅在状态机0下能触发中断的核心原因是:PIO汇编代码里固定用了irq(0),这会触发全局中断号0,而其他状态机绑定的中断处理函数,并没有接收到PIO触发的对应信号——本质是全局中断号与状态机的绑定关系没对应上。
下面提供两种可行的解决方案:
方案1:触发状态机自身关联的中断(推荐)
在PIO汇编里用irq(rel(0))替换irq(0),rel(0)代表触发当前状态机对应的相对中断线,不管你用0-3中的哪个状态机,都会触发该状态机绑定的中断处理函数,无需固定全局中断号。
修改后的完整代码:
from machine import Pin from time import sleep import rp2 class PioLoader(): @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) def blink(): wrap_target() set(pins, 1) [31] nop() [31] nop() [31] nop() [31] nop() [31] set(pins, 0) [31] nop() [31] nop() [31] nop() [31] nop() [31] irq(rel(0)) # 改为触发当前状态机的相对中断 wrap() def irqHandler(self, sm): # 修正:添加self参数,直接接收触发中断的状态机实例 sm.active(0) print(f"状态机{sm.id()}触发中断,已停止") def __init__(self): pass def test(self, sm_id=0): # 支持传入任意合法状态机ID(0-3) sm = rp2.StateMachine(sm_id, self.blink, freq=2000, set_base=Pin(25)) sm.irq(lambda sm: self.irqHandler(sm)) # 通过lambda传递self print(f"启动状态机{sm.id()}") sm.active(1) sleep(2) sm.active(0) # 测试状态机1 loader = PioLoader() loader.test(sm_id=1)
方案2:绑定全局中断号到指定状态机
如果需要使用全局中断号,可以在创建状态机时,指定监听的全局中断触发源,同时让PIO代码触发对应编号的全局中断。比如用状态机1触发全局中断1:
- PIO汇编中改为
irq(1) - 创建状态机时绑定对应中断:
sm = rp2.StateMachine(1, self.blink, freq=2000, set_base=Pin(25)) sm.irq(self.irqHandler, trigger=rp2.PIO.IRQ_SM1) # 指定监听状态机1的全局中断
这种方式适合多状态机触发不同全局中断的场景,但灵活性不如方案1。
额外修正:中断处理函数的问题
你原代码里的irqHandler有两个明显问题:
- 缺少
self参数,作为类方法无法访问实例上下文 - 使用
self.sm但sm是test方法的局部变量,会触发AttributeError
修改后的处理函数直接接收触发中断的状态机实例,避免了变量作用域错误。
内容的提问来源于stack exchange,提问作者user7117046
相关产品推荐
相关产品推荐

