Merge pull request #6 from maholli/samd-sleep-v1
cherry-picked original hierophect commit and merged with my samd `alarm` (deep sleep) work
This commit is contained in:
commit
9f0cb0e0c5
@ -12,15 +12,25 @@ LONGINT_IMPL = MPZ
|
|||||||
|
|
||||||
CIRCUITPY_DRIVE_LABEL = "PYCUBED"
|
CIRCUITPY_DRIVE_LABEL = "PYCUBED"
|
||||||
|
|
||||||
# Not needed.
|
CIRCUITPY_ULAB = 1
|
||||||
|
CIRCUITPY_BINASCII = 1
|
||||||
|
CIRCUITPY_SDCARDIO = 1
|
||||||
|
CIRCUITPY_JSON = 1
|
||||||
|
CIRCUITPY_MSGPACK = 1
|
||||||
|
CIRCUITPY_ALARM = 1
|
||||||
|
|
||||||
|
# no SAMD51 support... yet ;)
|
||||||
|
# CIRCUITPY_DUALBANK=1
|
||||||
|
|
||||||
|
# Not needed
|
||||||
CIRCUITPY_AUDIOBUSIO = 0
|
CIRCUITPY_AUDIOBUSIO = 0
|
||||||
CIRCUITPY_BITMAPTOOLS = 0
|
|
||||||
CIRCUITPY_DISPLAYIO = 0
|
CIRCUITPY_DISPLAYIO = 0
|
||||||
CIRCUITPY_FRAMEBUFFERIO = 0
|
CIRCUITPY_FRAMEBUFFERIO = 0
|
||||||
CIRCUITPY_KEYPAD = 0
|
CIRCUITPY_KEYPAD = 0
|
||||||
CIRCUITPY_RGBMATRIX = 0
|
CIRCUITPY_RGBMATRIX = 0
|
||||||
CIRCUITPY_PS2IO = 0
|
CIRCUITPY_PS2IO = 0
|
||||||
|
CIRCUITPY_BLEIO_HCI=0
|
||||||
|
CIRCUITPY_BLEIO=0
|
||||||
|
|
||||||
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
|
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
|
||||||
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register
|
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register
|
||||||
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD
|
|
||||||
|
@ -11,14 +11,16 @@ EXTERNAL_FLASH_DEVICE_COUNT = 1
|
|||||||
EXTERNAL_FLASH_DEVICES = W25Q80DV
|
EXTERNAL_FLASH_DEVICES = W25Q80DV
|
||||||
LONGINT_IMPL = MPZ
|
LONGINT_IMPL = MPZ
|
||||||
|
|
||||||
|
CIRCUITPY_DRIVE_LABEL = "PYCUBED"
|
||||||
|
|
||||||
CIRCUITPY_ULAB = 1
|
CIRCUITPY_ULAB = 1
|
||||||
CIRCUITPY_BINASCII = 1
|
CIRCUITPY_BINASCII = 1
|
||||||
CIRCUITPY_SDCARDIO = 1
|
CIRCUITPY_SDCARDIO = 1
|
||||||
CIRCUITPY_JSON = 1
|
CIRCUITPY_JSON = 1
|
||||||
CIRCUITPY_MSGPACK = 1
|
CIRCUITPY_MSGPACK = 1
|
||||||
|
CIRCUITPY_ALARM = 1
|
||||||
|
|
||||||
# no SAMD51 support... yet ;)
|
# no SAMD51 support... yet ;)
|
||||||
# CIRCUITPY_ALARM = 1
|
|
||||||
# CIRCUITPY_DUALBANK=1
|
# CIRCUITPY_DUALBANK=1
|
||||||
|
|
||||||
# Not needed
|
# Not needed
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "common-hal/alarm/SleepMemory.h"
|
#include "common-hal/alarm/SleepMemory.h"
|
||||||
|
#include "shared-bindings/nvm/ByteArray.h"
|
||||||
|
|
||||||
void alarm_sleep_memory_reset(void) {
|
void alarm_sleep_memory_reset(void) {
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
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);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "py/objtuple.h"
|
#include "py/objtuple.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "lib/utils/interrupt_char.h"
|
#include "lib/utils/interrupt_char.h"
|
||||||
|
// #include <stdio.h>
|
||||||
|
|
||||||
#include "shared-bindings/alarm/__init__.h"
|
#include "shared-bindings/alarm/__init__.h"
|
||||||
#include "shared-bindings/alarm/SleepMemory.h"
|
#include "shared-bindings/alarm/SleepMemory.h"
|
||||||
@ -36,7 +37,7 @@
|
|||||||
#include "shared-bindings/alarm/time/TimeAlarm.h"
|
#include "shared-bindings/alarm/time/TimeAlarm.h"
|
||||||
|
|
||||||
#include "shared-bindings/microcontroller/__init__.h"
|
#include "shared-bindings/microcontroller/__init__.h"
|
||||||
|
#include "samd/external_interrupts.h"
|
||||||
#include "supervisor/port.h"
|
#include "supervisor/port.h"
|
||||||
#include "supervisor/workflow.h"
|
#include "supervisor/workflow.h"
|
||||||
|
|
||||||
@ -46,8 +47,9 @@ const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
|
|||||||
.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?
|
// TODO: make a custom enum to avoid weird values like PM_SLEEPCFG_SLEEPMODE_BACKUP_Val?
|
||||||
|
STATIC volatile uint32_t _target;
|
||||||
|
STATIC bool fake_sleep;
|
||||||
|
|
||||||
void alarm_reset(void) {
|
void alarm_reset(void) {
|
||||||
// Reset the alarm flag
|
// Reset the alarm flag
|
||||||
@ -64,8 +66,14 @@ samd_sleep_source_t alarm_get_wakeup_cause(void) {
|
|||||||
if (alarm_time_timealarm_woke_this_cycle()) {
|
if (alarm_time_timealarm_woke_this_cycle()) {
|
||||||
return SAMD_WAKEUP_RTC;
|
return SAMD_WAKEUP_RTC;
|
||||||
}
|
}
|
||||||
// TODO: for deep sleep, manually determine how the chip woke up
|
if (RSTC->RCAUSE.bit.BACKUP) {
|
||||||
// TODO: try checking the interrupt flag tables for RTC TAMPER vs COMPARE
|
// 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) {
|
||||||
|
return SAMD_WAKEUP_GPIO;
|
||||||
|
}
|
||||||
|
return SAMD_WAKEUP_RTC;
|
||||||
|
}
|
||||||
return SAMD_WAKEUP_UNDEF;
|
return SAMD_WAKEUP_UNDEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +104,7 @@ 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) {
|
||||||
@ -126,10 +135,34 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
|
|||||||
// TODO: the SAMD implementation of this (purportedly) disables interrupts
|
// TODO: the SAMD implementation of this (purportedly) disables interrupts
|
||||||
// Presumably this doesn't impact the RTC interrupts, somehow, or it would never wake up?
|
// Presumably this doesn't impact the RTC interrupts, somehow, or it would never wake up?
|
||||||
// Will it prevent an external interrupt from waking?
|
// Will it prevent an external interrupt from waking?
|
||||||
port_idle_until_interrupt();
|
// port_idle_until_interrupt();
|
||||||
// Alternative would be `sleep(PM_SLEEPCFG_SLEEPMODE_IDLE2_Val)`, I think?
|
// Alternative would be `sleep(PM_SLEEPCFG_SLEEPMODE_IDLE2_Val)`, I think?
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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)) {
|
||||||
|
__set_FPSCR(__get_FPSCR() & ~(0x9f));
|
||||||
|
(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);
|
||||||
|
|
||||||
|
__DSB(); // Data Synchronization Barrier
|
||||||
|
__WFI(); // Wait For Interrupt
|
||||||
|
// Enable RTC interrupts
|
||||||
|
NVIC_EnableIRQ(RTC_IRQn);
|
||||||
|
|
||||||
|
|
||||||
|
// END ATTEMPT ------------------------------
|
||||||
|
}
|
||||||
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.
|
||||||
}
|
}
|
||||||
@ -145,25 +178,98 @@ 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?
|
||||||
|
|
||||||
// Set a flag in the backup registers to indicate sleep wakeup
|
// Set a flag in the backup registers to indicate sleep wakeup
|
||||||
SAMD_ALARM_FLAG = 0x01;
|
SAMD_ALARM_FLAG = 0x01;
|
||||||
|
|
||||||
// TODO: make sure this actually works, or replace with extracted register version
|
// Clear the FPU interrupt because it can prevent us from sleeping.
|
||||||
// sleep(PM_SLEEPCFG_SLEEPMODE_BACKUP_Val);
|
if (__get_FPSCR() & ~(0x9f)) {
|
||||||
|
__set_FPSCR(__get_FPSCR() & ~(0x9f));
|
||||||
|
(void)__get_FPSCR();
|
||||||
|
}
|
||||||
|
|
||||||
// The above shuts down RAM, so we should never hit this
|
// PinAlarm (hacky way of checking if time alarm or pin alarm)
|
||||||
|
if (RTC->MODE0.INTENSET.bit.TAMPER) {
|
||||||
|
// Disable interrupts
|
||||||
|
NVIC_DisableIRQ(RTC_IRQn);
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// TODO: map requested pin to limited selection of TAMPER pins
|
||||||
|
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);
|
||||||
|
// Set interrupts for TAMPER or overflow
|
||||||
|
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER;
|
||||||
|
// TimeAlarm
|
||||||
|
} else {
|
||||||
|
// Retrieve COMP1 value before resetting RTC
|
||||||
|
// Disable interrupts
|
||||||
|
NVIC_DisableIRQ(RTC_IRQn);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
RTC->MODE0.COMP[1].reg = (_target/1024) * 32;
|
||||||
|
while(RTC->MODE0.SYNCBUSY.reg);
|
||||||
|
|
||||||
|
// Enable interrupts
|
||||||
|
NVIC_SetPriority(RTC_IRQn, 0);
|
||||||
|
NVIC_EnableIRQ(RTC_IRQn);
|
||||||
|
// Set interrupts for COMPARE1 or overflow
|
||||||
|
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP1 | RTC_MODE1_INTENSET_OVF;
|
||||||
|
}
|
||||||
|
// Set-up Deep Sleep Mode
|
||||||
|
// RAM retention
|
||||||
|
PM->BKUPCFG.reg = PM_BKUPCFG_BRAMCFG(0x2); // No RAM retention 0x2 partial:0x1
|
||||||
|
while (PM->BKUPCFG.bit.BRAMCFG != 0x2); // Wait for synchronization
|
||||||
|
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
|
||||||
|
__WFI(); // Wait For Interrupt
|
||||||
|
|
||||||
|
// The above shuts down RAM and triggers a reset, so we should never hit this
|
||||||
while (1) {
|
while (1) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_alarm_pretending_deep_sleep(void) {
|
MP_NOINLINE void common_hal_alarm_pretending_deep_sleep(void) {
|
||||||
// TODO:
|
// TODO:
|
||||||
// If tamper detect interrupts cannot be used to wake from the Idle tier of sleep,
|
// 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
|
// This section will need to re-initialize the pins to allow the PORT peripheral
|
||||||
// to generate external interrupts again. See STM32 for reference.
|
// to generate external interrupts again. See STM32 for reference.
|
||||||
|
|
||||||
|
if (!fake_sleep) {
|
||||||
|
SAMD_ALARM_FLAG = 1;
|
||||||
|
while(RTC->MODE0.SYNCBUSY.reg);
|
||||||
|
fake_sleep = true;
|
||||||
|
} else {
|
||||||
|
port_idle_until_interrupt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_alarm_gc_collect(void) {
|
void common_hal_alarm_gc_collect(void) {
|
||||||
|
@ -25,6 +25,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
|
#include "samd/external_interrupts.h"
|
||||||
|
#include "eic_handler.h"
|
||||||
|
#include "atmel_start_pins.h"
|
||||||
|
#include "hal/include/hal_gpio.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
#include "shared-bindings/alarm/pin/PinAlarm.h"
|
#include "shared-bindings/alarm/pin/PinAlarm.h"
|
||||||
#include "shared-bindings/microcontroller/__init__.h"
|
#include "shared-bindings/microcontroller/__init__.h"
|
||||||
@ -33,34 +39,63 @@
|
|||||||
// 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 bool woke_up;
|
STATIC bool woke_up;
|
||||||
|
STATIC bool deep_wkup_enabled;
|
||||||
|
|
||||||
// TODO: Create tables here reserving IRQ instances, and for the IRQ
|
// TODO: Create tables here reserving IRQ instances, and for the IRQ
|
||||||
// callback to store what pin triggered the interrupt
|
// callback to store what pin triggered the interrupt
|
||||||
// STATIC bool reserved_alarms[SOME_VAL];
|
// STATIC bool reserved_alarms[SOME_VAL];
|
||||||
// STATIC uint16_t triggered_pins[SOME_VAL];
|
// STATIC uint16_t triggered_pins[SOME_VAL];
|
||||||
|
|
||||||
STATIC void pin_alarm_callback(uint8_t num) { // parameters can be changed
|
void pin_alarm_callback(uint8_t num) { // parameters can be changed
|
||||||
// TODO: This is responsible for resetting the IRQ (so it doesn't
|
// TODO: This is responsible for resetting the IRQ (so it doesn't
|
||||||
// go off constantly, and recording what pin was responsible for
|
// go off constantly, and recording what pin was responsible for
|
||||||
// the trigger. This will only work for light sleep/fake deep
|
// the trigger. This will only work for light sleep/fake deep
|
||||||
// sleep, in conjunction with the find_triggered_alarm function
|
// sleep, in conjunction with the find_triggered_alarm function
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
mp_raise_NotImplementedError(translate("PinAlarms not available"));
|
// Tamper Pins: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; OUT:PB01
|
||||||
// TODO: Catch edge or level mode if not supported
|
// TODO: Catch edge or level mode if not supported
|
||||||
// if (!edge) {
|
if (!pin->has_extint) {
|
||||||
// mp_raise_NotImplementedError(translate("Only edge detection is available on this hardware"));
|
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: determine if pin has an interrupt channel available
|
||||||
// TODO: set pin pull settings, input/output, etc
|
// TODO: set pin pull settings, input/output, etc
|
||||||
// QUESTION: can PORT/EVSYS interrupts (lightsleep) coexist with RTC->TAMPER (deepsleep) settings?
|
// QUESTION: can PORT/EVSYS interrupts (lightsleep) coexist with RTC->TAMPER (deepsleep) settings?
|
||||||
// Actual initialization of the interrupt should be delayed until set_alarm
|
// 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->pull = pull;
|
self->pull = pull;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
@ -81,6 +116,10 @@ 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 (RTC->MODE0.INTFLAG.bit.TAMPER){
|
||||||
|
woke_up = true;
|
||||||
|
RTC->MODE0.INTENCLR.bit.TAMPER = 1; // clear flag and interrupt setting
|
||||||
|
}
|
||||||
return woke_up;
|
return woke_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +129,15 @@ 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;
|
||||||
|
|
||||||
|
|
||||||
// TODO: Determine whether any pins have been marked as
|
// TODO: Determine whether any pins have been marked as
|
||||||
// triggering the alarm (using the static vars at
|
// triggering the alarm (using the static vars at
|
||||||
// start of file) and if so return that alarm.
|
// start of file) and if so return that alarm.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// Return nothing if no matching alarms are found.
|
// Return nothing if no matching alarms are found.
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
@ -115,6 +159,9 @@ void alarm_pin_pinalarm_reset(void) {
|
|||||||
// sure to clear any reserved tables, deinit both PORT and TAMPER
|
// sure to clear any reserved tables, deinit both PORT and TAMPER
|
||||||
// settings, etc. If flags are set to indicate this module is in
|
// settings, etc. If flags are set to indicate this module is in
|
||||||
// use, reset them.
|
// use, reset them.
|
||||||
|
|
||||||
|
// Disable TAMPER interrupt
|
||||||
|
RTC->MODE0.INTENCLR.bit.TAMPER = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -124,6 +171,15 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob
|
|||||||
if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
|
if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
|
||||||
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
|
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
|
||||||
if (deep_sleep) {
|
if (deep_sleep) {
|
||||||
|
// Tamper Pins: IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01; OUT:PB01
|
||||||
|
// Only these pins can do TAMPER
|
||||||
|
if (alarm->pin != &pin_PB00 && alarm->pin != &pin_PB02 &&
|
||||||
|
alarm->pin != &pin_PA02) {
|
||||||
|
mp_raise_ValueError(translate("Pin cannot wake from Deep Sleep"));
|
||||||
|
}
|
||||||
|
deep_wkup_enabled = true;
|
||||||
|
// Set tamper interrupt so deep sleep knows that's the intent
|
||||||
|
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER;
|
||||||
// TODO: Set up deep sleep alarms.
|
// TODO: Set up deep sleep alarms.
|
||||||
// For deep sleep alarms, first check if the
|
// For deep sleep alarms, first check if the
|
||||||
// alarm pin value is valid for RTC->TAMPER. Ensure
|
// alarm pin value is valid for RTC->TAMPER. Ensure
|
||||||
@ -138,6 +194,11 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob
|
|||||||
// `alarm_pin_pinalarm_prepare_for_deep_sleep`
|
// `alarm_pin_pinalarm_prepare_for_deep_sleep`
|
||||||
// below.
|
// below.
|
||||||
} // use else-if here if RTC-TAMPER can wake from IDLE
|
} // 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.
|
// TODO: Set up light sleep / fake deep sleep alarms.
|
||||||
// PORT/EVSYS should have more valid pin combinations
|
// PORT/EVSYS should have more valid pin combinations
|
||||||
|
@ -35,11 +35,13 @@ typedef struct {
|
|||||||
const mcu_pin_obj_t *pin;
|
const mcu_pin_obj_t *pin;
|
||||||
bool value;
|
bool value;
|
||||||
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(void);
|
||||||
|
|
||||||
|
void pin_alarm_callback(uint8_t num);
|
||||||
void alarm_pin_pinalarm_reset(void);
|
void alarm_pin_pinalarm_reset(void);
|
||||||
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);
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
|
#include "hpl/pm/hpl_pm_base.h"
|
||||||
|
// #include <stdio.h>
|
||||||
|
// #include "shared-bindings/microcontroller/__init__.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"
|
||||||
@ -37,6 +40,10 @@ void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self,
|
|||||||
// TODO: throw a ValueError if the input time exceeds the maximum
|
// TODO: throw a ValueError if the input time exceeds the maximum
|
||||||
// value of the Compare register. This must be calculated from the
|
// value of the Compare register. This must be calculated from the
|
||||||
// setup values in port.c. Should be ~3 days. Give it some margin.
|
// 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?
|
||||||
|
|
||||||
self->monotonic_time = monotonic_time;
|
self->monotonic_time = monotonic_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +71,8 @@ mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) {
|
|||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void timer_callback(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;
|
woke_up = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +85,8 @@ void alarm_time_timealarm_reset(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@ -100,13 +110,20 @@ void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_
|
|||||||
uint32_t wakeup_in_ticks = wakeup_in_secs * 1024;
|
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
|
// In the deep sleep case, we can't start the timer until the USB delay has finished
|
||||||
// (othersise it will go off while USB enumerates, and we'll risk entering deep sleep
|
// (otherwise it will go off while USB enumerates, and we'll risk entering deep sleep
|
||||||
// without any way to wake up again)
|
// without any way to wake up again)
|
||||||
if (deep_sleep) {
|
if (deep_sleep) {
|
||||||
deep_sleep_ticks = wakeup_in_ticks;
|
deep_sleep_ticks = wakeup_in_ticks;
|
||||||
} else {
|
} else {
|
||||||
deep_sleep_ticks = 0;
|
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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// TODO: set up RTC->COMP[1] and create a callback pointing to
|
// TODO: set up RTC->COMP[1] and create a callback pointing to
|
||||||
// timer_callback. See atmel-samd/supervisor/port.c -> _port_interrupt_after_ticks()
|
// timer_callback. See atmel-samd/supervisor/port.c -> _port_interrupt_after_ticks()
|
||||||
@ -116,6 +133,8 @@ void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_
|
|||||||
|
|
||||||
// If true deep sleep is called, it will either ignore or overwrite the above setup depending on
|
// 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
|
// 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) {
|
||||||
@ -124,6 +143,10 @@ void alarm_time_timealarm_prepare_for_deep_sleep(void) {
|
|||||||
// 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.
|
||||||
|
|
||||||
|
// RTC->MODE0.COMP[1].reg = deep_sleep_ticks;
|
||||||
|
// while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP1)) != 0) {
|
||||||
|
// }
|
||||||
deep_sleep_ticks = 0;
|
deep_sleep_ticks = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,11 @@ typedef struct {
|
|||||||
|
|
||||||
mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms);
|
mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms);
|
||||||
mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void);
|
mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void);
|
||||||
|
void timer_callback(void);
|
||||||
bool alarm_time_timealarm_woke_this_cycle(void);
|
bool alarm_time_timealarm_woke_this_cycle(void);
|
||||||
void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
|
uint32_t alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
|
||||||
void alarm_time_timealarm_reset(void);
|
void alarm_time_timealarm_reset(void);
|
||||||
|
|
||||||
void alarm_time_timealarm_prepare_for_deep_sleep(void);
|
uint32_t alarm_time_timealarm_prepare_for_deep_sleep(void);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM_TIMEALARM_H
|
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ALARM_TIMEALARM_H
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "common-hal/rotaryio/IncrementalEncoder.h"
|
#include "common-hal/rotaryio/IncrementalEncoder.h"
|
||||||
#include "common-hal/countio/Counter.h"
|
#include "common-hal/countio/Counter.h"
|
||||||
#include "shared-bindings/microcontroller/__init__.h"
|
#include "shared-bindings/microcontroller/__init__.h"
|
||||||
|
#include "common-hal/alarm/pin/PinAlarm.h"
|
||||||
// #include "samd/external_interrupts.h"
|
// #include "samd/external_interrupts.h"
|
||||||
#include "eic_handler.h"
|
#include "eic_handler.h"
|
||||||
|
|
||||||
@ -66,6 +67,12 @@ void shared_eic_handler(uint8_t channel) {
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CIRCUITPY_ALARM
|
||||||
|
case EIC_HANDLER_ALARM:
|
||||||
|
pin_alarm_callback(channel);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,9 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0
|
|||||||
|
|
||||||
# The ?='s allow overriding in mpconfigboard.mk.
|
# The ?='s allow overriding in mpconfigboard.mk.
|
||||||
|
|
||||||
|
|
||||||
CIRCUITPY_ALARM ?= 0
|
CIRCUITPY_ALARM ?= 0
|
||||||
CIRCUITPY_PS2IO ?= 1
|
PY_PS2IO ?= 1
|
||||||
CIRCUITPY_SAMD ?= 1
|
CIRCUITPY_SAMD ?= 1
|
||||||
CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_FULL_BUILD)
|
CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_FULL_BUILD)
|
||||||
CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_FULL_BUILD)
|
CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_FULL_BUILD)
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
#error Unknown chip family
|
#error Unknown chip family
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "common-hal/alarm/__init__.h"
|
||||||
#include "common-hal/analogio/AnalogIn.h"
|
#include "common-hal/analogio/AnalogIn.h"
|
||||||
#include "common-hal/analogio/AnalogOut.h"
|
#include "common-hal/analogio/AnalogOut.h"
|
||||||
#include "common-hal/audiobusio/PDMIn.h"
|
#include "common-hal/audiobusio/PDMIn.h"
|
||||||
@ -77,6 +78,7 @@
|
|||||||
#include "samd/dma.h"
|
#include "samd/dma.h"
|
||||||
#include "shared-bindings/microcontroller/__init__.h"
|
#include "shared-bindings/microcontroller/__init__.h"
|
||||||
#include "shared-bindings/rtc/__init__.h"
|
#include "shared-bindings/rtc/__init__.h"
|
||||||
|
#include "shared-bindings/alarm/time/TimeAlarm.h"
|
||||||
#include "shared_timers.h"
|
#include "shared_timers.h"
|
||||||
#include "reset.h"
|
#include "reset.h"
|
||||||
|
|
||||||
@ -496,6 +498,11 @@ void RTC_Handler(void) {
|
|||||||
// SAMD21 ticks are handled by EVSYS
|
// SAMD21 ticks are handled by EVSYS
|
||||||
#ifdef SAM_D5X_E5X
|
#ifdef SAM_D5X_E5X
|
||||||
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
|
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
|
||||||
|
// Check if we're sleeping
|
||||||
|
if (SAMD_ALARM_FLAG){
|
||||||
|
timer_callback();
|
||||||
|
SAMD_ALARM_FLAG = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user