Fix atmel-samd alarm module.

This commit is contained in:
Tsutomu IKEGAMI 2022-02-17 23:00:26 +09:00
parent b2e77bd11a
commit bd9dd3dc63
9 changed files with 406 additions and 362 deletions

View File

@ -355,6 +355,7 @@ msgstr ""
msgid "64 bit types" msgid "64 bit types"
msgstr "" 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/alarm/pin/PinAlarm.c
#: ports/atmel-samd/common-hal/countio/Counter.c #: ports/atmel-samd/common-hal/countio/Counter.c
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c #: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
@ -1682,6 +1683,7 @@ msgstr ""
msgid "No hardware support on clk pin" msgid "No hardware support on clk pin"
msgstr "" 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/alarm/pin/PinAlarm.c
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c
@ -1827,6 +1829,7 @@ msgstr ""
msgid "Only one address is allowed" msgid "Only one address is allowed"
msgstr "" msgstr ""
#: ports/atmel-samd/common-hal/alarm.orig/time/TimeAlarm.c
#: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c #: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c
#: ports/espressif/common-hal/alarm/time/TimeAlarm.c #: ports/espressif/common-hal/alarm/time/TimeAlarm.c
#: ports/nrf/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c
@ -1898,6 +1901,7 @@ msgstr ""
msgid "Permission denied" msgid "Permission denied"
msgstr "" 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/alarm/pin/PinAlarm.c
#: ports/stm/common-hal/alarm/pin/PinAlarm.c #: ports/stm/common-hal/alarm/pin/PinAlarm.c
msgid "Pin cannot wake from Deep Sleep" msgid "Pin cannot wake from Deep Sleep"
@ -1931,6 +1935,10 @@ msgstr ""
msgid "Pin is input only" msgid "Pin is input only"
msgstr "" msgstr ""
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
msgid "Pin is not free"
msgstr ""
#: ports/raspberrypi/common-hal/countio/Counter.c #: ports/raspberrypi/common-hal/countio/Counter.c
msgid "Pin must be on PWM Channel B" msgid "Pin must be on PWM Channel B"
msgstr "" msgstr ""
@ -2158,7 +2166,7 @@ msgstr ""
msgid "Size not supported" msgid "Size not supported"
msgstr "" 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 #: ports/raspberrypi/common-hal/alarm/SleepMemory.c
msgid "Sleep Memory not available" msgid "Sleep Memory not available"
msgstr "" msgstr ""
@ -2319,6 +2327,7 @@ msgstr ""
msgid "Total data to write is larger than %q" msgid "Total data to write is larger than %q"
msgstr "" msgstr ""
#: ports/atmel-samd/common-hal/alarm.orig/touch/TouchAlarm.c
#: ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c #: ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c
#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c
#: ports/stm/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" msgid "Unhandled ESP TLS error %d %d %x %d"
msgstr "" msgstr ""
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
msgid "Unknown error"
msgstr ""
#: shared-bindings/wifi/Radio.c #: shared-bindings/wifi/Radio.c
#, c-format #, c-format
msgid "Unknown failure %d" msgid "Unknown failure %d"
@ -3630,7 +3643,7 @@ msgstr ""
msgid "matrix is not positive definite" msgid "matrix is not positive definite"
msgstr "" msgstr ""
#: shared-bindings/wifi/Radio.c #: ports/espressif/common-hal/wifi/Radio.c
msgid "max_connections must be between 0 and 10" msgid "max_connections must be between 0 and 10"
msgstr "" msgstr ""
@ -4048,6 +4061,7 @@ msgstr ""
#: ports/espressif/boards/adafruit_metro_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_metro_esp32s2/mpconfigboard.h
#: ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h
#: ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/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_esp32-c3s/mpconfigboard.h
#: ports/espressif/boards/ai_thinker_esp_12k_nodemcu/mpconfigboard.h #: ports/espressif/boards/ai_thinker_esp_12k_nodemcu/mpconfigboard.h
#: ports/espressif/boards/artisense_rd00/mpconfigboard.h #: ports/espressif/boards/artisense_rd00/mpconfigboard.h
@ -4055,6 +4069,7 @@ msgstr ""
#: ports/espressif/boards/crumpspace_crumps2/mpconfigboard.h #: ports/espressif/boards/crumpspace_crumps2/mpconfigboard.h
#: ports/espressif/boards/electroniccats_bastwifi/mpconfigboard.h #: ports/espressif/boards/electroniccats_bastwifi/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32c3_devkitm_1_n4/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_box/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h
#: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8r2/mpconfigboard.h #: ports/espressif/boards/espressif_esp32s3_devkitc_1_n8r2/mpconfigboard.h

View File

@ -32,6 +32,7 @@
#include "shared-module/displayio/__init__.h" #include "shared-module/displayio/__init__.h"
#include "shared-module/displayio/mipi_constants.h" #include "shared-module/displayio/mipi_constants.h"
#include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/digitalio/DigitalInOut.h"
#include "common-hal/alarm/__init__.h"
displayio_fourwire_obj_t board_display_obj; displayio_fourwire_obj_t board_display_obj;
digitalio_digitalinout_obj_t CTR_5V; digitalio_digitalinout_obj_t CTR_5V;
@ -139,4 +140,14 @@ void reset_board(void) {
} }
void board_deinit(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);
} }

View File

@ -32,20 +32,24 @@
#include "shared-bindings/nvm/ByteArray.h" #include "shared-bindings/nvm/ByteArray.h"
void alarm_sleep_memory_reset(void) { void alarm_sleep_memory_reset(void) {
} }
uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) {
mp_raise_NotImplementedError(translate("Sleep Memory not available")); return BKUPRAM_SIZE;
return 0;
} }
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) { 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")); if (start_index + len > BKUPRAM_SIZE) {
return false; 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) { 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; return;
} }

View File

@ -31,8 +31,6 @@
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
uint8_t *start_address;
uint8_t len;
} alarm_sleep_memory_obj_t; } alarm_sleep_memory_obj_t;
extern void alarm_sleep_memory_reset(void); extern void alarm_sleep_memory_reset(void);

View File

@ -39,62 +39,50 @@
#include "supervisor/port.h" #include "supervisor/port.h"
#include "supervisor/workflow.h" #include "supervisor/workflow.h"
STATIC uint32_t TAMPID = 0;
// Singleton instance of SleepMemory. // Singleton instance of SleepMemory.
const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
.base = { .base = {
.type = &alarm_sleep_memory_type, .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) { void alarm_reset(void) {
// Reset the alarm flag // Reset the alarm flag
SAMD_ALARM_FLAG = 0x00;
alarm_pin_pinalarm_reset(); alarm_pin_pinalarm_reset();
alarm_time_timealarm_reset(); alarm_time_timealarm_reset();
} }
samd_sleep_source_t alarm_get_wakeup_cause(void) { void alarm_get_wakeup_cause(void) {
// If in light/fake sleep, check modules // Called from rtc_init, just before SWRST of RTC. It is called
if (alarm_pin_pinalarm_woke_this_cycle()) { // at an early stage of main(), to save TAMPID from SWRST. Later,
return SAMD_WAKEUP_GPIO; // common_hal_alarm_create_wake_alarm is called to make a wakeup
} // alarm from the deep sleep.
if (alarm_time_timealarm_woke_this_cycle()) {
return SAMD_WAKEUP_RTC; TAMPID = RTC->MODE0.TAMPID.reg;
}
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;
} }
bool common_hal_alarm_woken_from_sleep(void) { 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) { mp_obj_t common_hal_alarm_create_wake_alarm(void) {
// If woken from deep sleep, create a copy alarm similar to what would have // Called from main.c on the first start up, just before alarm_reset.
// been passed in originally. Otherwise, just return none // Return a copy of wakeup alarm from deep sleep / fake deep sleep.
samd_sleep_source_t cause = alarm_get_wakeup_cause(); // In case of fake sleep, status should be left in TimeAlarm/PinAlarm.
switch (cause) { bool true_deep = RSTC->RCAUSE.bit.BACKUP;
case SAMD_WAKEUP_RTC: {
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(); return alarm_time_timealarm_create_wakeup_alarm();
} }
case SAMD_WAKEUP_GPIO: { if (true_deep) {
return alarm_pin_pinalarm_create_wakeup_alarm(); return alarm_pin_pinalarm_create_wakeup_alarm(TAMPID);
}
case SAMD_WAKEUP_UNDEF:
default:
// Not a deep sleep reset.
break;
} }
return mp_const_none; 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) { 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_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms);
alarm_time_timealarm_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) { 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); _setup_sleep_alarms(false, n_alarms, alarms);
mp_obj_t wake_alarm = mp_const_none; 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()) { while (!mp_hal_is_interrupted()) {
RUN_BACKGROUND_TASKS; RUN_BACKGROUND_TASKS;
// Detect if interrupt was alarm or ctrl-C interrupt. // Detect if interrupt was alarm or ctrl-C interrupt.
if (common_hal_alarm_woken_from_sleep()) { if (alarm_time_timealarm_woke_this_cycle()) {
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); wake_alarm = alarm_time_timealarm_find_triggered_alarm(n_alarms,alarms);
break; break;
} }
case SAMD_WAKEUP_GPIO: { if (alarm_pin_pinalarm_woke_this_cycle()) {
wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms); wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms);
break; break;
} }
default:
// Should not reach this, if all light sleep types are covered correctly
break;
}
shared_alarm_save_wake_alarm(wake_alarm);
break;
}
// ATTEMPT ------------------------------
// This works but achieves same power consumption as time.sleep()
// Clear the FPU interrupt because it can prevent us from sleeping. // Clear the FPU interrupt because it can prevent us from sleeping.
if (__get_FPSCR() & ~(0x9f)) { 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(); (void)__get_FPSCR();
} }
// Disable RTC interrupts common_hal_mcu_disable_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) {
;
}
__DSB(); // Data Synchronization Barrier __DSB(); // Data Synchronization Barrier
__WFI(); // Wait For Interrupt __WFI(); // Wait For Interrupt
// Enable RTC interrupts common_hal_mcu_enable_interrupts();
NVIC_EnableIRQ(RTC_IRQn);
// END ATTEMPT ------------------------------
} }
// 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()) { if (mp_hal_is_interrupted()) {
return mp_const_none; // Shouldn't be given to python code because exception handling should kick in. return mp_const_none; // Shouldn't be given to python code because exception handling should kick in.
} }
alarm_reset();
return wake_alarm; 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) { void NORETURN common_hal_alarm_enter_deep_sleep(void) {
alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_pin_pinalarm_prepare_for_deep_sleep();
alarm_time_timealarm_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 // Set pin state before the deep sleep, to turn off devices and reduce power.
uint32_t _SAMD_ALARM_FLAG = SAMD_ALARM_FLAG; // 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. // Clear the FPU interrupt because it can prevent us from sleeping.
if (__get_FPSCR() & ~(0x9f)) { if (__get_FPSCR() & ~(0x9f)) {
@ -183,57 +169,42 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) {
(void)__get_FPSCR(); (void)__get_FPSCR();
} }
NVIC_DisableIRQ(RTC_IRQn); common_hal_mcu_disable_interrupts();
// Must disable the RTC before writing to EVCTRL and TMPCTRL // 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 RTC->MODE0.CTRLA.bit.SWRST = 1; // Software reset the RTC
while (RTC->MODE0.SYNCBUSY.bit.SWRST) { // Wait for synchronization 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.reg = RTC_MODE0_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024
RTC_MODE0_CTRLA_MODE_COUNT32; // Set RTC to mode 0, 32-bit timer RTC_MODE0_CTRLA_MODE_COUNT32; // Set RTC to mode 0, 32-bit timer
SAMD_ALARM_FLAG = _flag;
// Check if we're setting TimeAlarm // Check if we're setting TimeAlarm
if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_TIME) { if (SAMD_ALARM_FLAG_TIME_CHK) {
RTC->MODE0.COMP[1].reg = (_target / 1024) * 32; RTC->MODE0.COMP[1].reg = _target;
while (RTC->MODE0.SYNCBUSY.reg) { 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; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP1;
} }
if (_SAMD_ALARM_FLAG & SAMD_ALARM_FLAG_PIN) { // Check if we're setting PinAlarm
// Set interrupts for TAMPER pins if (SAMD_ALARM_FLAG_PIN_CHK) {
RTC->MODE0.TAMPCTRL.reg = _tampctrl;
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER;
} }
// Enable interrupts
common_hal_mcu_enable_interrupts();
// Set-up Deep Sleep Mode & RAM retention // 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 PM->BKUPCFG.reg = PM_BKUPCFG_BRAMCFG(0x2); // No RAM retention 0x2 partial:0x1
while (PM->BKUPCFG.bit.BRAMCFG != 0x2) { // Wait for synchronization while (PM->BKUPCFG.bit.BRAMCFG != 0x2) { // Wait for synchronization
;
} }
#endif
PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_BACKUP; PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_BACKUP;
while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val) { while (PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val) {
;
} }
RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC
while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization
;
} }
__DSB(); // Data Synchronization Barrier __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) { void common_hal_alarm_pretending_deep_sleep(void) {
// TODO: // RTC is already be furnished by common_hal_alarm_set_deep_sleep_alarms.
// If tamper detect interrupts cannot be used to wake from the Idle tier of sleep, // fake_sleep = true;
// This section will need to re-initialize the pins to allow the PORT peripheral
// to generate external interrupts again. See STM32 for reference.
if (!fake_sleep) { board_deep_sleep_hook();
fake_sleep = true; port_idle_until_interrupt();
}
} }
#endif
void common_hal_alarm_gc_collect(void) { void common_hal_alarm_gc_collect(void) {
gc_collect_ptr(shared_alarm_get_wake_alarm()); gc_collect_ptr(shared_alarm_get_wake_alarm());
} }
MP_WEAK void board_deep_sleep_hook(void) {
}

View File

@ -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 (RTC->MODE0.BKUP[0].reg)
#define SAMD_ALARM_FLAG_TIME (_U_(0x1) << 0) #define SAMD_ALARM_FLAG_TIME (_U_(0x1) << 0)
#define SAMD_ALARM_FLAG_PIN (_U_(0x1) << 1) #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 #endif
typedef enum { typedef enum {
@ -46,7 +54,8 @@ typedef enum {
} samd_sleep_source_t; } samd_sleep_source_t;
extern void alarm_set_wakeup_reason(samd_sleep_source_t reason); 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 alarm_reset(void);
extern void board_deep_sleep_hook(void);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM__INIT__H #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM__INIT__H

View File

@ -38,70 +38,61 @@
// This variable stores whether a PinAlarm woke in light sleep or fake deep sleep // This variable stores whether a PinAlarm woke in light sleep or fake deep sleep
// It CANNOT detect if the program woke from deep sleep. // It CANNOT detect if the program woke from deep sleep.
STATIC volatile bool woke_up; STATIC volatile bool woke_up = false;
// TODO: replace pinalarm_on with SAMD_ALARM_FLAG bit flags STATIC alarm_pin_pinalarm_obj_t *trig_alarm = NULL;
STATIC volatile bool pinalarm_on;
// TODO: Create tables here reserving IRQ instances, and for the IRQ // Tamper Pins for deep sleep: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01;
// callback to store what pin triggered the interrupt // wakeup from deep sleep seems to be triggered when these pins go from Hi/Lo to Hi-Z state.
// STATIC bool reserved_alarms[SOME_VAL]; typedef struct {
// STATIC uint16_t triggered_pins[SOME_VAL]; int n;
const mcu_pin_obj_t *pin;
} samd_tamper_pin_t;
void pin_alarm_callback(uint8_t num) { // parameters can be changed static samd_tamper_pin_t TAMPER_PINS[] = {
// TODO: This is responsible for resetting the IRQ (so it doesn't #if defined(PIN_PB00) && !defined(IGNORE_PIN_PB00)
// go off constantly, and recording what pin was responsible for { 0, &pin_PB00 },
// the trigger. This will only work for light sleep/fake deep #endif
// sleep, in conjunction with the find_triggered_alarm function #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) { void pin_alarm_callback(uint8_t num) {
// clear flag and interrupt setting // called back with EIC/RTC interrupt
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_TAMPER; if (!SAMD_ALARM_FLAG_PIN_CHK) {
pinalarm_on = false; return;
// 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; 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;
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) { 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->pin = pin;
self->value = value; self->value = value;
self->edge = edge;
self->pull = pull; 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) { 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) { 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) { 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) { bool alarm_pin_pinalarm_woke_this_cycle(void) {
if (pinalarm_on && RTC->MODE0.INTFLAG.bit.TAMPER) {
woke_up = true;
}
return woke_up; 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; continue;
} }
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
(void)alarm; if (alarm == trig_alarm) {
return alarms[i];
}
// 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; return mp_const_none;
} }
mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void) { 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_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t);
alarm->base.type = &alarm_pin_pinalarm_type; alarm->base.type = &alarm_pin_pinalarm_type;
// TODO: Extract information about what pin woke the device. alarm->pin = t->pin;
// 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; return alarm;
} }
}
return mp_const_none;
}
void alarm_pin_pinalarm_reset(void) { void alarm_pin_pinalarm_reset(void) {
// TODO: this is responsible for resetting ALL pinalarms. Make SAMD_ALARM_FLAG_PIN_CLR;
// 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; woke_up = false;
SAMD_ALARM_FLAG &= ~SAMD_ALARM_FLAG_PIN; // clear flag
// Disable TAMPER interrupt // Disable TAMPER interrupt
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_TAMPER; RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_TAMPER;
// Disable TAMPER control if (RTC->MODE0.TAMPCTRL.reg != 0) {
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC
while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization
;
} }
RTC->MODE0.TAMPCTRL.reg = 0; // reset everything RTC->MODE0.TAMPCTRL.reg = 0; // reset everything
RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC
while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization
;
} }
common_hal_mcu_enable_interrupts(); common_hal_mcu_enable_interrupts();
} }
}
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { #define PINALARM_NOERR 0
// The outer layer of this loop simply checks if there are any pin #define PINALARM_ERR_NOT_FREE -1
// alarms in the parameter array #define PINALARM_ERR_NOEXTINT -2
for (size_t i = 0; i < n_alarms; i++) { #define PINALARM_ERR_NOCHANNEL -3
if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { #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]); alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
gpio_set_pin_function(alarm->pin->number, GPIO_PIN_FUNCTION_A); const mcu_pin_obj_t *pin = alarm->pin;
if (alarm->pull) {
if (alarm->value) { if (!pin_number_is_free(pin->number)) {
// detect rising edge means pull down err = PINALARM_ERR_NOT_FREE;
gpio_set_pin_pull_mode(alarm->pin->number, GPIO_PULL_DOWN); 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 { } 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 // detect falling edge means pull up
gpio_set_pin_pull_mode(alarm->pin->number, GPIO_PULL_UP); gpio_set_pin_pull_mode(pin->number, GPIO_PULL_UP);
} }
} }
if (deep_sleep) {
// Tamper Pins: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; OUT:PB01 set_eic_channel_data(pin->extint_channel, (void *)alarm);
// Only these pins can do TAMPER claim_pin(pin);
if ( set_eic_handler(pin->extint_channel, EIC_HANDLER_ALARM);
#ifdef PIN_PB00 turn_on_eic_channel(pin->extint_channel, sense);
alarm->pin != &pin_PB00 }
#else
true if (err == PINALARM_NOERR) {
#endif return;
#ifdef PIN_PB02 }
&& alarm->pin != &pin_PB02
#endif SAMD_ALARM_FLAG_PIN_CLR;
&& alarm->pin != &pin_PA02) { 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")); 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 // 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; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER;
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC
while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization
;
} }
// TODO: map requested pin to limited selection of TAMPER pins RTC->MODE0.TAMPCTRL.reg = tampctrl;
// 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 RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC
while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization while (RTC->MODE0.SYNCBUSY.bit.ENABLE) { // Wait for synchronization
;
} }
// TODO: Set up deep sleep alarms. common_hal_mcu_enable_interrupts();
// 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. void alarm_pin_pinalarm_deinit_alarms(size_t n_alarms, const mp_obj_t *alarms) {
// PORT/EVSYS should have more valid pin combinations for (size_t i = 0; i < n_alarms; i++) {
// than the TAMPER system. Check if there is a valid if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
// PORT/EVSYS combination, set it up, and reserve it with continue;
// 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
} }
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);
} }
} }

View File

@ -34,15 +34,16 @@ typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
const mcu_pin_obj_t *pin; const mcu_pin_obj_t *pin;
bool value; bool value;
bool edge;
bool pull; bool pull;
uint8_t channel;
} alarm_pin_pinalarm_obj_t; } 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_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 pin_alarm_callback(uint8_t num);
void alarm_pin_pinalarm_reset(void); 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_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
void alarm_pin_pinalarm_prepare_for_deep_sleep(void); void alarm_pin_pinalarm_prepare_for_deep_sleep(void);
bool alarm_pin_pinalarm_woke_this_cycle(void); bool alarm_pin_pinalarm_woke_this_cycle(void);

View File

@ -26,26 +26,20 @@
#include "py/runtime.h" #include "py/runtime.h"
#include "hpl/pm/hpl_pm_base.h" #include "hpl/pm/hpl_pm_base.h"
// #include <stdio.h>
#include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/alarm/time/TimeAlarm.h"
#include "shared-bindings/time/__init__.h" #include "shared-bindings/time/__init__.h"
#include "common-hal/alarm/__init__.h" #include "common-hal/alarm/__init__.h"
#include "supervisor/port.h" #include "supervisor/port.h"
STATIC volatile bool woke_up; STATIC volatile bool woke_up = false;
STATIC uint32_t deep_sleep_ticks; STATIC mp_float_t wakeup_time;
// TODO: replace timealarm_on with SAMD_ALARM_FLAG bit flags
STATIC volatile bool timealarm_on;
void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_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 // TODO: when issueing light/seep sleep, throw a ValueError if the
// value of the Compare register. This must be calculated from the // time exceeds the maximum value. For light sleep, max =
// setup values in port.c. Should be ~3 days. Give it some margin. // 2**32 / 16384 = 3 days. For deep sleep, max = 2**32 / 32
// // = 1550 days.
// 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?
self->monotonic_time = monotonic_time; self->monotonic_time = monotonic_time;
} }
@ -74,29 +68,30 @@ mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) {
} }
void time_alarm_callback(void) { void time_alarm_callback(void) {
if (timealarm_on) { if (SAMD_ALARM_FLAG_TIME_CHK) {
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1; // clear flags RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP1;
// SAMD_ALARM_FLAG_TIME_CLR;
woke_up = true; woke_up = true;
timealarm_on = false;
} }
} }
bool alarm_time_timealarm_woke_this_cycle(void) { bool alarm_time_timealarm_woke_this_cycle(void) {
if (timealarm_on && (((uint32_t)port_get_raw_ticks(NULL) << 4) > RTC->MODE0.COMP[1].reg)) { 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; woke_up = true;
} }
}
return woke_up; return woke_up;
} }
void alarm_time_timealarm_reset(void) { void alarm_time_timealarm_reset(void) {
timealarm_on = false; SAMD_ALARM_FLAG_TIME_CLR;
woke_up = false; 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) { 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 // Search through alarms for TimeAlarm instances, and check that there's only one
bool timealarm_set = false; bool timealarm_set = false;
alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL; 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; return;
} }
// Compute how long to actually sleep, considering the time now. // In the true deep sleep case, counter is set again based on
mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; // wakeup_time in alarm_time_timealarm_prepare_for_deep_sleep.
uint32_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - now_secs); wakeup_time = timealarm->monotonic_time;
uint32_t wakeup_in_ticks = wakeup_in_secs * 1024;
// In the deep sleep case, we can't start the timer until the USB delay has finished // Compute how long to actually sleep, considering the time now.
// (otherwise it will go off while USB enumerates, and we'll risk entering deep sleep // At least 1 count = 1/16384 sec is necessary.
// without any way to wake up again) mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f;
if (deep_sleep) { uint32_t wakeup_in_counts = MAX(1, (uint32_t)((wakeup_time - now_secs) * 16384));
deep_sleep_ticks = wakeup_in_ticks;
} else {
deep_sleep_ticks = 0; SAMD_ALARM_FLAG_TIME_SET;
} RTC->MODE0.COMP[1].reg = RTC->MODE0.COUNT.reg + wakeup_in_counts;
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) { while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) {
} }
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP1; RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP1;
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_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) { void alarm_time_timealarm_prepare_for_deep_sleep(void) {
if (deep_sleep_ticks) { // set up RTC->COMP[1] again, since it needs to start AFTER the USB enumeration delay.
// 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 // 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. // is used for both fake and real deep sleep, so it still needs the callback.
// See STM32 for reference. // 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; 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) { while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) {
} }
deep_sleep_ticks = 0;
}
} }