32
32
#include "shared/timeutils/timeutils.h"
33
33
34
34
// 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
36
38
37
39
STATIC RTC_HandleTypeDef hrtc ;
38
40
@@ -47,6 +49,13 @@ volatile uint32_t cached_date = 0;
47
49
volatile uint32_t seconds_to_minute = 0 ;
48
50
volatile uint32_t cached_hours_minutes = 0 ;
49
51
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
+
50
59
volatile bool alarmed_already [2 ];
51
60
52
61
bool peripherals_wkup_on = false;
@@ -59,6 +68,9 @@ uint32_t stm32_peripherals_get_rtc_freq(void) {
59
68
}
60
69
61
70
void stm32_peripherals_rtc_init (void ) {
71
+ rtc_ticks_offset = 0 ;
72
+ rtc_subticks_offset = 0 ;
73
+
62
74
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
63
75
__HAL_RCC_RTC_ENABLE ();
64
76
hrtc .Instance = RTC ;
@@ -74,49 +86,9 @@ void stm32_peripherals_rtc_init(void) {
74
86
HAL_NVIC_EnableIRQ (RTC_Alarm_IRQn );
75
87
}
76
88
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
-
117
89
// This function is called often for timing so we cache the seconds elapsed computation based on the
118
90
// 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 ) {
120
92
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
121
93
// SSR twice to make sure we didn't read across a tick.
122
94
__disable_irq ();
@@ -157,13 +129,84 @@ uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
157
129
uint8_t seconds = (uint8_t )(time & (RTC_TR_ST | RTC_TR_SU ));
158
130
seconds = (uint8_t )RTC_Bcd2ToByte (seconds );
159
131
if (subticks != NULL ) {
160
- * subticks = subseconds % 32 ;
132
+ * subticks = subseconds % SUBTICKS_PER_TICK ;
161
133
}
162
134
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 ;
164
136
return raw_ticks ;
165
137
}
166
138
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
+
167
210
void stm32_peripherals_rtc_assign_wkup_callback (void (* callback )(void )) {
168
211
wkup_callback = callback ;
169
212
}
@@ -177,7 +220,7 @@ void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) {
177
220
}
178
221
179
222
void 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 );
181
224
}
182
225
183
226
void stm32_peripherals_rtc_enable_wakeup_timer (void ) {
@@ -205,9 +248,9 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
205
248
uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks (NULL ) + ticks ;
206
249
207
250
RTC_AlarmTypeDef alarm ;
208
- if (ticks > TICK_DIVISOR ) {
251
+ if (ticks > TICKS_PER_SECOND ) {
209
252
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 );
211
254
alarm .AlarmTime .Hours = tm .tm_hour ;
212
255
alarm .AlarmTime .Minutes = tm .tm_min ;
213
256
alarm .AlarmTime .Seconds = tm .tm_sec ;
@@ -221,7 +264,7 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
221
264
}
222
265
223
266
alarm .AlarmTime .SubSeconds = rtc_clock_frequency - 1 -
224
- ((raw_ticks % TICK_DIVISOR ) * 32 );
267
+ ((raw_ticks % TICKS_PER_SECOND ) * SUBTICKS_PER_TICK );
225
268
if (alarm .AlarmTime .SubSeconds > rtc_clock_frequency ) {
226
269
alarm .AlarmTime .SubSeconds = alarm .AlarmTime .SubSeconds +
227
270
rtc_clock_frequency ;
0 commit comments