-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pin alarming #3830
Add pin alarming #3830
Conversation
@tannewt All peripherals are clock-gated during light sleep. RTC_IO, RTC_CNTL, Touch, and the ULP are the only ones which can keep working. |
Here is the test code I'm using: import alarm
import alarm.pin
import alarm.time
import time
import neopixel
import board
import digitalio
if alarm.wake_alarm:
print("awake", alarm.wake_alarm, alarm.wake_alarm.pin)
else:
print("no wake up alarm")
enable = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
enable.switch_to_output(False)
# Sleep for 5 so USB can start.
time.sleep(5)
pixels = neopixel.NeoPixel(board.NEOPIXEL, 4)
pixels.fill(0x0f0000)
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic()+10)
print("light time sleep")
a = alarm.light_sleep_until_alarms(time_alarm)
print(a)
pixels.fill(0x000f00)
pin_alarm = alarm.pin.PinAlarm(pin=board.D11, value=False, pull=True)
print("light pin sleep")
a = alarm.light_sleep_until_alarms(pin_alarm)
while not a:
pass
print(dir(a))
print(a, a.pin)
time.sleep(1)
print("sleeping")
# We don't turn off the neopixels explicitly.
# We expect code after the VM to do it.
alarm.exit_and_deep_sleep_until_alarms(pin_alarm) |
I was able to reproduce the error. Uncommenting ESP_LOGI(TAG, "type"); on line 151 of PinAlarm.c prevented the crash. Interestingly, if I had the serial monitor of the mu-editor open it would still crash at the same point even with the line uncommented. |
I spent a while looking at the assembly code of both the good and bad versions. I don't see an error in the assembly code. It's a bit tricky to read because xtensa uses register windows, which make the registers used for in/out arg passing vary based on the caller or callee, and also based on whether it's a We are debugging
I got the assembly code with: $ xtensa-esp32s2-elf-objdump -D build-adafruit_magtag_2.9_grayscale/firmware.elf I won't paste the full excerpts here, because this looks like a dead end. But here are few notes. In both versions circuitpython/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c Lines 111 to 113 in 3923351
The corresponding assembly code is looks like this in both versions (with changes to actual hex values and some register changes, but the registers are still fine and don't get smashed): 400b2a4d: d0dfe5 call8 4008384c <m_malloc> ; m_new_obj()
400b2a50: 3ff751 l32r a5, 40082a2c <_stext+0x2a0c> ; get address for use in following for loop
; unrelated to code immediately following
400b2a53: 030c movi.n a3, 0
400b2a55: 1578 l32i.n a7, a5, 4 ; unrelated to code immediately following
400b2a57: 0a49 s32i.n a4, a10, 0 ; alarm->base.type = &alarm_pin_pin_alarm_type;
400b2a59: 1a39 s32i.n a3, a10, 4 ; alarm->pin = NULL; So if the assembly code looks OK, what's wrong? I think that the bug may not be in this routine, but instead is further along. Adding the |
@dhalbert You can set a watchpoint from within the program using Do I understand right the gist of the issue: you are saving a micropython object in RTC slow memory before sleep, and (at least) one object field has incorrect value after wakeup? |
Great to know, thanks!
No, it's not that tricky. the object is created after CircuitPython restarts, when the VM and our object heap should be in good shape. We check what kind of event woke us up and then create the object. |
I ran some tests and found out that delaying the object creation does seem to work. import alarm
alarm.save()
print(alarm.wake_alarm) Above delays setting wake_alarm by |
I think that the problem may be that the |
I was working in a clone of tannewt/circuitpython, instead of as a remote, so I don't seem to be able to push to the PR. However, here is a patch file with fixes that prevent the crash. 0001-mark-alarm.wake_alarm-during-gc-sweep.patch.zip The test program above is acting oddly, so I wrote a simpler program. Tested on battery power on and USB (simulated deep sleep). import alarm
import alarm.pin
import time
import neopixel
import board
import digitalio
if alarm.wake_alarm:
print("awake", alarm.wake_alarm, alarm.wake_alarm.pin)
alarm.sleep_memory[0] += 1
else:
print("no wake up alarm")
alarm.sleep_memory[0] = 0
print("count:", alarm.sleep_memory[0])
enable = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
enable.switch_to_output(False)
pixels = neopixel.NeoPixel(board.NEOPIXEL, 4)
pixels.fill(0x000f00)
pin_alarm = alarm.pin.PinAlarm(pin=board.D11, value=False, pull=True)
board.DISPLAY.refresh()
print("about to deep_sleep")
# We don't turn off the neopixels explicitly.
# We expect code after the VM to do it.
alarm.exit_and_deep_sleep_until_alarms(pin_alarm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, with some minor naming changes. But what do we want to do about light sleep? Is the code here fine for idle-based light sleep?
* Better messaging when code is stopped by an auto-reload. * Auto-reload works during sleeps on ESP32-S2. Ticks wake up the main task each time. * Made internal naming consistent. CamelCase Python names are NOT separated by an underscore.
I've removed ESP light sleep and now just idle. I also tweaked the messaging in main to add "Code stopped by auto-reload." Please take another look! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! I did not run further tests.
I'll have to revise the sleep guide about light sleep power consumption (doc and graphs), and see if we see any reduction during the sleep.
I've seen about 30mA. The PinAlarm also takes ~1.4mA while deep sleeping because it has the RTC peripherals enabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-approving in anticipation of a clean build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the last bit. I was doing my pushes via the web. I'll let you merge if you're satisfied.
This mostly works but I have a crash happening when exiting deep sleep off of USB and reading the wake up object. The object has an incorrect type even though I'm setting it. If I log where I set it, the problem goes away. The last commit is the debug logs for it.
I also noticed that GPIO is disabled during time-based light sleep. I wonder if other peripherals are also disabled. The docs only discuss the RTC domains. @igrr can you explain what peripherals are preserved during light sleep?