Search code examples
interruptuartmicropythonrp2040

Nested UART pin change interrupts


I'm trying to implement a UART interrupt in MicroPython on an RP2040-based custom board loaded with Adafruit Feather MicroPython 1.2. While I'm aware this feature is typically available for WiPy devices, it's essential for my custom board.

I can't make hardware changes so I've initialized the UART on pins 4 (TXD) and 5 (RXD) and assigned a pin-change interrupt on RXD. My code:

import _thread
from machine import Pin, UART, I2C, mem32, Timer
from micropython import schedule
import micropython

# ... Other code ...

Serial = UART(1, baudrate=115200, tx=txPin, rx=rxPin)
rxTrig = False

def onSerial(p):
    global rxTrig
    if Serial.any():
        rxTrig = True

# ... Other code ...

def main():
    global rxTrig

    rxPin.irq(handler=onSerial, trigger=Pin.IRQ_RISING, hard=True)

onSerial is called for every rising edge on RXD and if data is available it sets rxTrig to True, which is used in a continuous while loop running on Core 1. This works seamlessly.

I implemented two nested timers which got stuck due to the infinite loop on Core 1. I suspect the timers are executing on Core 1, causing this. So I moved away from using variable rxTrig and the while loop, to use another pin-change interrupt by modifying the onSerial(p) code:

cmdPin = Pin(0, Pin.OUT)

# ... Other code ...

def onSerial(p):
    global rxTrig
    if Serial.any():
        cmdPin.on()

# ... Other code ...

def onCommand():
    # Consume the command and execute the actions
    cmdPin.off()

def main():
    cmdPin.irq(handler=onCommand, trigger=Pin.IRQ_RISING, hard=True)

The cmdPin changed its output as anticipated, but the interrupt didn't fire. How to properly implement this?


Solution

  • Somehow cmdPin = Pin(0, Pin.OUT) was not executing correctly. Once I restarted Thonny it started working and now I have UART with interrupt successfully on the custom RP2040 board.

    mdPin = Pin(0, Pin.OUT)
    
    # ... Other code ...
    
    def onSerial(p):
        global rxTrig
        if Serial.any():
            cmdPin.on()
    
    # ... Other code ...
    
    def onCommand():
        # Consume the command and execute the actions
        cmdPin.off()
    
    def main():
        cmdPin.irq(handler=onCommand, trigger=Pin.IRQ_RISING, hard=True)
    

    This is the correct code.