Skip to content

Commit 9f0cb0e

Browse files
authored
Merge pull request #6 from maholli/samd-sleep-v1
cherry-picked original hierophect commit and merged with my samd `alarm` (deep sleep) work
2 parents f748d66 + b95325e commit 9f0cb0e

File tree

12 files changed

+247
-25
lines changed

12 files changed

+247
-25
lines changed

ports/atmel-samd/boards/pycubed/mpconfigboard.mk

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@ LONGINT_IMPL = MPZ
1212

1313
CIRCUITPY_DRIVE_LABEL = "PYCUBED"
1414

15-
# Not needed.
15+
CIRCUITPY_ULAB = 1
16+
CIRCUITPY_BINASCII = 1
17+
CIRCUITPY_SDCARDIO = 1
18+
CIRCUITPY_JSON = 1
19+
CIRCUITPY_MSGPACK = 1
20+
CIRCUITPY_ALARM = 1
21+
22+
# no SAMD51 support... yet ;)
23+
# CIRCUITPY_DUALBANK=1
24+
25+
# Not needed
1626
CIRCUITPY_AUDIOBUSIO = 0
17-
CIRCUITPY_BITMAPTOOLS = 0
1827
CIRCUITPY_DISPLAYIO = 0
1928
CIRCUITPY_FRAMEBUFFERIO = 0
2029
CIRCUITPY_KEYPAD = 0
2130
CIRCUITPY_RGBMATRIX = 0
2231
CIRCUITPY_PS2IO = 0
32+
CIRCUITPY_BLEIO_HCI=0
33+
CIRCUITPY_BLEIO=0
2334

2435
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
2536
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register
26-
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD

ports/atmel-samd/boards/pycubed_v05/mpconfigboard.mk

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ EXTERNAL_FLASH_DEVICE_COUNT = 1
1111
EXTERNAL_FLASH_DEVICES = W25Q80DV
1212
LONGINT_IMPL = MPZ
1313

14+
CIRCUITPY_DRIVE_LABEL = "PYCUBED"
15+
1416
CIRCUITPY_ULAB = 1
1517
CIRCUITPY_BINASCII = 1
1618
CIRCUITPY_SDCARDIO = 1
1719
CIRCUITPY_JSON = 1
1820
CIRCUITPY_MSGPACK = 1
21+
CIRCUITPY_ALARM = 1
1922

2023
# no SAMD51 support... yet ;)
21-
# CIRCUITPY_ALARM = 1
2224
# CIRCUITPY_DUALBANK=1
2325

2426
# Not needed

ports/atmel-samd/common-hal/alarm/SleepMemory.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "py/runtime.h"
3030
#include "common-hal/alarm/SleepMemory.h"
31+
#include "shared-bindings/nvm/ByteArray.h"
3132

3233
void alarm_sleep_memory_reset(void) {
3334

ports/atmel-samd/common-hal/alarm/SleepMemory.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
typedef struct {
3333
mp_obj_base_t base;
34+
uint8_t *start_address;
35+
uint8_t len;
3436
} alarm_sleep_memory_obj_t;
3537

3638
extern void alarm_sleep_memory_reset(void);

ports/atmel-samd/common-hal/alarm/__init__.c

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@
2929
#include "py/objtuple.h"
3030
#include "py/runtime.h"
3131
#include "lib/utils/interrupt_char.h"
32+
// #include <stdio.h>
3233

3334
#include "shared-bindings/alarm/__init__.h"
3435
#include "shared-bindings/alarm/SleepMemory.h"
3536
#include "shared-bindings/alarm/pin/PinAlarm.h"
3637
#include "shared-bindings/alarm/time/TimeAlarm.h"
3738

3839
#include "shared-bindings/microcontroller/__init__.h"
39-
40+
#include "samd/external_interrupts.h"
4041
#include "supervisor/port.h"
4142
#include "supervisor/workflow.h"
4243

@@ -46,8 +47,9 @@ const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
4647
.type = &alarm_sleep_memory_type,
4748
},
4849
};
49-
5050
// TODO: make a custom enum to avoid weird values like PM_SLEEPCFG_SLEEPMODE_BACKUP_Val?
51+
STATIC volatile uint32_t _target;
52+
STATIC bool fake_sleep;
5153

5254
void alarm_reset(void) {
5355
// Reset the alarm flag
@@ -64,8 +66,14 @@ samd_sleep_source_t alarm_get_wakeup_cause(void) {
6466
if (alarm_time_timealarm_woke_this_cycle()) {
6567
return SAMD_WAKEUP_RTC;
6668
}
67-
// TODO: for deep sleep, manually determine how the chip woke up
68-
// TODO: try checking the interrupt flag tables for RTC TAMPER vs COMPARE
69+
if (RSTC->RCAUSE.bit.BACKUP) {
70+
// not able to detect PinAlarm wake since registers are getting reset
71+
// TODO: come up with a way to detect a TAMPER
72+
if (RTC->MODE0.TAMPID.reg || RTC->MODE0.INTFLAG.bit.TAMPER) {
73+
return SAMD_WAKEUP_GPIO;
74+
}
75+
return SAMD_WAKEUP_RTC;
76+
}
6977
return SAMD_WAKEUP_UNDEF;
7078
}
7179

@@ -96,6 +104,7 @@ mp_obj_t common_hal_alarm_create_wake_alarm(void) {
96104
STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
97105
alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms);
98106
alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms);
107+
fake_sleep = false;
99108
}
100109

101110
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
126135
// TODO: the SAMD implementation of this (purportedly) disables interrupts
127136
// Presumably this doesn't impact the RTC interrupts, somehow, or it would never wake up?
128137
// Will it prevent an external interrupt from waking?
129-
port_idle_until_interrupt();
138+
// port_idle_until_interrupt();
130139
// Alternative would be `sleep(PM_SLEEPCFG_SLEEPMODE_IDLE2_Val)`, I think?
131-
}
132140

141+
// ATTEMPT ------------------------------
142+
// This works but achieves same power consumption as time.sleep()
143+
144+
// Clear the FPU interrupt because it can prevent us from sleeping.
145+
if (__get_FPSCR() & ~(0x9f)) {
146+
__set_FPSCR(__get_FPSCR() & ~(0x9f));
147+
(void)__get_FPSCR();
148+
}
149+
150+
// Disable RTC interrupts
151+
NVIC_DisableIRQ(RTC_IRQn);
152+
// Set standby power domain stuff
153+
PM->STDBYCFG.reg = PM_STDBYCFG_RAMCFG_OFF;
154+
// Set-up Sleep Mode
155+
PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_STANDBY;
156+
while(PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_STANDBY_Val);
157+
158+
__DSB(); // Data Synchronization Barrier
159+
__WFI(); // Wait For Interrupt
160+
// Enable RTC interrupts
161+
NVIC_EnableIRQ(RTC_IRQn);
162+
163+
164+
// END ATTEMPT ------------------------------
165+
}
133166
if (mp_hal_is_interrupted()) {
134167
return mp_const_none; // Shouldn't be given to python code because exception handling should kick in.
135168
}
@@ -145,25 +178,98 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala
145178
void NORETURN common_hal_alarm_enter_deep_sleep(void) {
146179
alarm_pin_pinalarm_prepare_for_deep_sleep();
147180
alarm_time_timealarm_prepare_for_deep_sleep();
181+
_target = RTC->MODE0.COMP[1].reg;
148182
port_disable_tick(); // TODO: Required for SAMD?
149183

150184
// Set a flag in the backup registers to indicate sleep wakeup
151185
SAMD_ALARM_FLAG = 0x01;
152186

153-
// TODO: make sure this actually works, or replace with extracted register version
154-
// sleep(PM_SLEEPCFG_SLEEPMODE_BACKUP_Val);
187+
// Clear the FPU interrupt because it can prevent us from sleeping.
188+
if (__get_FPSCR() & ~(0x9f)) {
189+
__set_FPSCR(__get_FPSCR() & ~(0x9f));
190+
(void)__get_FPSCR();
191+
}
155192

156-
// The above shuts down RAM, so we should never hit this
193+
// PinAlarm (hacky way of checking if time alarm or pin alarm)
194+
if (RTC->MODE0.INTENSET.bit.TAMPER) {
195+
// Disable interrupts
196+
NVIC_DisableIRQ(RTC_IRQn);
197+
// Must disable the RTC before writing to EVCTRL and TMPCTRL
198+
RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC
199+
while (RTC->MODE0.SYNCBUSY.bit.ENABLE); // Wait for synchronization
200+
RTC->MODE0.CTRLA.bit.SWRST = 1; // Software reset the RTC
201+
while (RTC->MODE0.SYNCBUSY.bit.SWRST); // Wait for synchronization
202+
RTC->MODE0.CTRLA.reg = RTC_MODE0_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024
203+
RTC_MODE0_CTRLA_MODE_COUNT32; // Set RTC to mode 0, 32-bit timer
204+
205+
// TODO: map requested pin to limited selection of TAMPER pins
206+
RTC->MODE0.TAMPCTRL.bit.DEBNC2 = 1; // Edge triggered when INn is stable for 4 CLK_RTC_DEB periods
207+
RTC->MODE0.TAMPCTRL.bit.TAMLVL2 = 1; // rising edge
208+
//PA02 = IN2
209+
RTC->MODE0.TAMPCTRL.bit.IN2ACT = 1; // WAKE on IN2 (doesn't save timestamp)
210+
211+
// Enable interrupts
212+
NVIC_SetPriority(RTC_IRQn, 0);
213+
NVIC_EnableIRQ(RTC_IRQn);
214+
// Set interrupts for TAMPER or overflow
215+
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_TAMPER;
216+
// TimeAlarm
217+
} else {
218+
// Retrieve COMP1 value before resetting RTC
219+
// Disable interrupts
220+
NVIC_DisableIRQ(RTC_IRQn);
221+
222+
// Must disable the RTC before writing to EVCTRL and TMPCTRL
223+
RTC->MODE0.CTRLA.bit.ENABLE = 0; // Disable the RTC
224+
while (RTC->MODE0.SYNCBUSY.bit.ENABLE); // Wait for synchronization
225+
226+
RTC->MODE0.CTRLA.bit.SWRST = 1; // Software reset the RTC
227+
while (RTC->MODE0.SYNCBUSY.bit.SWRST); // Wait for synchronization
228+
229+
RTC->MODE0.CTRLA.reg = RTC_MODE0_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024
230+
RTC_MODE0_CTRLA_MODE_COUNT32; // Set RTC to mode 0, 32-bit timer
231+
232+
RTC->MODE0.COMP[1].reg = (_target/1024) * 32;
233+
while(RTC->MODE0.SYNCBUSY.reg);
234+
235+
// Enable interrupts
236+
NVIC_SetPriority(RTC_IRQn, 0);
237+
NVIC_EnableIRQ(RTC_IRQn);
238+
// Set interrupts for COMPARE1 or overflow
239+
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP1 | RTC_MODE1_INTENSET_OVF;
240+
}
241+
// Set-up Deep Sleep Mode
242+
// RAM retention
243+
PM->BKUPCFG.reg = PM_BKUPCFG_BRAMCFG(0x2); // No RAM retention 0x2 partial:0x1
244+
while (PM->BKUPCFG.bit.BRAMCFG != 0x2); // Wait for synchronization
245+
PM->SLEEPCFG.reg = PM_SLEEPCFG_SLEEPMODE_BACKUP;
246+
while(PM->SLEEPCFG.bit.SLEEPMODE != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val);
247+
248+
RTC->MODE0.CTRLA.bit.ENABLE = 1; // Enable the RTC
249+
while (RTC->MODE0.SYNCBUSY.bit.ENABLE); // Wait for synchronization
250+
251+
__DSB(); // Data Synchronization Barrier
252+
__WFI(); // Wait For Interrupt
253+
254+
// The above shuts down RAM and triggers a reset, so we should never hit this
157255
while (1) {
158256
;
159257
}
160258
}
161259

162-
void common_hal_alarm_pretending_deep_sleep(void) {
260+
MP_NOINLINE void common_hal_alarm_pretending_deep_sleep(void) {
163261
// TODO:
164262
// If tamper detect interrupts cannot be used to wake from the Idle tier of sleep,
165263
// This section will need to re-initialize the pins to allow the PORT peripheral
166264
// to generate external interrupts again. See STM32 for reference.
265+
266+
if (!fake_sleep) {
267+
SAMD_ALARM_FLAG = 1;
268+
while(RTC->MODE0.SYNCBUSY.reg);
269+
fake_sleep = true;
270+
} else {
271+
port_idle_until_interrupt();
272+
}
167273
}
168274

169275
void common_hal_alarm_gc_collect(void) {

0 commit comments

Comments
 (0)