From bd9dd3dc63f6b184b276df27c03011c040a490a6 Mon Sep 17 00:00:00 2001 From: Tsutomu IKEGAMI Date: Thu, 17 Feb 2022 23:00:26 +0900 Subject: [PATCH] Fix atmel-samd alarm module. --- locale/circuitpython.pot | 19 +- .../boards/seeeduino_wio_terminal/board.c | 11 + .../atmel-samd/common-hal/alarm/SleepMemory.c | 16 +- .../atmel-samd/common-hal/alarm/SleepMemory.h | 2 - ports/atmel-samd/common-hal/alarm/__init__.c | 194 ++++----- ports/atmel-samd/common-hal/alarm/__init__.h | 11 +- .../common-hal/alarm/pin/PinAlarm.c | 410 ++++++++++-------- .../common-hal/alarm/pin/PinAlarm.h | 5 +- .../common-hal/alarm/time/TimeAlarm.c | 100 ++--- 9 files changed, 406 insertions(+), 362 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 8daa071c57..84586801d5 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -355,6 +355,7 @@ msgstr "" msgid "64 bit types" msgstr "" +#: ports/atmel-samd/common-hal/alarm.orig/pin/PinAlarm.c #: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c #: ports/atmel-samd/common-hal/countio/Counter.c #: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -1682,6 +1683,7 @@ msgstr "" msgid "No hardware support on clk pin" msgstr "" +#: ports/atmel-samd/common-hal/alarm.orig/pin/PinAlarm.c #: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c @@ -1827,6 +1829,7 @@ msgstr "" msgid "Only one address is allowed" msgstr "" +#: ports/atmel-samd/common-hal/alarm.orig/time/TimeAlarm.c #: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c #: ports/espressif/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c @@ -1898,6 +1901,7 @@ msgstr "" msgid "Permission denied" msgstr "" +#: ports/atmel-samd/common-hal/alarm.orig/pin/PinAlarm.c #: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Pin cannot wake from Deep Sleep" @@ -1931,6 +1935,10 @@ msgstr "" msgid "Pin is input only" msgstr "" +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +msgid "Pin is not free" +msgstr "" + #: ports/raspberrypi/common-hal/countio/Counter.c msgid "Pin must be on PWM Channel B" msgstr "" @@ -2158,7 +2166,7 @@ msgstr "" msgid "Size not supported" msgstr "" -#: ports/atmel-samd/common-hal/alarm/SleepMemory.c +#: ports/atmel-samd/common-hal/alarm.orig/SleepMemory.c #: ports/raspberrypi/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2319,6 +2327,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/atmel-samd/common-hal/alarm.orig/touch/TouchAlarm.c #: ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c @@ -2427,6 +2436,10 @@ msgstr "" msgid "Unhandled ESP TLS error %d %d %x %d" msgstr "" +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +msgid "Unknown error" +msgstr "" + #: shared-bindings/wifi/Radio.c #, c-format msgid "Unknown failure %d" @@ -3630,7 +3643,7 @@ msgstr "" msgid "matrix is not positive definite" msgstr "" -#: shared-bindings/wifi/Radio.c +#: ports/espressif/common-hal/wifi/Radio.c msgid "max_connections must be between 0 and 10" msgstr "" @@ -4048,6 +4061,7 @@ msgstr "" #: ports/espressif/boards/adafruit_metro_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h +#: ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.h #: ports/espressif/boards/ai_thinker_esp32-c3s/mpconfigboard.h #: ports/espressif/boards/ai_thinker_esp_12k_nodemcu/mpconfigboard.h #: ports/espressif/boards/artisense_rd00/mpconfigboard.h @@ -4055,6 +4069,7 @@ msgstr "" #: ports/espressif/boards/crumpspace_crumps2/mpconfigboard.h #: ports/espressif/boards/electroniccats_bastwifi/mpconfigboard.h #: ports/espressif/boards/espressif_esp32c3_devkitm_1_n4/mpconfigboard.h +#: ports/espressif/boards/espressif_esp32s2_devkitc_1_n4r2/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_box/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8r2/mpconfigboard.h diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c index f10a666707..78f856aaad 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c @@ -32,6 +32,7 @@ #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "common-hal/alarm/__init__.h" displayio_fourwire_obj_t board_display_obj; digitalio_digitalinout_obj_t CTR_5V; @@ -139,4 +140,14 @@ void reset_board(void) { } void board_deinit(void) { + common_hal_displayio_release_displays(); + common_hal_digitalio_digitalinout_deinit(&CTR_5V); + common_hal_digitalio_digitalinout_deinit(&CTR_3V3); + common_hal_digitalio_digitalinout_deinit(&USB_HOST_ENABLE); +} + +void board_deep_sleep_hook(void) { + // Pins are reset before entering deep sleep in cleanup_after_vm() + // This hook turn RTL_PWR off + gpio_set_pin_direction(pin_PA18.number, GPIO_DIRECTION_OUT); } diff --git a/ports/atmel-samd/common-hal/alarm/SleepMemory.c b/ports/atmel-samd/common-hal/alarm/SleepMemory.c index 8ad3577564..1a8eef3aec 100644 --- a/ports/atmel-samd/common-hal/alarm/SleepMemory.c +++ b/ports/atmel-samd/common-hal/alarm/SleepMemory.c @@ -32,20 +32,24 @@ #include "shared-bindings/nvm/ByteArray.h" void alarm_sleep_memory_reset(void) { - } uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { - mp_raise_NotImplementedError(translate("Sleep Memory not available")); - return 0; + return BKUPRAM_SIZE; } bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, const uint8_t *values, uint32_t len) { - mp_raise_NotImplementedError(translate("Sleep Memory not available")); - return false; + if (start_index + len > BKUPRAM_SIZE) { + return false; + } + memcpy((uint8_t *)(BKUPRAM_ADDR + start_index), values, len); + return true; } void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t *values, uint32_t len) { - mp_raise_NotImplementedError(translate("Sleep Memory not available")); + if (start_index + len > BKUPRAM_SIZE) { + return; + } + memcpy(values, (uint8_t *)(BKUPRAM_ADDR + start_index), len); return; } diff --git a/ports/atmel-samd/common-hal/alarm/SleepMemory.h b/ports/atmel-samd/common-hal/alarm/SleepMemory.h index a922ff324a..5fad5946e2 100644 --- a/ports/atmel-samd/common-hal/alarm/SleepMemory.h +++ b/ports/atmel-samd/common-hal/alarm/SleepMemory.h @@ -31,8 +31,6 @@ typedef struct { mp_obj_base_t base; - uint8_t *start_address; - uint8_t len; } alarm_sleep_memory_obj_t; extern void alarm_sleep_memory_reset(void); diff --git a/ports/atmel-samd/common-hal/alarm/__init__.c b/ports/atmel-samd/common-hal/alarm/__init__.c index 66b42a3062..4812248303 100644 --- a/ports/atmel-samd/common-hal/alarm/__init__.c +++ b/ports/atmel-samd/common-hal/alarm/__init__.c @@ -39,62 +39,50 @@ #include "supervisor/port.h" #include "supervisor/workflow.h" +STATIC uint32_t TAMPID = 0; + // Singleton instance of SleepMemory. const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { .base = { .type = &alarm_sleep_memory_type, }, }; -// TODO: make a custom enum to avoid weird values like PM_SLEEPCFG_SLEEPMODE_BACKUP_Val? -STATIC volatile uint32_t _target; -STATIC bool fake_sleep; -STATIC bool pin_wake; void alarm_reset(void) { // Reset the alarm flag - SAMD_ALARM_FLAG = 0x00; alarm_pin_pinalarm_reset(); alarm_time_timealarm_reset(); } -samd_sleep_source_t alarm_get_wakeup_cause(void) { - // If in light/fake sleep, check modules - if (alarm_pin_pinalarm_woke_this_cycle()) { - return SAMD_WAKEUP_GPIO; - } - if (alarm_time_timealarm_woke_this_cycle()) { - return SAMD_WAKEUP_RTC; - } - if (!fake_sleep && RSTC->RCAUSE.bit.BACKUP) { - // This is checked during rtc_init to cache TAMPID if necessary - if (pin_wake || RTC->MODE0.TAMPID.reg) { - pin_wake = true; - return SAMD_WAKEUP_GPIO; - } - return SAMD_WAKEUP_RTC; - } - return SAMD_WAKEUP_UNDEF; +void alarm_get_wakeup_cause(void) { + // Called from rtc_init, just before SWRST of RTC. It is called + // at an early stage of main(), to save TAMPID from SWRST. Later, + // common_hal_alarm_create_wake_alarm is called to make a wakeup + // alarm from the deep sleep. + + TAMPID = RTC->MODE0.TAMPID.reg; } bool common_hal_alarm_woken_from_sleep(void) { - return alarm_get_wakeup_cause() != SAMD_WAKEUP_UNDEF; + return alarm_pin_pinalarm_woke_this_cycle() || alarm_time_timealarm_woke_this_cycle(); } mp_obj_t common_hal_alarm_create_wake_alarm(void) { - // If woken from deep sleep, create a copy alarm similar to what would have - // been passed in originally. Otherwise, just return none - samd_sleep_source_t cause = alarm_get_wakeup_cause(); - switch (cause) { - case SAMD_WAKEUP_RTC: { - return alarm_time_timealarm_create_wakeup_alarm(); - } - case SAMD_WAKEUP_GPIO: { - return alarm_pin_pinalarm_create_wakeup_alarm(); - } - case SAMD_WAKEUP_UNDEF: - default: - // Not a deep sleep reset. - break; + // Called from main.c on the first start up, just before alarm_reset. + // Return a copy of wakeup alarm from deep sleep / fake deep sleep. + // In case of fake sleep, status should be left in TimeAlarm/PinAlarm. + bool true_deep = RSTC->RCAUSE.bit.BACKUP; + + if (alarm_pin_pinalarm_woke_this_cycle()) { + TAMPID = RTC->MODE0.TAMPID.reg; + RTC->MODE0.TAMPID.reg = TAMPID; // clear register + return alarm_pin_pinalarm_create_wakeup_alarm(TAMPID); + } + if (alarm_time_timealarm_woke_this_cycle() || (true_deep && TAMPID == 0)) { + return alarm_time_timealarm_create_wakeup_alarm(); + } + if (true_deep) { + return alarm_pin_pinalarm_create_wakeup_alarm(TAMPID); } return mp_const_none; } @@ -103,36 +91,33 @@ mp_obj_t common_hal_alarm_create_wake_alarm(void) { STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms); alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms); - fake_sleep = false; } mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) { _setup_sleep_alarms(false, n_alarms, alarms); mp_obj_t wake_alarm = mp_const_none; + // This works but achieves same power consumption as time.sleep() + PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_STANDBY; + while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_STANDBY_Val) { + } + // Even though RAMCFG_OFF, SYSRAM seems to be retained. Probably + // because RTC keeps sleepwalking. Anyway, STDBYCFG should be + // left intact as 0 to retain SYSRAM. + #if 0 + PM->STDBYCFG.reg = PM_STDBYCFG_RAMCFG_OFF; + #endif while (!mp_hal_is_interrupted()) { RUN_BACKGROUND_TASKS; // Detect if interrupt was alarm or ctrl-C interrupt. - if (common_hal_alarm_woken_from_sleep()) { - samd_sleep_source_t cause = alarm_get_wakeup_cause(); - switch (cause) { - case SAMD_WAKEUP_RTC: { - wake_alarm = alarm_time_timealarm_find_triggered_alarm(n_alarms,alarms); - break; - } - case SAMD_WAKEUP_GPIO: { - wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms); - break; - } - default: - // Should not reach this, if all light sleep types are covered correctly - break; - } - shared_alarm_save_wake_alarm(wake_alarm); + if (alarm_time_timealarm_woke_this_cycle()) { + wake_alarm = alarm_time_timealarm_find_triggered_alarm(n_alarms,alarms); + break; + } + if (alarm_pin_pinalarm_woke_this_cycle()) { + wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms); break; } - // ATTEMPT ------------------------------ - // This works but achieves same power consumption as time.sleep() // Clear the FPU interrupt because it can prevent us from sleeping. if (__get_FPSCR() & ~(0x9f)) { @@ -140,27 +125,22 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj (void)__get_FPSCR(); } - // Disable RTC interrupts - NVIC_DisableIRQ(RTC_IRQn); - // Set standby power domain stuff - PM->STDBYCFG.reg = PM_STDBYCFG_RAMCFG_OFF; - // Set-up Sleep Mode - PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_STANDBY; - while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_STANDBY_Val) { - ; - } - + common_hal_mcu_disable_interrupts(); __DSB(); // Data Synchronization Barrier __WFI(); // Wait For Interrupt - // Enable RTC interrupts - NVIC_EnableIRQ(RTC_IRQn); - // END ATTEMPT ------------------------------ + common_hal_mcu_enable_interrupts(); } + // Restore SLEEPCFG or port_idle_until_interrupt sleeps in STANDBY mode. + PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_IDLE2; + while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_IDLE2_Val) { + } + alarm_pin_pinalarm_deinit_alarms(n_alarms, alarms); // after care for alarm_pin_pinalarm_set_alarms + alarm_reset(); + if (mp_hal_is_interrupted()) { return mp_const_none; // Shouldn't be given to python code because exception handling should kick in. } - alarm_reset(); return wake_alarm; } @@ -171,11 +151,17 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala void NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); - _target = RTC->MODE0.COMP[1].reg; - port_disable_tick(); // TODO: Required for SAMD? + // port_disable_tick(); // TODO: Required for SAMD? - // cache alarm flag since backup registers about to be reset - uint32_t _SAMD_ALARM_FLAG = SAMD_ALARM_FLAG; + // Set pin state before the deep sleep, to turn off devices and reduce power. + // In case of Wio Terminal, set PA18 to 0 to turn off RTL8720DN. + // Pin state is kept during BACKUP sleep. + board_deep_sleep_hook(); + + // cache alarm flag and etc since RTC about to be reset + uint32_t _flag = SAMD_ALARM_FLAG; // RTC->MODE0.BKUP[0].reg + uint32_t _target = RTC->MODE0.COMP[1].reg; + uint32_t _tampctrl = RTC->MODE0.TAMPCTRL.reg; // Clear the FPU interrupt because it can prevent us from sleeping. if (__get_FPSCR() & ~(0x9f)) { @@ -183,57 +169,42 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) { (void)__get_FPSCR(); } - NVIC_DisableIRQ(RTC_IRQn); + common_hal_mcu_disable_interrupts(); // Must disable the RTC before writing to EVCTRL and TMPCTRL - RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC - while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; - } RTC->MODE0.CTRLA.bit.SWRST = 1; // Software reset the RTC while (RTC->MODE0.SYNCBUSY.bit.SWRST) { // Wait for synchronization - ; } RTC->MODE0.CTRLA.reg = RTC_MODE0_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024 RTC_MODE0_CTRLA_MODE_COUNT32; // Set RTC to mode 0, 32-bit timer + SAMD_ALARM_FLAG = _flag; // Check if we're setting TimeAlarm - if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_TIME) { - RTC->MODE0.COMP[1].reg = (_target / 1024) * 32; + if (SAMD_ALARM_FLAG_TIME_CHK) { + RTC->MODE0.COMP[1].reg = _target; while (RTC->MODE0.SYNCBUSY.reg) { - ; } - } - // Check if we're setting PinAlarm - if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_PIN) { - RTC->MODE0.TAMPCTRL.bit.DEBNC2 = 1; // Edge triggered when INn is stable for 4 CLK_RTC_DEB periods - RTC->MODE0.TAMPCTRL.bit.TAMLVL2 = 1; // rising edge - // PA02 = IN2 - RTC->MODE0.TAMPCTRL.bit.IN2ACT = 1; // WAKE on IN2 (doesn't save timestamp) - } - // Enable interrupts - NVIC_SetPriority(RTC_IRQn, 0); - NVIC_EnableIRQ(RTC_IRQn); - if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_TIME) { - // Set interrupts for COMPARE1 RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP1; } - if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_PIN) { - // Set interrupts for TAMPER pins + // Check if we're setting PinAlarm + if (SAMD_ALARM_FLAG_PIN_CHK) { + RTC->MODE0.TAMPCTRL.reg = _tampctrl; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER; } + // Enable interrupts + common_hal_mcu_enable_interrupts(); // Set-up Deep Sleep Mode & RAM retention + // Left BRAMCFG untouched as 0 + #if 0 PM->BKUPCFG.reg = PM_BKUPCFG_BRAMCFG(0x2); // No RAM retention 0x2 partial:0x1 while (PM->BKUPCFG.bit.BRAMCFG != 0x2) { // Wait for synchronization - ; } + #endif PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_BACKUP; while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val) { - ; } RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; } __DSB(); // Data Synchronization Barrier @@ -245,17 +216,24 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) { } } +// In case of fake deep sleep, event loop is mostly managed in main.c. +// Default common_hal_alarm_pretending_deep_sleep is defined in shared-bindings. +// Note that "pretending" does not work on REPL; it only works for main.py (or code.py, ...). +// +// In case of fake sleep, if pin state is modified in the hook, the pin is left dirty... +#if 0 void common_hal_alarm_pretending_deep_sleep(void) { - // TODO: - // If tamper detect interrupts cannot be used to wake from the Idle tier of sleep, - // This section will need to re-initialize the pins to allow the PORT peripheral - // to generate external interrupts again. See STM32 for reference. + // RTC is already be furnished by common_hal_alarm_set_deep_sleep_alarms. + // fake_sleep = true; - if (!fake_sleep) { - fake_sleep = true; - } + board_deep_sleep_hook(); + port_idle_until_interrupt(); } +#endif void common_hal_alarm_gc_collect(void) { gc_collect_ptr(shared_alarm_get_wake_alarm()); } + +MP_WEAK void board_deep_sleep_hook(void) { +} diff --git a/ports/atmel-samd/common-hal/alarm/__init__.h b/ports/atmel-samd/common-hal/alarm/__init__.h index 5e777bdf87..11d83f4f19 100644 --- a/ports/atmel-samd/common-hal/alarm/__init__.h +++ b/ports/atmel-samd/common-hal/alarm/__init__.h @@ -37,6 +37,14 @@ extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj; #define SAMD_ALARM_FLAG (RTC->MODE0.BKUP[0].reg) #define SAMD_ALARM_FLAG_TIME (_U_(0x1) << 0) #define SAMD_ALARM_FLAG_PIN (_U_(0x1) << 1) + +#define SAMD_ALARM_FLAG_TIME_SET (SAMD_ALARM_FLAG |= SAMD_ALARM_FLAG_TIME) +#define SAMD_ALARM_FLAG_TIME_CLR (SAMD_ALARM_FLAG &= ~SAMD_ALARM_FLAG_TIME) +#define SAMD_ALARM_FLAG_TIME_CHK (SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_TIME) + +#define SAMD_ALARM_FLAG_PIN_SET (SAMD_ALARM_FLAG |= SAMD_ALARM_FLAG_PIN) +#define SAMD_ALARM_FLAG_PIN_CLR (SAMD_ALARM_FLAG &= ~SAMD_ALARM_FLAG_PIN) +#define SAMD_ALARM_FLAG_PIN_CHK (SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_PIN) #endif typedef enum { @@ -46,7 +54,8 @@ typedef enum { } samd_sleep_source_t; extern void alarm_set_wakeup_reason(samd_sleep_source_t reason); -samd_sleep_source_t alarm_get_wakeup_cause(void); +void alarm_get_wakeup_cause(void); extern void alarm_reset(void); +extern void board_deep_sleep_hook(void); #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM__INIT__H diff --git a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c index da99863e4c..b51fb21929 100644 --- a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c @@ -38,70 +38,61 @@ // This variable stores whether a PinAlarm woke in light sleep or fake deep sleep // It CANNOT detect if the program woke from deep sleep. -STATIC volatile bool woke_up; -// TODO: replace pinalarm_on with SAMD_ALARM_FLAG bit flags -STATIC volatile bool pinalarm_on; +STATIC volatile bool woke_up = false; +STATIC alarm_pin_pinalarm_obj_t *trig_alarm = NULL; -// TODO: Create tables here reserving IRQ instances, and for the IRQ -// callback to store what pin triggered the interrupt -// STATIC bool reserved_alarms[SOME_VAL]; -// STATIC uint16_t triggered_pins[SOME_VAL]; +// Tamper Pins for deep sleep: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; +// wakeup from deep sleep seems to be triggered when these pins go from Hi/Lo to Hi-Z state. +typedef struct { + int n; + const mcu_pin_obj_t *pin; +} samd_tamper_pin_t; -void pin_alarm_callback(uint8_t num) { // parameters can be changed - // TODO: This is responsible for resetting the IRQ (so it doesn't - // go off constantly, and recording what pin was responsible for - // the trigger. This will only work for light sleep/fake deep - // sleep, in conjunction with the find_triggered_alarm function +static samd_tamper_pin_t TAMPER_PINS[] = { + #if defined(PIN_PB00) && !defined(IGNORE_PIN_PB00) + { 0, &pin_PB00 }, + #endif + #if defined(PIN_PB02) && !defined(IGNORE_PIN_PB02) + { 1, &pin_PB02 }, + #endif + #if defined(PIN_PA02) && !defined(IGNORE_PIN_PA02) + { 2, &pin_PA02 }, + #endif + #if defined(PIN_PC00) && !defined(IGNORE_PIN_PC00) + { 3, &pin_PC00 }, + #endif + #if defined(PIN_PC01) && !defined(IGNORE_PIN_PC01) + { 4, &pin_PC01 }, + #endif + { -1, NULL } +}; - if (pinalarm_on) { - // clear flag and interrupt setting +void pin_alarm_callback(uint8_t num) { + // called back with EIC/RTC interrupt + if (!SAMD_ALARM_FLAG_PIN_CHK) { + return; + } + woke_up = true; + // SAMD_ALARM_FLAG_PIN_CLR; + if (RTC->MODE0.INTFLAG.reg & RTC_MODE0_INTFLAG_TAMPER) { // fake deep sleep RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_TAMPER; - pinalarm_on = false; - // QUESTION: How to reference the correct EIC? - // set_eic_handler(self->channel, EIC_HANDLER_NO_INTERRUPT); - // turn_off_eic_channel(self->channel); - // reset_pin_number(self->pin); - woke_up = true; + trig_alarm = NULL; + } else { // light sleep + // Turn off sensing for the channel, or level detection will + // be tirggered again. + // If we clear EIC->INTENCLR flag here, cleaning up in deint + // may fail because MCLK to EIC is stopped earlier by + // turn_off_eic_channel. + configure_eic_channel(num, EIC_CONFIG_SENSE0_NONE_Val); + trig_alarm = get_eic_channel_data(num); } } void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { - // Tamper Pins: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; OUT:PB01 - // TODO: Catch edge or level mode if not supported - if (!pin->has_extint) { - mp_raise_RuntimeError(translate("No hardware support on pin")); - } - if (eic_get_enable()) { - if (!eic_channel_free(pin->extint_channel)) { - mp_raise_RuntimeError(translate("A hardware interrupt channel is already in use")); - } - } else { - turn_on_external_interrupt_controller(); - } - - // TODO: determine if pin has an interrupt channel available - // TODO: set pin pull settings, input/output, etc - // QUESTION: can PORT/EVSYS interrupts (lightsleep) coexist with RTC->TAMPER (deepsleep) settings? - // Actual initialization of the interrupt should be delayed until set_alarm - - self->channel = pin->extint_channel; self->pin = pin; self->value = value; + self->edge = edge; self->pull = pull; - - gpio_set_pin_function(pin->number, GPIO_PIN_FUNCTION_A); - if (self->pull) { - if (self->value) { - // detect rising edge means pull down - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_DOWN); - } else { - // detect falling edge means pull up - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_UP); - } - } - set_eic_channel_data(self->channel, (void *)self); - - claim_pin(self->pin); } const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { @@ -113,7 +104,7 @@ bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self) { } bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self) { - return true; + return self->edge; } bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { @@ -121,9 +112,6 @@ bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { } bool alarm_pin_pinalarm_woke_this_cycle(void) { - if (pinalarm_on && RTC->MODE0.INTFLAG.bit.TAMPER) { - woke_up = true; - } return woke_up; } @@ -133,145 +121,209 @@ mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t continue; } alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); - (void)alarm; - - - // TODO: Determine whether any pins have been marked as - // triggering the alarm (using the static vars at - // start of file) and if so return that alarm. + if (alarm == trig_alarm) { + return alarms[i]; + } } - // Return nothing if no matching alarms are found. return mp_const_none; } -mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void) { - alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t); - alarm->base.type = &alarm_pin_pinalarm_type; - // TODO: Extract information about what pin woke the device. - // Because this interrupt occurs in deep sleep, the callback - // cannot be used to store this information. It must be extracted - // from the registers, if possible. It may be impossible to - // obtain, in which case return a dummy value. - return alarm; +mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(uint32_t TAMPID) { + // Create tamper alarm that caused wakeup from deep sleep + + for (samd_tamper_pin_t *t = TAMPER_PINS; t->n >= 0; t++) { + if (TAMPID & (1 << t->n)) { + alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t); + alarm->base.type = &alarm_pin_pinalarm_type; + alarm->pin = t->pin; + return alarm; + } + } + return mp_const_none; } void alarm_pin_pinalarm_reset(void) { - // TODO: this is responsible for resetting ALL pinalarms. Make - // sure to clear any reserved tables, deinit both PORT and TAMPER - // settings, etc. If flags are set to indicate this module is in - // use, reset them. - pinalarm_on = false; + SAMD_ALARM_FLAG_PIN_CLR; woke_up = false; - SAMD_ALARM_FLAG &= ~SAMD_ALARM_FLAG_PIN; // clear flag // Disable TAMPER interrupt RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_TAMPER; - // Disable TAMPER control - common_hal_mcu_disable_interrupts(); - RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC - while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; + if (RTC->MODE0.TAMPCTRL.reg != 0) { + common_hal_mcu_disable_interrupts(); + RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC + while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization + } + RTC->MODE0.TAMPCTRL.reg = 0; // reset everything + RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC + while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization + } + common_hal_mcu_enable_interrupts(); } - RTC->MODE0.TAMPCTRL.reg = 0; // reset everything - RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC - while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; +} + +#define PINALARM_NOERR 0 +#define PINALARM_ERR_NOT_FREE -1 +#define PINALARM_ERR_NOEXTINT -2 +#define PINALARM_ERR_NOCHANNEL -3 +#define PINALARM_ERR_NOTTAMPER -4 + +// Because PinAlarm does not have deinit, pins are furnished just +// before the light sleep here, and deinited after wake-up. +static void pinalarm_set_alarms_light(size_t n_alarms, const mp_obj_t *alarms) { + int err = PINALARM_NOERR; + size_t i; + + for (i = 0; i < n_alarms; i++) { + if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + continue; + } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + const mcu_pin_obj_t *pin = alarm->pin; + + if (!pin_number_is_free(pin->number)) { + err = PINALARM_ERR_NOT_FREE; + break; + } + if (!pin->has_extint) { + err = PINALARM_ERR_NOEXTINT; + break; + } + if (eic_get_enable()) { + if (!eic_channel_free(pin->extint_channel)) { + err = PINALARM_ERR_NOCHANNEL; + break; + } + } else { + turn_on_external_interrupt_controller(); + // It is automatically turned-off in turn_off_eic_channel. + } + + SAMD_ALARM_FLAG_PIN_SET; + gpio_set_pin_function(pin->number, GPIO_PIN_FUNCTION_A); + uint32_t sense; + if (alarm->value) { + sense = alarm->edge ? EIC_CONFIG_SENSE0_RISE_Val : EIC_CONFIG_SENSE0_HIGH_Val; + if (alarm->pull) { + // detect rising edge means pull down + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_DOWN); + } + } else { + sense = alarm->edge ? EIC_CONFIG_SENSE0_FALL_Val : EIC_CONFIG_SENSE0_LOW_Val; + if (alarm->pull) { + // detect falling edge means pull up + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_UP); + } + } + + set_eic_channel_data(pin->extint_channel, (void *)alarm); + claim_pin(pin); + set_eic_handler(pin->extint_channel, EIC_HANDLER_ALARM); + turn_on_eic_channel(pin->extint_channel, sense); + } + + if (err == PINALARM_NOERR) { + return; + } + + SAMD_ALARM_FLAG_PIN_CLR; + alarm_pin_pinalarm_deinit_alarms(i, alarms); + + switch (err) { + case PINALARM_ERR_NOT_FREE: + mp_raise_RuntimeError(translate("Pin is not free")); + ; + case PINALARM_ERR_NOEXTINT: + mp_raise_RuntimeError(translate("No hardware support on pin")); + case PINALARM_ERR_NOCHANNEL: + mp_raise_RuntimeError(translate("A hardware interrupt channel is already in use")); + default: + mp_raise_RuntimeError(translate("Unknown error")); + } +} + +static void pinalarm_set_alarms_deep(size_t n_alarms, const mp_obj_t *alarms) { + // In case of deep sleep, pin_number_is_free is not checked. + // Errata says that falling edge should be detected for the tamper pins. + samd_tamper_pin_t *t; + uint32_t tampctrl = 0; + + for (size_t i = 0; i < n_alarms; i++) { + if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + continue; + } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + const mcu_pin_obj_t *pin = alarm->pin; + + for (t = TAMPER_PINS; t->n >= 0; t++) { + if (pin == t->pin) { + break; + } + } + if (t->n < 0) { + mp_raise_ValueError(translate("Pin cannot wake from Deep Sleep")); + } + + // It is strange, but to my experiment, interrupt during sleep + // is triggered on transition from Hi/Lo to Hi-Z state, and + // not on switching from Hi/Lo to Lo/Hi state, regardless of + // TAMLVL value. If tested on non-sleep condition, TAMPID + // reacts either on falling or rising edge, according to + // TAMLVL value as doumented. + tampctrl |= (((1UL << t->n) << 24) | // DEBNCn + ((alarm->value << t->n) << 16) | // TAMLVLn + (1UL << t->n * 2)); // INnACT + + // Tamper pins under the control of RTC are kept in Hi-Z + // state, and pull mode is not effective. + #if 0 + if (alarm->pull) { + if (alarm->value) { + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_DOWN); + } else { + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_UP); + } + } + #endif + } + if (tampctrl == 0) { + return; + } + + SAMD_ALARM_FLAG_PIN_SET; + RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_TAMPER; + RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER; + common_hal_mcu_disable_interrupts(); + RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC + while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization + } + RTC->MODE0.TAMPCTRL.reg = tampctrl; + RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC + while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization } common_hal_mcu_enable_interrupts(); } -void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { - // The outer layer of this loop simply checks if there are any pin - // alarms in the parameter array +void alarm_pin_pinalarm_deinit_alarms(size_t n_alarms, const mp_obj_t *alarms) { for (size_t i = 0; i < n_alarms; i++) { - if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { - alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); - gpio_set_pin_function(alarm->pin->number, GPIO_PIN_FUNCTION_A); - if (alarm->pull) { - if (alarm->value) { - // detect rising edge means pull down - gpio_set_pin_pull_mode(alarm->pin->number, GPIO_PULL_DOWN); - } else { - // detect falling edge means pull up - gpio_set_pin_pull_mode(alarm->pin->number, GPIO_PULL_UP); - } - } - if (deep_sleep) { - // Tamper Pins: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; OUT:PB01 - // Only these pins can do TAMPER - if ( - #ifdef PIN_PB00 - alarm->pin != &pin_PB00 - #else - true - #endif - #ifdef PIN_PB02 - && alarm->pin != &pin_PB02 - #endif - && alarm->pin != &pin_PA02) { - mp_raise_ValueError(translate("Pin cannot wake from Deep Sleep")); - } - pinalarm_on = true; - SAMD_ALARM_FLAG |= SAMD_ALARM_FLAG_PIN; - - // Set tamper interrupt so deep sleep knows that's the intent - RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER; - common_hal_mcu_disable_interrupts(); - RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC - while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; - } - // TODO: map requested pin to limited selection of TAMPER pins - // PA02 is n=2: IN2, LVL2, etc... - RTC->MODE0.TAMPCTRL.bit.DEBNC2 = 1; // Edge triggered when INn is stable for 4 CLK_RTC_DEB periods - RTC->MODE0.TAMPCTRL.bit.TAMLVL2 = alarm->value; // rising or falling edge - RTC->MODE0.TAMPCTRL.bit.IN2ACT = 1; // WAKE on IN2 (doesn't save timestamp) - common_hal_mcu_enable_interrupts(); - RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER; - RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC - while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization - ; - } - // TODO: Set up deep sleep alarms. - // For deep sleep alarms, first check if the - // alarm pin value is valid for RTC->TAMPER. Ensure - // that the PULL and VALUE values are possible to - // implement with TAMPER, and throw value errors if - // not. - // A VM reset will occur before deep sleep - // starts, so either init these settings now and - // protect them with a `never_reset` function, or - // store them all in static variables and only - // actually implement the settings in - // `alarm_pin_pinalarm_prepare_for_deep_sleep` - // below. - } // use else-if here if RTC-TAMPER can wake from IDLE - else { - // Light sleep so turn on EIC channel - set_eic_handler(alarm->channel, EIC_HANDLER_ALARM); - turn_on_eic_channel(alarm->channel, EIC_CONFIG_SENSE0_RISE_Val); - } - - // TODO: Set up light sleep / fake deep sleep alarms. - // PORT/EVSYS should have more valid pin combinations - // than the TAMPER system. Check if there is a valid - // PORT/EVSYS combination, set it up, and reserve it with - // the tables at the start of this file so it can't be - // reused. Also set up IRQ callbacks and any other - // busywork. - - // TODO: Might want to reserve or otherwise interact with - // peripherals/sam_d5x/external_interrupts.c or events.c here - - // TODO: Even if an alarm is being set for deep sleep, it - // still needs to be able to wake from fake deep sleep, - // which is actually just like a light sleep. If the - // RTC Tamper IRQs are capable of waking from IDLE mode, - // this isn't a big deal, and there can be a strict - // if-else statement here. Otherwise, it will need to - // either set PORT and TAMPER IRQs simultaniously, or if that - // isn't possible, make a new function that can shunt fake deep - // sleep setup to common_hal_alarm_pretending_deep_sleep + if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + continue; } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + const mcu_pin_obj_t *pin = alarm->pin; + + set_eic_handler(pin->extint_channel, EIC_HANDLER_NO_INTERRUPT); + // Set sense = 0 or INTFLAG may be set on reset_pin_number. + configure_eic_channel(pin->extint_channel, EIC_CONFIG_SENSE0_NONE_Val); + turn_off_eic_channel(pin->extint_channel); + reset_pin_number(pin->number); + } +} + +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + trig_alarm = NULL; + if (deep_sleep) { + pinalarm_set_alarms_deep(n_alarms, alarms); + } else { + pinalarm_set_alarms_light(n_alarms, alarms); } } diff --git a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.h b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.h index f3cd4cf27b..57dc7d6c65 100644 --- a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.h +++ b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.h @@ -34,15 +34,16 @@ typedef struct { mp_obj_base_t base; const mcu_pin_obj_t *pin; bool value; + bool edge; bool pull; - uint8_t channel; } alarm_pin_pinalarm_obj_t; mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms); -mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void); +mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(uint32_t TAMPID); void pin_alarm_callback(uint8_t num); void alarm_pin_pinalarm_reset(void); +void alarm_pin_pinalarm_deinit_alarms(size_t n_alarms, const mp_obj_t *alarms); void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); void alarm_pin_pinalarm_prepare_for_deep_sleep(void); bool alarm_pin_pinalarm_woke_this_cycle(void); diff --git a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c index 49ce90a0b6..14a7346c17 100644 --- a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c @@ -26,26 +26,20 @@ #include "py/runtime.h" #include "hpl/pm/hpl_pm_base.h" -// #include #include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/time/__init__.h" #include "common-hal/alarm/__init__.h" #include "supervisor/port.h" -STATIC volatile bool woke_up; -STATIC uint32_t deep_sleep_ticks; -// TODO: replace timealarm_on with SAMD_ALARM_FLAG bit flags -STATIC volatile bool timealarm_on; +STATIC volatile bool woke_up = false; +STATIC mp_float_t wakeup_time; void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) { - // TODO: throw a ValueError if the input time exceeds the maximum - // value of the Compare register. This must be calculated from the - // setup values in port.c. Should be ~3 days. Give it some margin. - // - // UPDATE: for deep sleep at least, it's far more than 3 days since - // prescalar is set to 1024. (2^32)/32 seconds so >1500 days? - + // TODO: when issueing light/seep sleep, throw a ValueError if the + // time exceeds the maximum value. For light sleep, max = + // 2**32 / 16384 = 3 days. For deep sleep, max = 2**32 / 32 + // = 1550 days. self->monotonic_time = monotonic_time; } @@ -74,29 +68,30 @@ mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) { } void time_alarm_callback(void) { - if (timealarm_on) { - RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1; // clear flags + if (SAMD_ALARM_FLAG_TIME_CHK) { + RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1; + // SAMD_ALARM_FLAG_TIME_CLR; woke_up = true; - timealarm_on = false; } } bool alarm_time_timealarm_woke_this_cycle(void) { - if (timealarm_on && (((uint32_t)port_get_raw_ticks(NULL) << 4) > RTC->MODE0.COMP[1].reg)) { - woke_up = true; + if (SAMD_ALARM_FLAG_TIME_CHK) { + mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; + if (now_secs > wakeup_time) { + woke_up = true; + } } return woke_up; } void alarm_time_timealarm_reset(void) { - timealarm_on = false; + SAMD_ALARM_FLAG_TIME_CLR; woke_up = false; - SAMD_ALARM_FLAG &= ~SAMD_ALARM_FLAG_TIME; // clear flag + RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1; } void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { - // Turn on debug control - // RTC->MODE0.DBGCTRL.bit.DBGRUN = 1; // Search through alarms for TimeAlarm instances, and check that there's only one bool timealarm_set = false; alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL; @@ -114,55 +109,36 @@ void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ return; } - // Compute how long to actually sleep, considering the time now. - mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; - uint32_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - now_secs); - uint32_t wakeup_in_ticks = wakeup_in_secs * 1024; + // In the true deep sleep case, counter is set again based on + // wakeup_time in alarm_time_timealarm_prepare_for_deep_sleep. + wakeup_time = timealarm->monotonic_time; - // In the deep sleep case, we can't start the timer until the USB delay has finished - // (otherwise it will go off while USB enumerates, and we'll risk entering deep sleep - // without any way to wake up again) - if (deep_sleep) { - deep_sleep_ticks = wakeup_in_ticks; - } else { - deep_sleep_ticks = 0; - } - timealarm_on = true; - // Set COMP1 for fake sleep. This will be read and reset for real deep sleep anyways. - // RTC->MODE0.COMP[1].reg = wakeup_in_ticks; - RTC->MODE0.COMP[1].reg = ((uint32_t)port_get_raw_ticks(NULL) + wakeup_in_ticks) << 4; + // Compute how long to actually sleep, considering the time now. + // At least 1 count = 1/16384 sec is necessary. + mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; + uint32_t wakeup_in_counts = MAX(1, (uint32_t)((wakeup_time - now_secs) * 16384)); + + + SAMD_ALARM_FLAG_TIME_SET; + RTC->MODE0.COMP[1].reg = RTC->MODE0.COUNT.reg + wakeup_in_counts; while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) { } RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP1; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP1; - SAMD_ALARM_FLAG |= SAMD_ALARM_FLAG_TIME; // set TimeAlarm flag - - // This is set for fake sleep. Max fake sleep time is ~72 hours - // True deep sleep isn't limited by this - // port_interrupt_after_ticks(wakeup_in_ticks); - // printf("second t %lu, cmp0 %lu, cmp1 %lu\n", (uint32_t)port_get_raw_ticks(NULL),RTC->MODE0.COMP[0].reg,RTC->MODE0.COMP[1].reg); - // TODO: set up RTC->COMP[1] and create a callback pointing to - // time_alarm_callback. See atmel-samd/supervisor/port.c -> _port_interrupt_after_ticks() - // for how to set this up. I don't know how you do the callback, though. You MUST use - // COMP[1], since port.c uses COMP[0] already and needs to set that for - // things like the USB enumeration delay. - - // If true deep sleep is called, it will either ignore or overwrite the above setup depending on - // whether it is shorter or longer than the USB delay - // printf("set deep alarm finished\n"); - } void alarm_time_timealarm_prepare_for_deep_sleep(void) { - if (deep_sleep_ticks) { - // TODO: set up RTC->COMP[1] again, since it needs to start AFTER the USB enumeration delay. - // Just do the exact same setup as alarm_time_timealarm_set_alarms(). Note, this - // is used for both fake and real deep sleep, so it still needs the callback. - // See STM32 for reference. + // set up RTC->COMP[1] again, since it needs to start AFTER the USB enumeration delay. + // Just do the exact same setup as alarm_time_timealarm_set_alarms(). Note, this + // is used for both fake and real deep sleep, so it still needs the callback. + // See STM32 for reference. + // + // In deep sleep mode, prescaler is set to 1024, so that 1 count = 1/32 s. + // At least 1 count is necessary. - RTC->MODE0.COMP[1].reg = deep_sleep_ticks; - while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) { - } - deep_sleep_ticks = 0; + mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; + uint32_t wakeup_in_counts = MAX(1, (uint32_t)((wakeup_time - now_secs) * 32)); + RTC->MODE0.COMP[1].reg = wakeup_in_counts; + while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) { } }