3232#include "shared/timeutils/timeutils.h"
3333
3434// Default period for ticks is 1/1024 second
35- #define TICK_DIVISOR 1024
35+ #define TICKS_PER_SECOND 1024
36+ // Based on a 32768 kHz clock
37+ #define SUBTICKS_PER_TICK 32
3638
3739STATIC RTC_HandleTypeDef hrtc ;
3840
@@ -47,6 +49,13 @@ volatile uint32_t cached_date = 0;
4749volatile uint32_t seconds_to_minute = 0 ;
4850volatile uint32_t cached_hours_minutes = 0 ;
4951
52+ // The RTC starts at 2000-01-01 when it comes up.
53+ // If the RTC is set to a later time, the ticks the RTC returns will be offset by the new time.
54+ // Remember that offset so it can be removed when returning a monotonic tick count.
55+ static int64_t rtc_ticks_offset ;
56+ // Normalized to be 0-31 inclusive, so always positive.
57+ static uint8_t rtc_subticks_offset ;
58+
5059volatile bool alarmed_already [2 ];
5160
5261bool peripherals_wkup_on = false;
@@ -59,6 +68,9 @@ uint32_t stm32_peripherals_get_rtc_freq(void) {
5968}
6069
6170void stm32_peripherals_rtc_init (void ) {
71+ rtc_ticks_offset = 0 ;
72+ rtc_subticks_offset = 0 ;
73+
6274 // RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
6375 __HAL_RCC_RTC_ENABLE ();
6476 hrtc .Instance = RTC ;
@@ -74,49 +86,9 @@ void stm32_peripherals_rtc_init(void) {
7486 HAL_NVIC_EnableIRQ (RTC_Alarm_IRQn );
7587}
7688
77- #if CIRCUITPY_RTC
78- void stm32_peripherals_rtc_get_time (timeutils_struct_time_t * tm ) {
79- RTC_DateTypeDef date = {0 };
80- RTC_TimeTypeDef time = {0 };
81-
82- int code ;
83- if ((code = HAL_RTC_GetTime (& hrtc , & time , RTC_FORMAT_BIN )) == HAL_OK &&
84- (code = HAL_RTC_GetDate (& hrtc , & date , RTC_FORMAT_BIN )) == HAL_OK ) {
85- tm -> tm_hour = time .Hours ;
86- tm -> tm_min = time .Minutes ;
87- tm -> tm_sec = time .Seconds ;
88- tm -> tm_wday = date .WeekDay - 1 ;
89- tm -> tm_mday = date .Date ;
90- tm -> tm_mon = date .Month ;
91- tm -> tm_year = date .Year + 2000 ;
92- tm -> tm_yday = -1 ;
93- }
94- }
95-
96- void stm32_peripherals_rtc_set_time (timeutils_struct_time_t * tm ) {
97- RTC_DateTypeDef date = {0 };
98- RTC_TimeTypeDef time = {0 };
99-
100- time .Hours = tm -> tm_hour ;
101- time .Minutes = tm -> tm_min ;
102- time .Seconds = tm -> tm_sec ;
103- date .WeekDay = tm -> tm_wday + 1 ;
104- date .Date = tm -> tm_mday ;
105- date .Month = tm -> tm_mon ;
106- date .Year = tm -> tm_year - 2000 ;
107- time .DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
108- time .StoreOperation = RTC_STOREOPERATION_RESET ;
109-
110- if (HAL_RTC_SetTime (& hrtc , & time , RTC_FORMAT_BIN ) != HAL_OK ||
111- HAL_RTC_SetDate (& hrtc , & date , RTC_FORMAT_BIN ) != HAL_OK ) {
112- // todo - throw an exception
113- }
114- }
115- #endif
116-
11789// This function is called often for timing so we cache the seconds elapsed computation based on the
11890// register value. The STM HAL always does shifts and conversion if we use it directly.
119- uint64_t stm32_peripherals_rtc_raw_ticks (uint8_t * subticks ) {
91+ STATIC uint64_t stm32_peripherals_rtc_raw_ticks (uint8_t * subticks ) {
12092 // Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
12193 // SSR twice to make sure we didn't read across a tick.
12294 __disable_irq ();
@@ -157,13 +129,84 @@ uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
157129 uint8_t seconds = (uint8_t )(time & (RTC_TR_ST | RTC_TR_SU ));
158130 seconds = (uint8_t )RTC_Bcd2ToByte (seconds );
159131 if (subticks != NULL ) {
160- * subticks = subseconds % 32 ;
132+ * subticks = subseconds % SUBTICKS_PER_TICK ;
161133 }
162134
163- uint64_t raw_ticks = ((uint64_t )TICK_DIVISOR ) * (seconds_to_date + seconds_to_minute + seconds ) + subseconds / 32 ;
135+ uint64_t raw_ticks = ((uint64_t )TICKS_PER_SECOND ) * (seconds_to_date + seconds_to_minute + seconds ) + subseconds / SUBTICKS_PER_TICK ;
164136 return raw_ticks ;
165137}
166138
139+ // This function returns monotonically increasing ticks by adjusting away the RTC tick offset
140+ // from the last time the date was set.
141+ uint64_t stm32_peripherals_rtc_monotonic_ticks (uint8_t * subticks ) {
142+ uint8_t raw_subticks ;
143+ uint64_t monotonic_ticks = stm32_peripherals_rtc_raw_ticks (& raw_subticks ) - rtc_ticks_offset ;
144+ int8_t monotonic_subticks = raw_subticks - rtc_subticks_offset ;
145+ // Difference might be negative. Normalize to 0-31.
146+ // `while` not really necessary; should only loop 0 or 1 times.
147+ while (monotonic_subticks < 0 ) {
148+ monotonic_ticks -- ;
149+ monotonic_subticks += SUBTICKS_PER_TICK ;
150+ }
151+ * subticks = (uint8_t )monotonic_subticks ;
152+ return monotonic_ticks ;
153+ }
154+
155+ #if CIRCUITPY_RTC
156+ void stm32_peripherals_rtc_get_time (timeutils_struct_time_t * tm ) {
157+ RTC_DateTypeDef date = {0 };
158+ RTC_TimeTypeDef time = {0 };
159+
160+ int code ;
161+ if ((code = HAL_RTC_GetTime (& hrtc , & time , RTC_FORMAT_BIN )) == HAL_OK &&
162+ (code = HAL_RTC_GetDate (& hrtc , & date , RTC_FORMAT_BIN )) == HAL_OK ) {
163+ tm -> tm_hour = time .Hours ;
164+ tm -> tm_min = time .Minutes ;
165+ tm -> tm_sec = time .Seconds ;
166+ tm -> tm_wday = date .WeekDay - 1 ;
167+ tm -> tm_mday = date .Date ;
168+ tm -> tm_mon = date .Month ;
169+ tm -> tm_year = date .Year + 2000 ;
170+ tm -> tm_yday = -1 ;
171+ }
172+ }
173+
174+ void stm32_peripherals_rtc_set_time (timeutils_struct_time_t * tm ) {
175+ RTC_DateTypeDef date = {0 };
176+ RTC_TimeTypeDef time = {0 };
177+
178+ uint8_t current_monotonic_subticks ;
179+ uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks (& current_monotonic_subticks );
180+
181+ // SubSeconds will always be set to zero.
182+ time .Hours = tm -> tm_hour ;
183+ time .Minutes = tm -> tm_min ;
184+ time .Seconds = tm -> tm_sec ;
185+ date .WeekDay = tm -> tm_wday + 1 ;
186+ date .Date = tm -> tm_mday ;
187+ date .Month = tm -> tm_mon ;
188+ date .Year = tm -> tm_year - 2000 ;
189+ time .DayLightSaving = RTC_DAYLIGHTSAVING_NONE ;
190+ time .StoreOperation = RTC_STOREOPERATION_RESET ;
191+
192+ if (HAL_RTC_SetTime (& hrtc , & time , RTC_FORMAT_BIN ) != HAL_OK ||
193+ HAL_RTC_SetDate (& hrtc , & date , RTC_FORMAT_BIN ) != HAL_OK ) {
194+ // todo - throw an exception
195+ }
196+
197+ uint8_t raw_subticks ;
198+ rtc_ticks_offset = stm32_peripherals_rtc_raw_ticks (& raw_subticks ) - current_monotonic_ticks ;
199+ int8_t rtc_subticks_offset_signed = raw_subticks - current_monotonic_subticks ;
200+ // Difference might be negative. Normalize subticks to 0-31.
201+ // `while` not really necessary; should only loop 0 or 1 times.
202+ while (rtc_subticks_offset_signed < 0 ) {
203+ rtc_ticks_offset -- ;
204+ rtc_subticks_offset_signed += SUBTICKS_PER_TICK ;
205+ }
206+ rtc_subticks_offset = (uint8_t )rtc_subticks_offset_signed ;
207+ }
208+ #endif
209+
167210void stm32_peripherals_rtc_assign_wkup_callback (void (* callback )(void )) {
168211 wkup_callback = callback ;
169212}
@@ -177,7 +220,7 @@ void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) {
177220}
178221
179222void stm32_peripherals_rtc_set_wakeup_mode_tick (void ) {
180- HAL_RTCEx_SetWakeUpTimer_IT (& hrtc , (rtc_clock_frequency / 16 ) / TICK_DIVISOR , RTC_WAKEUPCLOCK_RTCCLK_DIV2 );
223+ HAL_RTCEx_SetWakeUpTimer_IT (& hrtc , (rtc_clock_frequency / 16 ) / TICKS_PER_SECOND , RTC_WAKEUPCLOCK_RTCCLK_DIV2 );
181224}
182225
183226void stm32_peripherals_rtc_enable_wakeup_timer (void ) {
@@ -205,9 +248,9 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
205248 uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks (NULL ) + ticks ;
206249
207250 RTC_AlarmTypeDef alarm ;
208- if (ticks > TICK_DIVISOR ) {
251+ if (ticks > TICKS_PER_SECOND ) {
209252 timeutils_struct_time_t tm ;
210- timeutils_seconds_since_2000_to_struct_time (raw_ticks / TICK_DIVISOR , & tm );
253+ timeutils_seconds_since_2000_to_struct_time (raw_ticks / TICKS_PER_SECOND , & tm );
211254 alarm .AlarmTime .Hours = tm .tm_hour ;
212255 alarm .AlarmTime .Minutes = tm .tm_min ;
213256 alarm .AlarmTime .Seconds = tm .tm_sec ;
@@ -221,7 +264,7 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
221264 }
222265
223266 alarm .AlarmTime .SubSeconds = rtc_clock_frequency - 1 -
224- ((raw_ticks % TICK_DIVISOR ) * 32 );
267+ ((raw_ticks % TICKS_PER_SECOND ) * SUBTICKS_PER_TICK );
225268 if (alarm .AlarmTime .SubSeconds > rtc_clock_frequency ) {
226269 alarm .AlarmTime .SubSeconds = alarm .AlarmTime .SubSeconds +
227270 rtc_clock_frequency ;
0 commit comments