2020
2121#include "util/time/time.h"
2222
23- #define NRF5_COMPATIBLE
2423#include <mcu.h>
2524
2625#include <inttypes.h>
2726#include <stdio.h>
2827#include <string.h>
2928
29+ static RTC_HandleTypeDef RTC_Handler = {
30+ .Instance = (RTC_TypeDef * ) RTC_BASE
31+ };
32+
33+ typedef uint32_t RtcIntervalTicks ;
34+
35+ static const unsigned int LSE_FREQUENCY_HZ = 32768 ;
36+ #define SECONDS_IN_A_DAY (60 * 60 * 24)
37+ #define TICKS_IN_AN_INTERVAL SECONDS_IN_A_DAY
38+
39+ //! This variable is a UNIX timestamp of what the current wall clock time was at tick s_time_tick_base.
40+ static time_t s_time_base = 0 ;
41+ //! This variable is the tick where the wall clock time was equal to s_time_base. If you subtract this variable
42+ //! from the current tick count, you'll get the number of ticks that have elapsed since s_time_base, which will
43+ //! allow you to calculate the current wall clock time. Note that this value may be negative on startup, see
44+ //! restore_rtc_time_state
45+ static int64_t s_time_tick_base = 0 ;
46+
3047//! The value of the RTC registers last time we checked them.
31- static uint32_t s_last_ticks = 0 ;
48+ static RtcIntervalTicks s_last_ticks = 0 ;
3249//! This value is added to the current value of the RTC ticks to get the number
3350//! of ticks since system start. Incremented whenever we detect a rollover.
3451static RtcTicks s_coarse_ticks = 1 ;
3552
36- void rtc_set_time (time_t time ) {
53+ //! The time that we last set the alarm at. See rtc_alarm_set and rtc_alarm_get_elapsed_ticks.
54+ static RtcTicks s_alarm_set_time = 0 ;
55+
56+ static bool s_tick_alarm_initialized = false;
57+
58+ static void save_rtc_time_state (RtcIntervalTicks current_rtc_ticks );
59+
60+ //! Our RTC tick counter actually overflows once every 86 seconds. If we don't call rtc_get_ticks() every 86 seconds,
61+ //! the counter may roll over multiple times, causing our clock to appear to have gaps. This repeating callback allows
62+ //! us to make sure this doesn't happen.
63+ static void rtc_resync_timer_callback () {
64+ rtc_get_ticks ();
65+ }
66+
67+ static uint8_t BcdToByte (uint8_t Value ) {
68+ const uint8_t tmp = ((uint8_t )(Value & (uint8_t )0xF0 ) >> (uint8_t )0x4 ) * 10 ;
69+ return (tmp + (Value & (uint8_t )0x0F ));
70+ }
71+
72+ static RtcIntervalTicks get_rtc_interval_ticks (void ) {
73+ uint32_t time_register = RTC_Handler .Instance -> TR ;
74+
75+ const uint8_t hours = BcdToByte ((time_register & (RTC_TR_HT | RTC_TR_HU )) >> 16 );
76+ const uint8_t minutes = BcdToByte ((time_register & (RTC_TR_MNT | RTC_TR_MNU )) >> 8 );
77+ const uint8_t seconds = BcdToByte (time_register & (RTC_TR_ST | RTC_TR_SU ));
78+
79+ return (((hours * 60 ) + minutes ) * 60 ) + seconds ;
80+ }
81+
82+ static RtcIntervalTicks elapsed_ticks (RtcIntervalTicks before , RtcIntervalTicks after ) {
83+ int32_t result = after - before ;
84+ if (result < 0 ) {
85+ result = (TICKS_IN_AN_INTERVAL - before ) + after ;
86+ }
87+ return result ;
88+ }
89+
90+ static void restore_rtc_time_state (void ) {
91+ // Recover the previously set time from the RTC backup registers.
92+ RtcIntervalTicks last_save_time_ticks = HAL_Get_backup (CURRENT_INTERVAL_TICKS_REGISTER );
93+ time_t last_save_time = HAL_Get_backup (CURRENT_TIME_REGISTER );
94+
95+ RtcIntervalTicks current_ticks = get_rtc_interval_ticks ();
96+ const int32_t ticks_since_last_save = elapsed_ticks (last_save_time_ticks , current_ticks );
97+ s_time_base = last_save_time + (ticks_since_last_save / RTC_TICKS_HZ );
98+
99+ s_time_tick_base = - (((int64_t )current_ticks ) % RTC_TICKS_HZ );
100+
101+ #ifdef VERBOSE_LOGGING
102+ char buffer [TIME_STRING_BUFFER_SIZE ];
103+ PBL_LOG_VERBOSE ("Restore RTC: saved: %" PRIu32 " diff: %" PRIu32 , last_save_time_ticks , ticks_since_last_save );
104+ PBL_LOG_VERBOSE ("Restore RTC: saved_time: %s raw: %lu" , time_t_to_string (buffer , last_save_time ), last_save_time );
105+ PBL_LOG_VERBOSE ("Restore RTC: current time: %s" , time_t_to_string (buffer , s_time_base ));
106+ PBL_LOG_VERBOSE ("Restore RTC: s_time_tick_base: %" PRId64 , s_time_tick_base );
107+ #endif
108+ }
109+
110+ static time_t ticks_to_time (RtcTicks ticks ) {
111+ return s_time_base + ((ticks - s_time_tick_base ) / RTC_TICKS_HZ );
112+ }
113+
114+ static RtcIntervalTicks get_last_save_time_ticks (void ) {
115+ return HAL_Get_backup (CURRENT_INTERVAL_TICKS_REGISTER );
116+ }
117+
118+ static void save_rtc_time_state_exact (RtcIntervalTicks current_rtc_ticks , time_t time ) {
119+ HAL_Set_backup (CURRENT_TIME_REGISTER , time );
120+ HAL_Set_backup (CURRENT_INTERVAL_TICKS_REGISTER , current_rtc_ticks );
121+
122+ // Dbgserial instead of PBL_LOG to avoid infinite recursion due to PBL_LOG wanting to know
123+ // the current ticks.
124+ //char buffer[128];
125+ //dbgserial_putstr_fmt(buffer, 128, "Saving RTC state: ticks: %"PRIu32" time: %s raw: %lu", current_rtc_ticks, time_t_to_string(time), time);
126+ //itoa(time, buffer, sizeof(buffer));
127+ //dbgserial_putstr(buffer);
128+ //dbgserial_putstr("Done");
129+ }
130+
131+ static void save_rtc_time_state (RtcIntervalTicks current_rtc_ticks ) {
132+ // Floor it to the latest second
133+ const RtcIntervalTicks current_rtc_ticks_at_second = (current_rtc_ticks / RTC_TICKS_HZ ) * RTC_TICKS_HZ ;
134+
135+ save_rtc_time_state_exact (current_rtc_ticks_at_second , ticks_to_time (s_coarse_ticks + current_rtc_ticks ));
136+ }
137+
138+ static void initialize_fast_mode_state (void ) {
139+ RtcIntervalTicks before_ticks = get_rtc_interval_ticks ();
140+
141+ // Set the RTC to value 0 so we start from scratch nicely
142+ RTC_TimeTypeDef rtc_time = {
143+ .Seconds = 0 ,
144+ .Minutes = 0 ,
145+ .Hours = 0
146+ };
147+ // RTC_TimeStructInit(&rtc_time);
148+ HAL_RTC_SetTime (& RTC_Handler , & rtc_time , RTC_FORMAT_BIN );
149+
150+ // Reset the last ticks counter so we don't rollover prematurely.
151+ // This value will be set to non-zero if anyone asked for the tick count
152+ // before this point.
153+ s_last_ticks = 0 ;
154+
155+ // Refresh the saved time so it's more current.
156+ save_rtc_time_state_exact (TICKS_IN_AN_INTERVAL - (RTC_TICKS_HZ - (before_ticks % RTC_TICKS_HZ )), ticks_to_time (s_coarse_ticks ));
157+ //save_rtc_time_state(0);
158+ }
159+
160+ void rtc_init (void ) {
161+ periph_config_acquire_lock ();
162+ rtc_enable_backup_regs ();
163+ #ifdef LXT_DISABLE
164+ #error "RTC LXT is disabled, but RTC init requires it to be enabled"
165+ #endif
166+ #ifdef SF32LB52X
167+ if (HAL_PMU_LXTReady () != HAL_OK )
168+ #else
169+ if (HAL_RTC_LXT_ENABLED () && HAL_PMU_LXTReady () != HAL_OK )
170+ #endif
171+ {
172+ WTF ;
173+ }
174+ RTC_Handler .Init .DivAInt = 0x80 ;
175+ RTC_Handler .Init .DivAFrac = 0x0 ;
176+ RTC_Handler .Init .DivB = 0x100 ;
177+ uint32_t wakesrc = RTC_INIT_NORMAL ;
178+ if (HAL_RTC_Init (& RTC_Handler , wakesrc ) != HAL_OK )
179+ {
180+ WTF ;
181+ }
182+
183+ periph_config_release_lock ();
184+
185+ restore_rtc_time_state ();
186+ initialize_fast_mode_state ();
187+
37188#ifdef PBL_LOG_ENABLED
38189 char buffer [TIME_STRING_BUFFER_SIZE ];
39- PBL_LOG (LOG_LEVEL_INFO , "Setting time to %lu <%s>" , time , time_t_to_string (buffer , time ));
190+ PBL_LOG (LOG_LEVEL_DEBUG , "Current time is <%s>" , rtc_get_time_string (buffer ));
40191#endif
41192}
42193
43- time_t rtc_get_time (void ) {
44- return 0 ;
194+ void rtc_init_timers (void ) {
195+ static RegularTimerInfo rtc_sync_timer = { .list_node = { 0 , 0 }, .cb = rtc_resync_timer_callback };
196+ regular_timer_add_minutes_callback (& rtc_sync_timer );
45197}
46198
47- void rtc_get_time_ms (time_t * out_seconds , uint16_t * out_ms ) {
48- * out_seconds = 0 ;
49- * out_ms = 0 ;
199+ //! How frequently we save the time state to the backup registers in ticks.
200+ #define SAVE_TIME_FREQUENCY (30 * RTC_TICKS_HZ)
201+
202+ static void check_and_handle_rollover (RtcIntervalTicks rtc_ticks ) {
203+ bool save_needed = false;
204+
205+ const RtcIntervalTicks last_ticks = s_last_ticks ;
206+ s_last_ticks = rtc_ticks ;
207+
208+ if (rtc_ticks < last_ticks ) {
209+ // We've wrapped. Add on the number of seconds in a day to the base number.
210+ s_coarse_ticks += TICKS_IN_AN_INTERVAL ;
211+
212+ save_needed = true;
213+ } else if (elapsed_ticks (get_last_save_time_ticks (), rtc_ticks ) > SAVE_TIME_FREQUENCY ) {
214+ // If we didn't do this, we would have an edge case where if the watch reset
215+ // immediately before rollover and then rolled over before we booted again,
216+ // we wouldn't be able to detect the rollover and we'd think the saved state
217+ // is very fresh, when really it's over an interval old. By saving multiple
218+ // times an interval this is still possible to happen, but it's much less likely.
219+ // We would need to be shutdown for (SECONDS_IN_A_DAY - SAVE_TIME_FREQUENCY) ticks
220+ // for this to happen.
221+ save_needed = true;
222+ }
223+
224+
225+ if (save_needed ) {
226+ save_rtc_time_state (rtc_ticks );
227+ }
50228}
51229
52- RtcTicks rtc_get_ticks (void ) {
230+ static RtcTicks get_ticks (void ) {
231+
53232 // Prevent this from being interrupted
54233 bool ints_enabled = mcu_state_are_interrupts_enabled ();
55234 if (ints_enabled ) {
56235 __disable_irq ();
57236 }
58237
59- // TODO
60- RtcTicks rtc_interval_ticks = 1 ;
238+ RtcTicks rtc_interval_ticks = get_rtc_interval_ticks ();
239+ check_and_handle_rollover ( rtc_interval_ticks ) ;
61240
62241 if (ints_enabled ) {
63242 __enable_irq ();
@@ -66,20 +245,101 @@ RtcTicks rtc_get_ticks(void) {
66245 return s_coarse_ticks + rtc_interval_ticks ;
67246}
68247
248+ void rtc_set_time (time_t time ) {
249+ #ifdef PBL_LOG_ENABLED
250+ char buffer [TIME_STRING_BUFFER_SIZE ];
251+ PBL_LOG (LOG_LEVEL_INFO , "Setting time to %lu <%s>" , time , time_t_to_string (buffer , time ));
252+ #endif
253+
254+ s_time_base = time ;
255+ s_time_tick_base = get_ticks ();
256+
257+ save_rtc_time_state (s_time_tick_base - s_coarse_ticks );
258+ }
259+
260+ time_t rtc_get_time (void ) {
261+ return ticks_to_time (get_ticks ());
262+ }
263+
264+ void rtc_get_time_ms (time_t * out_seconds , uint16_t * out_ms ) {
265+ RtcTicks ticks = get_ticks ();
266+
267+ RtcTicks ticks_since_time_base = (ticks - s_time_tick_base );
268+ * out_seconds = s_time_base + (ticks_since_time_base / RTC_TICKS_HZ );
269+
270+ RtcTicks ticks_this_second = ticks_since_time_base % RTC_TICKS_HZ ;
271+ * out_ms = (ticks_this_second * 1000 ) / RTC_TICKS_HZ ;
272+ }
273+
274+ RtcTicks rtc_get_ticks (void ) {
275+ return get_ticks ();
276+ }
277+
278+
69279void rtc_alarm_init (void ) {
280+ // RTC_ITConfig(RTC_IT_ALRA, DISABLE);
281+ // RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
282+
283+ // RTC_ClearITPendingBit(RTC_IT_ALRA);
284+
285+ // exti_configure_other(ExtiLineOther_RTCAlarm, ExtiTrigger_Rising);
286+ // exti_enable_other(ExtiLineOther_RTCAlarm);
287+
288+ // s_tick_alarm_initialized = true;
70289}
71290
72291void rtc_alarm_set (RtcTicks num_ticks ) {
292+ // PBL_ASSERTN(s_tick_alarm_initialized);
293+
294+ // RTC_ITConfig(RTC_IT_ALRA, DISABLE);
295+ // RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
296+
297+ // RTC_AlarmTypeDef alarm_config;
298+ // RTC_AlarmStructInit(&alarm_config);
299+ // alarm_config.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay;
300+
301+ // s_alarm_set_time = rtc_get_ticks();
302+
303+ // RtcTicks alarm_expiry_time = s_alarm_set_time + num_ticks;
304+
305+ // uint32_t days, hours, minutes, seconds;
306+ // time_util_split_seconds_into_parts(alarm_expiry_time, &days, &hours, &minutes, &seconds);
307+
308+ // (void) days; // Don't care about days.
309+ // alarm_config.RTC_AlarmTime.RTC_Hours = hours;
310+ // alarm_config.RTC_AlarmTime.RTC_Minutes = minutes;
311+ // alarm_config.RTC_AlarmTime.RTC_Seconds = seconds;
312+
313+ // RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &alarm_config);
314+
315+ // RTC_ITConfig(RTC_IT_ALRA, ENABLE);
316+ // RTC_AlarmCmd(RTC_Alarm_A, ENABLE);
317+
318+ // RTC_ClearFlag(RTC_FLAG_ALRAF);
319+ // EXTI_ClearITPendingBit(EXTI_Line17);
320+ // RTC_ClearITPendingBit(RTC_IT_ALRA);
73321}
74322
75323RtcTicks rtc_alarm_get_elapsed_ticks (void ) {
76- return 0 ;
324+ return rtc_get_ticks () - s_alarm_set_time ;
77325}
78326
79327bool rtc_alarm_is_initialized (void ) {
80- return 0 ;
328+ return s_tick_alarm_initialized ;
81329}
82330
331+
332+ //! Handler for the RTC alarm interrupt. We don't actually have to do anything in this handler,
333+ //! just the interrupt firing is enough to bring us out of stop mode.
334+ // void RTC_Alarm_IRQHandler(void) {
335+ // if (RTC_GetITStatus(RTC_IT_ALRA) != RESET) {
336+ // RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
337+
338+ // RTC_ClearITPendingBit(RTC_IT_ALRA);
339+ // EXTI_ClearITPendingBit(EXTI_Line17);
340+ // }
341+ // }
342+
83343bool rtc_sanitize_struct_tm (struct tm * t ) {
84344 // These values come from time_t (which suffers from the 2038 problem) and our hardware which
85345 // only stores a 2 digit year, so we only represent values after 2000.
@@ -124,7 +384,6 @@ const char* time_t_to_string(char* buffer, time_t t) {
124384 return buffer ;
125385}
126386
127-
128387//! We attempt to save registers by placing both the timezone abbreviation
129388//! timezone index and the daylight_savingtime into the same register set
130389void rtc_set_timezone (TimezoneInfo * tzinfo ) {
@@ -150,10 +409,3 @@ void rtc_enable_backup_regs(void) {
150409
151410void rtc_calibrate_frequency (uint32_t frequency ) {
152411}
153-
154- void rtc_init (void ) {
155-
156- }
157-
158- void rtc_init_timers (void ) {
159- }
0 commit comments