4747#include "mxc_delay.h"
4848#include "rtc.h"
4949
50+ // msec to RTC subsec ticks (4 kHz)
51+ #define MSEC_TO_SS_ALARM (x ) \
52+ (0 - ((x * 4096) / \
53+ 1000)) /* Converts a time in milleseconds to the equivalent RSSA register value. */
54+
5055// Externs defined by linker .ld file
5156extern uint32_t _stack , _heap , _estack , _eheap ;
5257extern uint32_t _ebss ;
@@ -60,8 +65,9 @@ extern const int num_leds;
6065//todo: define an LED HAL
6166// #include "peripherals/led.h"
6267
63- // For caching rtc data for ticks
68+ // For saving rtc data for ticks
6469static uint32_t subsec , sec = 0 ;
70+ static uint32_t tick_flag = 0 ;
6571
6672// defined by cmsis core files
6773extern void NVIC_SystemReset (void ) NORETURN ;
@@ -91,20 +97,43 @@ safe_mode_t port_init(void) {
9197 // Turn on one LED to indicate Sign of Life
9298 MXC_GPIO_OutSet (led_pin [2 ].port , led_pin [2 ].mask );
9399
100+ // Enable clock to RTC peripheral
101+ MXC_GCR -> clkctrl |= MXC_F_GCR_CLKCTRL_ERTCO_EN ;
102+ while (!(MXC_GCR -> clkctrl & MXC_F_GCR_CLKCTRL_ERTCO_RDY ));
103+
104+ NVIC_EnableIRQ (RTC_IRQn );
105+ NVIC_EnableIRQ (USB_IRQn );
106+
94107 // Init RTC w/ 0sec, 0subsec
95108 // Driven by 32.768 kHz ERTCO, with ssec= 1/4096 s
96- err = MXC_RTC_Init (0 , 0 );
97- if (err ) {
98- return SAFE_MODE_SDK_FATAL_ERROR ;
99- }
100- NVIC_EnableIRQ (RTC_IRQn );
109+ while ( MXC_RTC_Init (0 ,0 ) != E_SUCCESS ) {};
101110
102- // todo: init periph clocks / console here when ready
111+ // enable 1 sec RTC SSEC alarm
112+ MXC_RTC_DisableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE );
113+ MXC_RTC_SetSubsecondAlarm (MSEC_TO_SS_ALARM (1000 ));
114+ MXC_RTC_EnableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE );
115+
116+ // Enable RTC
117+ while ( MXC_RTC_Start () != E_SUCCESS ) {};
103118
104- MXC_RTC_Start ();
105119 return SAFE_MODE_NONE ;
106120}
107121
122+ void RTC_IRQHandler (void ) {
123+ // Read flags to clear
124+ int flags = MXC_RTC_GetFlags ();
125+
126+ if (flags & MXC_F_RTC_CTRL_SSEC_ALARM ) {
127+ MXC_RTC_ClearFlags (MXC_F_RTC_CTRL_SSEC_ALARM );
128+ }
129+
130+ if (flags & MXC_F_RTC_CTRL_TOD_ALARM ) {
131+ MXC_RTC_ClearFlags (MXC_F_RTC_CTRL_TOD_ALARM );
132+ }
133+
134+ tick_flag = 1 ;
135+ }
136+
108137// Reset the MCU completely
109138void reset_cpu (void ) {
110139 // includes MCU reset request + awaits on memory bus
@@ -114,18 +143,15 @@ void reset_cpu(void) {
114143// Reset MCU state
115144void reset_port (void ) {
116145 reset_all_pins ();
117-
118- // todo: may need rtc-related resets here later
119146}
120147
121148// Reset to the bootloader
122149// note: not implemented since max32 requires external stim ignals to
123150// activate bootloaders
124- // todo: check if there's a method to jump to it
125151void reset_to_bootloader (void ) {
126152 NVIC_SystemReset ();
127153 while (true) {
128- asm ( "nop;" );
154+ __NOP ( );
129155 }
130156}
131157
@@ -169,7 +195,14 @@ uint64_t port_get_raw_ticks(uint8_t *subticks) {
169195 // Ensure we can read from ssec register as soon as we can
170196 // MXC function does cross-tick / busy checking of RTC controller
171197 __disable_irq ();
172- MXC_RTC_GetTime (& sec , & subsec );
198+ if (MXC_RTC -> ctrl & MXC_F_RTC_CTRL_EN ) {
199+ // NOTE: RTC_GetTime always returns BUSY if RTC is not running
200+ while ( (MXC_RTC_GetTime (& sec , & subsec )) != E_NO_ERROR );
201+ }
202+ else {
203+ sec = MXC_RTC -> sec ;
204+ subsec = MXC_RTC -> ssec ;
205+ }
173206 __enable_irq ();
174207
175208 // Return ticks given total subseconds
@@ -197,71 +230,44 @@ void port_disable_tick(void) {
197230
198231// Wake the CPU after a given # of ticks or sooner
199232void port_interrupt_after_ticks (uint32_t ticks ) {
233+ uint32_t ticks_msec = 0 ;
200234 // Stop RTC & store current time & ticks
201235 port_disable_tick ();
202236 port_get_raw_ticks (NULL );
203237
204- uint32_t target_sec = (ticks / TICKS_PER_SEC );
205- uint32_t target_ssec = (ticks - (target_sec * TICKS_PER_SEC )) * SUBSEC_PER_TICK ;
206-
207- // Set up alarm configuration
208- // if alarm is greater than 1 s,
209- // use the ToD alarm --> resol. to closest second
210- // else
211- // use Ssec alarm --> resn. to 1/1024 s. (down to a full tick)
212- if (target_sec > 0 ) {
213- if (MXC_RTC_DisableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE |
214- MXC_F_RTC_CTRL_TOD_ALARM_IE ) == E_BUSY ) {
215- // todo: signal some RTC error!
216- }
238+ ticks_msec = 1000 * ticks / TICKS_PER_SEC ;
217239
218- if (MXC_RTC_SetTimeofdayAlarm (target_sec ) != E_NO_ERROR ) {
219- // todo: signal some RTC error!
220- }
221- if (MXC_RTC_EnableInt (MXC_F_RTC_CTRL_TOD_ALARM_IE ) == E_BUSY ) {
222- // todo: signal some RTC error!
223- }
224- }
225- else {
226- if (MXC_RTC_DisableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE |
227- MXC_F_RTC_CTRL_TOD_ALARM_IE ) == E_BUSY ) {
228- // todo: signal some RTC error!
229- }
240+ while (MXC_RTC_DisableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE |
241+ MXC_F_RTC_CTRL_TOD_ALARM_IE ) == E_BUSY ) {};
230242
231- if (MXC_RTC_SetSubsecondAlarm (target_ssec ) != E_NO_ERROR ) {
232- // todo: signal some RTC error!
233- }
234-
235- if (MXC_RTC_EnableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE ) == E_BUSY ) {
236- // todo: signal some RTC error!
237- }
238- }
239- port_enable_tick ();
240- }
243+ // Clear the flag to be set by the RTC Handler
244+ tick_flag = 0 ;
241245
242- void RTC_IRQHandler (void ) {
243- // Read flags to clear
244- int flags = MXC_RTC_GetFlags ();
246+ // Subsec alarm is the starting/reload value of the SSEC counter.
247+ // ISR triggered when SSEC rolls over from 0xFFFF_FFFF to 0x0
248+ while ( MXC_RTC_SetSubsecondAlarm (MSEC_TO_SS_ALARM (ticks_msec ) ) == E_BUSY ) {}
249+ while (MXC_RTC_EnableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE ) == E_BUSY ) {}
245250
246- if (flags & MXC_F_RTC_CTRL_TOD_ALARM ) {
247- MXC_RTC_ClearFlags (MXC_F_RTC_CTRL_TOD_ALARM );
248- while (MXC_RTC_DisableInt (MXC_F_RTC_CTRL_TOD_ALARM_IE ) == E_BUSY ) {}
249- }
251+ NVIC_EnableIRQ (RTC_IRQn );
250252
251- if (flags & MXC_F_RTC_CTRL_SSEC_ALARM ) {
252- MXC_RTC_ClearFlags (MXC_F_RTC_CTRL_SSEC_ALARM );
253- while (MXC_RTC_DisableInt (MXC_F_RTC_CTRL_SSEC_ALARM_IE ) == E_BUSY ) {}
254- }
253+ port_enable_tick ();
255254}
256255
257256void port_idle_until_interrupt (void ) {
258- // Check if alarm triggers before we even got here
259- if (MXC_RTC_GetFlags () == (MXC_F_RTC_CTRL_TOD_ALARM | MXC_F_RTC_CTRL_SSEC_ALARM )) {
260- return ;
261- }
257+ #if CIRCUITPY_RTC
258+ // Check if alarm triggers before we even got here
259+ if (MXC_RTC_GetFlags () == (MXC_F_RTC_CTRL_TOD_ALARM | MXC_F_RTC_CTRL_SSEC_ALARM )) {
260+ return ;
261+ }
262+ #endif
262263
264+ // Interrupts should be disabled to ensure the ISR queue is flushed
265+ // WFI still returns as long as the interrupt flag toggles;
266+ // only when we re-enable interrupts will the ISR function trigger
263267 common_hal_mcu_disable_interrupts ();
264268 if (!background_callback_pending ()) {
269+ __DSB ();
270+ /** DEBUG: may comment out WFI for debugging port functions */
265271 __WFI ();
266272 }
267273 common_hal_mcu_enable_interrupts ();
0 commit comments