adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.04k stars 1.19k forks source link

exit_and_deep_sleep_until_alarms not working on rp2040 #7365

Open adriangalilea opened 1 year ago

adriangalilea commented 1 year ago

CircuitPython version

Adafruit CircuitPython 8.0.0-beta.4 on 2022-10-30; Raspberry Pi Pico W with rp2040

Code/REPL

import alarm
import time
import board

print("Waking up")

# Create an alarm for 60 seconds from now, and also a pin alarm.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 20)
pin_alarm = alarm.pin.PinAlarm(pin=board.GP12, value=False)

# Deep sleep until one of the alarm goes off. Then restart the program.
alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm)

Behavior

I get no errors and simply it doesn't wake up again, neither from the time nor the pin alarms.

Description

Using thonny as IDE

Additional information

No response

DavePutz commented 1 year ago

Testing with Adafruit CircuitPython 8.1.0-beta.2 on 2023-04-26; Raspberry Pi Pico W with rp2040 If I remove the pin_alarm the time_alarm works consistently. With the pin_alarm enabled I see wildly inconsistent results (even though nothing was connected to the pin); sometimes with the alarm getting called constantly, and sometimes including a hard crash:

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
Waking up
Auto-reload is off.
Running in safe mode! Not running saved code.

You are in safe mode because:
CircuitPython core code crashed hard. Whoops!
Fault detected by hardware.
Please file an issue with your program at https://github.com/adafruit/circuitpython/issues.
Press reset to exit safe mode.

Sometimes even rebooting only into the boot handler, requiring a reload of the .uf2.

DavePutz commented 1 year ago

Tested on a plain Pico (no W) running CP 8.1 beta and both alarms worked correctly. Re-tested the PICO W without wifi on and still saw the issues with the pin_alarm.

DavePutz commented 1 year ago

I believe this issue may be occurring when the status of the pin is already in the triggered state when the alarm is being created. . @adriangalilea ; could you test your script adding in "edge=True" to the pin_alarm parameter list?

adriangalilea commented 1 year ago

Apologies but I might not be able to test it for now, will add a reminder, sorry 😞

bablokb commented 1 year ago

With the pin_alarm enabled I see wildly inconsistent results (even though nothing was connected to the pin);

Nothing connected means floating... Can you reproduce the behavior with a small pullup attached?

kbsriram commented 6 months ago

Adding some additional notes here after testing on a Pico W with

Adafruit CircuitPython 9.0.0-beta.2 on 2024-02-20; Raspberry Pi Pico with rp2040

and this code:

import alarm
import board
import digitalio
import time

# Just to make sure we're awake.
with digitalio.DigitalInOut(board.GP14) as status:
    status.switch_to_output(value=True)
    time.sleep(2)
    status.switch_to_output(value=False)

# Create an alarm for 10 seconds from now, and also a pin alarm.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)
pin_alarm = alarm.pin.PinAlarm(pin=board.GP15, value=False, pull=True)

# Deep sleep until one of the alarm goes off. Then restart the program.
alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm)

When running connected (i.e., supervisor runtime faking deep sleep), both sleep and pin alarm work correctly, and wake up the device out at the appropriate times. I see this output when GP14 is pulled low, or after 10 seconds.

Press any key to enter the REPL. Use CTRL-D to reload.
Pretending to deep sleep until alarm, CTRL-C or file write.
Woken up by alarm.

However, when running disconnected, only the sleep timer wakes up the device - pulling GP14 low does not wake up the device. (This remains the case even if explicitly pulling the pin high during boot up, and then pulling it low once it goes into sleep.)

bablokb commented 6 months ago

Have you tested this with an external pull-up? Please be aware that pulling it high in software before going to sleep is not relevant, the pico won't keep the state when in deep-sleep.

kbsriram commented 6 months ago

Hi @bablokb - thanks for verifying! Yes, this is with an external pull-up resistor that's grounded to wake the device.

I also saw your power-tests repo (nice!) and that prompted me to poke a bit deeper, and wondering if the issue is seen when both pin and timer wakeups are used at the same time?

This is the sample code I used (with commented sections changed as appropriate) together with a pullup resistor at G14 and an LED at G15.

import alarm
import board
import digitalio
import time

from_pin = isinstance(alarm.wake_alarm, alarm.pin.PinAlarm)

with digitalio.DigitalInOut(board.GP15) as status:
    for _ in range(1 if from_pin else 5):
        status.switch_to_output(value=True)
        time.sleep(0.1)
        status.switch_to_output(value=False)
        time.sleep(0.1)

# Create an alarm for 30 seconds from now, and also a pin alarm.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 30)
pin_alarm = alarm.pin.PinAlarm(pin=board.GP14, value=False, pull=True)

# Deep sleep until one of the alarm goes off. Then restart the program.
alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm)
# alarm.exit_and_deep_sleep_until_alarms(pin_alarm)

Everything works as expected when connected.

I power down the device, and then power it up without connecting it to anything.

When only the pin or timer is used in the exit_and_deep_sleep_until_alarms call, things also work as expected.

When both are used, the device wakes up only at the timer interval regardless of whether I ground the pin or not. However, in any period where I have grounded the pin, upon wakeup the device flashes once - so it must have still recorded a PinAlarm. Somehow it's not actually fully waking up the device in this situation for me.

I'm not familiar with the code, but at first glance like the pin alarm logic does this when deep sleep is requested, but the deep_sleep code does this - could that lead to any issues?

bablokb commented 6 months ago

When both are used, the device wakes up only at the timer interval regardless of whether I ground the pin or not. However, in any period where I have grounded the pin, upon wakeup the device flashes once - so it must have still recorded a PinAlarm. Somehow it's not actually fully waking up the device in this situation for me.

I'm not familiar with the code, but at first glance like the pin alarm logic does this when deep sleep is requested, but the deep_sleep code does this - could that lead to any issues?

I think you found the issue. The call to __wfi() (wait-for-interrupt) will wait for the timer-interrupt to fire. The reset_cpu() at the end of the function will eventually also call __wfi().

The core problem will be that timer-interrupts are implemented differently than pin-interrupts (in the code: "If there's a timealarm, just enter a very deep light sleep").

My workaround is that I use the interrupt-pin of an external RTC instead of a TimeAlarm if possible.