29
29
#include "py/objtuple.h"
30
30
#include "py/runtime.h"
31
31
#include "lib/utils/interrupt_char.h"
32
+ // #include <stdio.h>
32
33
33
34
#include "shared-bindings/alarm/__init__.h"
34
35
#include "shared-bindings/alarm/SleepMemory.h"
35
36
#include "shared-bindings/alarm/pin/PinAlarm.h"
36
37
#include "shared-bindings/alarm/time/TimeAlarm.h"
37
38
38
39
#include "shared-bindings/microcontroller/__init__.h"
39
-
40
+ #include "samd/external_interrupts.h"
40
41
#include "supervisor/port.h"
41
42
#include "supervisor/workflow.h"
42
43
@@ -46,8 +47,9 @@ const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
46
47
.type = & alarm_sleep_memory_type ,
47
48
},
48
49
};
49
-
50
50
// 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 ;
51
53
52
54
void alarm_reset (void ) {
53
55
// Reset the alarm flag
@@ -64,8 +66,14 @@ samd_sleep_source_t alarm_get_wakeup_cause(void) {
64
66
if (alarm_time_timealarm_woke_this_cycle ()) {
65
67
return SAMD_WAKEUP_RTC ;
66
68
}
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
+ }
69
77
return SAMD_WAKEUP_UNDEF ;
70
78
}
71
79
@@ -96,6 +104,7 @@ mp_obj_t common_hal_alarm_create_wake_alarm(void) {
96
104
STATIC void _setup_sleep_alarms (bool deep_sleep , size_t n_alarms , const mp_obj_t * alarms ) {
97
105
alarm_pin_pinalarm_set_alarms (deep_sleep , n_alarms , alarms );
98
106
alarm_time_timealarm_set_alarms (deep_sleep , n_alarms , alarms );
107
+ fake_sleep = false;
99
108
}
100
109
101
110
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
126
135
// TODO: the SAMD implementation of this (purportedly) disables interrupts
127
136
// Presumably this doesn't impact the RTC interrupts, somehow, or it would never wake up?
128
137
// Will it prevent an external interrupt from waking?
129
- port_idle_until_interrupt ();
138
+ // port_idle_until_interrupt();
130
139
// Alternative would be `sleep(PM_SLEEPCFG_SLEEPMODE_IDLE2_Val)`, I think?
131
- }
132
140
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
+ }
133
166
if (mp_hal_is_interrupted ()) {
134
167
return mp_const_none ; // Shouldn't be given to python code because exception handling should kick in.
135
168
}
@@ -145,25 +178,98 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala
145
178
void NORETURN common_hal_alarm_enter_deep_sleep (void ) {
146
179
alarm_pin_pinalarm_prepare_for_deep_sleep ();
147
180
alarm_time_timealarm_prepare_for_deep_sleep ();
181
+ _target = RTC -> MODE0 .COMP [1 ].reg ;
148
182
port_disable_tick (); // TODO: Required for SAMD?
149
183
150
184
// Set a flag in the backup registers to indicate sleep wakeup
151
185
SAMD_ALARM_FLAG = 0x01 ;
152
186
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
+ }
155
192
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
157
255
while (1 ) {
158
256
;
159
257
}
160
258
}
161
259
162
- void common_hal_alarm_pretending_deep_sleep (void ) {
260
+ MP_NOINLINE void common_hal_alarm_pretending_deep_sleep (void ) {
163
261
// TODO:
164
262
// If tamper detect interrupts cannot be used to wake from the Idle tier of sleep,
165
263
// This section will need to re-initialize the pins to allow the PORT peripheral
166
264
// 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
+ }
167
273
}
168
274
169
275
void common_hal_alarm_gc_collect (void ) {
0 commit comments