Skip to content

Commit c0f209a

Browse files
committed
feat: add rtc drivers
1 parent 1096130 commit c0f209a

2 files changed

Lines changed: 287 additions & 22 deletions

File tree

src/fw/drivers/sf32lb/stubs/rtc.c

Lines changed: 274 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,44 +20,223 @@
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.
3451
static 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+
69279
void 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

72291
void 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

75323
RtcTicks rtc_alarm_get_elapsed_ticks(void) {
76-
return 0;
324+
return rtc_get_ticks() - s_alarm_set_time;
77325
}
78326

79327
bool 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+
83343
bool 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
130389
void rtc_set_timezone(TimezoneInfo *tzinfo) {
@@ -150,10 +409,3 @@ void rtc_enable_backup_regs(void) {
150409

151410
void rtc_calibrate_frequency(uint32_t frequency) {
152411
}
153-
154-
void rtc_init(void) {
155-
156-
}
157-
158-
void rtc_init_timers(void) {
159-
}

src/fw/system/rtc_registers.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@
4545
// zero them out and give up.
4646
#define NRF_RETAINED_REGISTER_CRC 31
4747

48+
#elif MICRO_FAMILY_SF32LB
49+
#define RTC_BKP_BOOTBIT_DR 0
50+
#define STUCK_BUTTON_REGISTER 1
51+
#define BOOTLOADER_VERSION_REGISTER 2
52+
#define CURRENT_TIME_REGISTER 3
53+
#define CURRENT_INTERVAL_TICKS_REGISTER 4
54+
#define REBOOT_REASON_REGISTER_1 5
55+
#define REBOOT_REASON_REGISTER_2 6
56+
#define REBOOT_REASON_STUCK_TASK_PC 7
57+
#define REBOOT_REASON_STUCK_TASK_LR 8
58+
#define REBOOT_REASON_STUCK_TASK_CALLBACK 9
59+
#define REBOOT_REASON_DROPPED_EVENT 10
60+
4861
#else
4962
#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0
5063
#define STUCK_BUTTON_REGISTER RTC_BKP_DR1

0 commit comments

Comments
 (0)