rp2: Reading pin value from IRQ hangs forver
from machine import Pin, idle, disable_irq, enable_irq
from time import sleep_ms
pin = Pin(1, Pin.IN)
def on_change(pin):
state = disable_irq()
pin.value()
enable_irq(state)
pin.irq(on_change, Pin.IRQ_RISING | Pin.IRQ_FALLING)
for i in range(1000):
idle()
sleep_ms(100)
print("WORKING")
It seems impossible to read the value of the pin object passed as an argument to the callback. As soon as it is triggered the RPI Pico stop working until I unplug/replug it. Using the pin from the global context works or even recreating a new Pin instance, but not using the one passed as an argument
firmware: rp2-pico-20230116-unstable-v1.19.1-803-g1583c1f67.uf2
port/rp2: Make tickless, remove 1ms timeout when idle.
The main motivation for doing this was to reduce the latency when the system is woken by a USB interrupt. The best_effort_wfe_or_timeout() function calls into the pico-sdk dynamic timer framework which sets up a new dynamic timer instance each time, and then has to tear it down before continuing after a WFE.
Testing Python interrupt latency it seems to improved by about 12us (from average of 46us to 34us running a Pin IRQ). C-based "scheduled nodes" should see even lower latency.
Test code was:
import machine
from machine import Pin
i = Pin(2, Pin.IN, Pin.PULL_UP)
o = Pin(3, Pin.OUT)
def handler(_):
o(1)
print('!')
o(0)
i.irq(handler, Pin.IRQ_FALLING)
while True:
machine.idle()
With latency measured using logic analyzer:
(Channel 0 is "i", Channel is "o". Please excuse noisy CH0, was triggering with a button. Time 0 is the first falling edge of channel 0.)
The risk with this change is that it exposes cases where a driver expects execution to continue without an interrupt. Alternative is to enable the standard ARM SysTick interrupt to wake the CPU at tick intervals. (There is a branch with SysTick enabled and working, but testing shows tickless is just as functional.)
This work was funded through GitHub Sponsors.