rp2 and ISRs defined by the Pin class
On the RP2040 all GPIO interrupts are funneled through a single vector, which then runs MicroPython functions attached to the GPIO interrupt defined by the Pin class. Unfortunately, this also slows down urgent ISRs that drive external hardware, which can be delayed for a very long time.
One solution is to have a first level interrupt handler (FLIH) for GPIO pins in RAM and higher priority than the MicroPython atomic section (see https://github.com/micropython/micropython/issues/6995), mark as pending a lower priority interrupt (one of the spare interrupt vectors on the RP2040 can be marked pending in software even though there is no hardware line connected to them) that is within the MicroPython priority range, and have this lower priority interrupt vector the MicroPython Pin ISRs as at present. This way, ISRsfor hardware drivers that don't interact with MicroPython directly are not delayed.
WIP: rp2: Make GPIO wakeup from lightsleep consistent.
Summary
Make the Pin.irq() wakeup behaviour more consistent on rp2. Closes https://github.com/micropython/micropython/issues/7035 and partial fix for https://github.com/micropython/micropython/issues/16180
Goal is that any pin which has Pin.irq() and a trigger set will wake rp2 port from any machine.lightsleep() call.
Details
Currently:
- A Pin with an irq() trigger is a wakeup source only when
machine.lightsleep(n)is used with a timeout, and only if an IRQ handler is set.
After this PR:
-
A Pin with an irq() trigger is a wakeup source from both indefinite
machine.lightsleep()andmachine.lightsleep(n)with a timeout. -
A Pin with an irq() trigger is a wakeup source even if no handler function is set.
TODOs
- Waking RP2040 repeatedly from
machine.lightsleep()with no timeout and USB causes the USB connection to misbehave and eventually the CPU hangs. This is the same thing fixed for lightsleep with a timeout in https://github.com/micropython/micropython/pull/15111 and https://github.com/micropython/micropython/pull/15301 . However, with no timeout argument the RP2 goes to the lower power "DORMANT" mode so keeping USB clock running isn't an option. Callingtud_disconnect()/tud_connect()ortud_deinit()/tud_init()before and after going to dormant mode doesn't seem to be enough to fix this. One heavy-handed workaround would be to not select DORMANT if USB is enabled, just call WFI instead. However, then the wakeup conditions are a lot different which may be confusing if developing and testing via USB and then deploying... - Test on RP2350. I have hardware coming, otherwise help is welcome.
- This implementation doesn't match the machine.Pin docs re: irq(wake=) argument. However, I think only cc3200 port has actually implemented this argument as documented, so the fix here might be to make the docs match the current reality rather than the other way around.
This work was funded through GitHub Sponsors.
Testing
- TBD
Trade-offs and Alternatives
- It seems like
machine.lightsleep()has had very limited utility until now (i.e. no possible wake source?) so maybe we should refactor to only use DORMANT mode for deep sleep where we don't have to worry about recovering the USB peripheral. Keeping clocks running does increase consumption, though.