diff --git a/ports/atmel-samd/common-hal/alarm/__init__.c b/ports/atmel-samd/common-hal/alarm/__init__.c index 09177f389f..fa1a75ef6f 100644 --- a/ports/atmel-samd/common-hal/alarm/__init__.c +++ b/ports/atmel-samd/common-hal/alarm/__init__.c @@ -66,7 +66,7 @@ samd_sleep_source_t alarm_get_wakeup_cause(void) { if (alarm_time_timealarm_woke_this_cycle()) { return SAMD_WAKEUP_RTC; } - if (RSTC->RCAUSE.bit.BACKUP) { + if (!fake_sleep && RSTC->RCAUSE.bit.BACKUP) { // not able to detect PinAlarm wake since registers are getting reset // TODO: come up with a way to detect a TAMPER if (RTC->MODE0.TAMPID.reg || RTC->MODE0.INTFLAG.bit.TAMPER) { @@ -192,6 +192,7 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) { (void)__get_FPSCR(); } + // TODO: Be able to set PinAlarm and TimeAlarm together // PinAlarm (hacky way of checking if time alarm or pin alarm) if (RTC->MODE0.INTENSET.bit.TAMPER) { // Disable interrupts @@ -274,7 +275,7 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) { } } -MP_NOINLINE void common_hal_alarm_pretending_deep_sleep(void) { +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 @@ -286,8 +287,6 @@ MP_NOINLINE void common_hal_alarm_pretending_deep_sleep(void) { ; } fake_sleep = true; - } else { - port_idle_until_interrupt(); } } diff --git a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c index 08fff6df9f..0ec606813d 100644 --- a/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c @@ -38,8 +38,9 @@ // 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 bool woke_up; -STATIC bool deep_wkup_enabled; +STATIC volatile bool woke_up; +// TODO: replace pinalarm_on with SAMD_ALARM_FLAG bit flags +STATIC volatile bool pinalarm_on; // TODO: Create tables here reserving IRQ instances, and for the IRQ // callback to store what pin triggered the interrupt @@ -55,12 +56,16 @@ void pin_alarm_callback(uint8_t num) { // parameters can be changed // Turn off interrupts while in handler // printf("Woke up from pin!!\n"); // printf("EIC Flags: %lu\n",EIC->INTFLAG.reg); - - // 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; + if (pinalarm_on) { + // clear flag and interrupt setting + 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; + } } 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) { @@ -89,9 +94,13 @@ void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, con gpio_set_pin_function(pin->number, GPIO_PIN_FUNCTION_A); if (self->pull) { - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_UP); - } else { - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_DOWN); + 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); @@ -107,7 +116,6 @@ 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) { - // TODO: is SAMD edge or level only? return true; } @@ -116,9 +124,8 @@ bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { } bool alarm_pin_pinalarm_woke_this_cycle(void) { - if (RTC->MODE0.INTFLAG.bit.TAMPER) { + if (pinalarm_on && RTC->MODE0.INTFLAG.bit.TAMPER) { woke_up = true; - RTC->MODE0.INTENCLR.bit.TAMPER = 1; // clear flag and interrupt setting } return woke_up; } @@ -135,9 +142,6 @@ mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t // 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. - - - } // Return nothing if no matching alarms are found. return mp_const_none; @@ -159,9 +163,22 @@ void alarm_pin_pinalarm_reset(void) { // 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; + woke_up = false; // Disable TAMPER interrupt RTC->MODE0.INTENCLR.bit.TAMPER = 1; + // 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 + ; + } + 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(); } void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { @@ -177,9 +194,25 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob alarm->pin != &pin_PA02) { mp_raise_ValueError(translate("Pin cannot wake from Deep Sleep")); } - deep_wkup_enabled = true; + pinalarm_on = true; // 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 = 0x1; // 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 diff --git a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c index 11e12d0990..43b9eaefa7 100644 --- a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c @@ -27,7 +27,6 @@ #include "py/runtime.h" #include "hpl/pm/hpl_pm_base.h" // #include -// #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/time/__init__.h" @@ -35,6 +34,8 @@ STATIC volatile bool woke_up; STATIC uint32_t deep_sleep_ticks; +// TODO: replace timealarm_on with SAMD_ALARM_FLAG bit flags +STATIC bool timealarm_on; 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 @@ -72,21 +73,28 @@ mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) { } void timer_callback(void) { - RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1 | RTC_MODE0_INTENCLR_CMP0 | RTC_MODE0_INTENCLR_OVF; // clear flags - woke_up = true; + if (timealarm_on) { + RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1 | RTC_MODE0_INTENCLR_CMP0 | RTC_MODE0_INTENCLR_OVF; // clear flags + 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; + } return woke_up; } void alarm_time_timealarm_reset(void) { + timealarm_on = false; woke_up = false; } 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; + // 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; @@ -117,16 +125,19 @@ void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ } else { deep_sleep_ticks = 0; } - // Set COMP1 for fake sleep. This will be reset for real deep sleep anyways. - RTC->MODE0.COMP[1].reg = wakeup_in_ticks; - while (RTC->MODE0.SYNCBUSY.reg) { - ; + 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; + 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; // 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); - + // 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 // timer_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 @@ -146,9 +157,9 @@ void alarm_time_timealarm_prepare_for_deep_sleep(void) { // is used for both fake and real deep sleep, so it still needs the callback. // See STM32 for reference. - // RTC->MODE0.COMP[1].reg = deep_sleep_ticks; - // while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) { - // } + RTC->MODE0.COMP[1].reg = deep_sleep_ticks; + while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) { + } deep_sleep_ticks = 0; } } diff --git a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.h b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.h index 45506ce71f..7b1593488d 100644 --- a/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.h +++ b/ports/atmel-samd/common-hal/alarm/time/TimeAlarm.h @@ -38,9 +38,9 @@ mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void); void timer_callback(void); bool alarm_time_timealarm_woke_this_cycle(void); -uint32_t alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); void alarm_time_timealarm_reset(void); -uint32_t alarm_time_timealarm_prepare_for_deep_sleep(void); +void alarm_time_timealarm_prepare_for_deep_sleep(void); #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM_TIMEALARM_H diff --git a/ports/atmel-samd/eic_handler.h b/ports/atmel-samd/eic_handler.h index 08da7ea34d..d73ef2a0f7 100644 --- a/ports/atmel-samd/eic_handler.h +++ b/ports/atmel-samd/eic_handler.h @@ -31,6 +31,7 @@ #define EIC_HANDLER_INCREMENTAL_ENCODER 0x2 #define EIC_HANDLER_PS2 0x3 #define EIC_HANDLER_COUNTER 0x04 +#define EIC_HANDLER_ALARM 0x05 void set_eic_handler(uint8_t channel, uint8_t eic_handler); void shared_eic_handler(uint8_t channel); diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 48c4239ee7..09f7b024c4 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -79,6 +79,7 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/alarm/pin/PinAlarm.h" #include "shared_timers.h" #include "reset.h" @@ -490,6 +491,16 @@ void RTC_Handler(void) { // Do things common to all ports when the tick occurs supervisor_tick(); } + if (intflag & RTC_MODE0_INTFLAG_CMP1) { + // Likely TimeAlarm fake sleep wake + timer_callback(); + RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP1; + } + if (intflag & RTC_MODE0_INTFLAG_TAMPER) { + // Likely PinAlarm fake sleep wake + pin_alarm_callback(1); // TODO: set channel? + RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_TAMPER; + } #endif if (intflag & RTC_MODE0_INTFLAG_CMP0) { // Clear the interrupt because we may have hit a sleep @@ -498,11 +509,6 @@ void RTC_Handler(void) { // SAMD21 ticks are handled by EVSYS #ifdef SAM_D5X_E5X RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0; - // Check if we're sleeping - if (SAMD_ALARM_FLAG) { - timer_callback(); - SAMD_ALARM_FLAG = 0; - } #endif } }