Merge pull request #7965 from dhalbert/stm-rtc-monotonic

STM: monotonic time even when RTC is changed
This commit is contained in:
Scott Shawcroft 2023-05-12 13:43:13 -07:00 committed by GitHub
commit ca01200b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 50 deletions

View File

@ -32,7 +32,9 @@
#include "shared/timeutils/timeutils.h"
// Default period for ticks is 1/1024 second
#define TICK_DIVISOR 1024
#define TICKS_PER_SECOND 1024
// Based on a 32768 kHz clock
#define SUBTICKS_PER_TICK 32
STATIC RTC_HandleTypeDef hrtc;
@ -47,6 +49,13 @@ volatile uint32_t cached_date = 0;
volatile uint32_t seconds_to_minute = 0;
volatile uint32_t cached_hours_minutes = 0;
// The RTC starts at 2000-01-01 when it comes up.
// If the RTC is set to a later time, the ticks the RTC returns will be offset by the new time.
// Remember that offset so it can be removed when returning a monotonic tick count.
static int64_t rtc_ticks_offset;
// Normalized to be 0-31 inclusive, so always positive.
static uint8_t rtc_subticks_offset;
volatile bool alarmed_already[2];
bool peripherals_wkup_on = false;
@ -59,6 +68,9 @@ uint32_t stm32_peripherals_get_rtc_freq(void) {
}
void stm32_peripherals_rtc_init(void) {
rtc_ticks_offset = 0;
rtc_subticks_offset = 0;
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
__HAL_RCC_RTC_ENABLE();
hrtc.Instance = RTC;
@ -74,49 +86,9 @@ void stm32_peripherals_rtc_init(void) {
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
#if CIRCUITPY_RTC
void stm32_peripherals_rtc_get_time(timeutils_struct_time_t *tm) {
RTC_DateTypeDef date = {0};
RTC_TimeTypeDef time = {0};
int code;
if ((code = HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN)) == HAL_OK &&
(code = HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN)) == HAL_OK) {
tm->tm_hour = time.Hours;
tm->tm_min = time.Minutes;
tm->tm_sec = time.Seconds;
tm->tm_wday = date.WeekDay - 1;
tm->tm_mday = date.Date;
tm->tm_mon = date.Month;
tm->tm_year = date.Year + 2000;
tm->tm_yday = -1;
}
}
void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) {
RTC_DateTypeDef date = {0};
RTC_TimeTypeDef time = {0};
time.Hours = tm->tm_hour;
time.Minutes = tm->tm_min;
time.Seconds = tm->tm_sec;
date.WeekDay = tm->tm_wday + 1;
date.Date = tm->tm_mday;
date.Month = tm->tm_mon;
date.Year = tm->tm_year - 2000;
time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
time.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK ||
HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) {
// todo - throw an exception
}
}
#endif
// This function is called often for timing so we cache the seconds elapsed computation based on the
// register value. The STM HAL always does shifts and conversion if we use it directly.
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
STATIC uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
// SSR twice to make sure we didn't read across a tick.
__disable_irq();
@ -157,13 +129,84 @@ uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU));
seconds = (uint8_t)RTC_Bcd2ToByte(seconds);
if (subticks != NULL) {
*subticks = subseconds % 32;
*subticks = subseconds % SUBTICKS_PER_TICK;
}
uint64_t raw_ticks = ((uint64_t)TICK_DIVISOR) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32;
uint64_t raw_ticks = ((uint64_t)TICKS_PER_SECOND) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / SUBTICKS_PER_TICK;
return raw_ticks;
}
// This function returns monotonically increasing ticks by adjusting away the RTC tick offset
// from the last time the date was set.
uint64_t stm32_peripherals_rtc_monotonic_ticks(uint8_t *subticks) {
uint8_t raw_subticks;
uint64_t monotonic_ticks = stm32_peripherals_rtc_raw_ticks(&raw_subticks) - rtc_ticks_offset;
int8_t monotonic_subticks = raw_subticks - rtc_subticks_offset;
// Difference might be negative. Normalize to 0-31.
// `while` not really necessary; should only loop 0 or 1 times.
while (monotonic_subticks < 0) {
monotonic_ticks--;
monotonic_subticks += SUBTICKS_PER_TICK;
}
*subticks = (uint8_t)monotonic_subticks;
return monotonic_ticks;
}
#if CIRCUITPY_RTC
void stm32_peripherals_rtc_get_time(timeutils_struct_time_t *tm) {
RTC_DateTypeDef date = {0};
RTC_TimeTypeDef time = {0};
int code;
if ((code = HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN)) == HAL_OK &&
(code = HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN)) == HAL_OK) {
tm->tm_hour = time.Hours;
tm->tm_min = time.Minutes;
tm->tm_sec = time.Seconds;
tm->tm_wday = date.WeekDay - 1;
tm->tm_mday = date.Date;
tm->tm_mon = date.Month;
tm->tm_year = date.Year + 2000;
tm->tm_yday = -1;
}
}
void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) {
RTC_DateTypeDef date = {0};
RTC_TimeTypeDef time = {0};
uint8_t current_monotonic_subticks;
uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks(&current_monotonic_subticks);
// SubSeconds will always be set to zero.
time.Hours = tm->tm_hour;
time.Minutes = tm->tm_min;
time.Seconds = tm->tm_sec;
date.WeekDay = tm->tm_wday + 1;
date.Date = tm->tm_mday;
date.Month = tm->tm_mon;
date.Year = tm->tm_year - 2000;
time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
time.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK ||
HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) {
// todo - throw an exception
}
uint8_t raw_subticks;
rtc_ticks_offset = stm32_peripherals_rtc_raw_ticks(&raw_subticks) - current_monotonic_ticks;
int8_t rtc_subticks_offset_signed = raw_subticks - current_monotonic_subticks;
// Difference might be negative. Normalize subticks to 0-31.
// `while` not really necessary; should only loop 0 or 1 times.
while (rtc_subticks_offset_signed < 0) {
rtc_ticks_offset--;
rtc_subticks_offset_signed += SUBTICKS_PER_TICK;
}
rtc_subticks_offset = (uint8_t)rtc_subticks_offset_signed;
}
#endif
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)) {
wkup_callback = callback;
}
@ -177,7 +220,7 @@ void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) {
}
void stm32_peripherals_rtc_set_wakeup_mode_tick(void) {
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICK_DIVISOR, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICKS_PER_SECOND, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
}
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) {
uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks(NULL) + ticks;
RTC_AlarmTypeDef alarm;
if (ticks > TICK_DIVISOR) {
if (ticks > TICKS_PER_SECOND) {
timeutils_struct_time_t tm;
timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICK_DIVISOR, &tm);
timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICKS_PER_SECOND, &tm);
alarm.AlarmTime.Hours = tm.tm_hour;
alarm.AlarmTime.Minutes = tm.tm_min;
alarm.AlarmTime.Seconds = tm.tm_sec;
@ -221,7 +264,7 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
}
alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 -
((raw_ticks % TICK_DIVISOR) * 32);
((raw_ticks % TICKS_PER_SECOND) * SUBTICKS_PER_TICK);
if (alarm.AlarmTime.SubSeconds > rtc_clock_frequency) {
alarm.AlarmTime.SubSeconds = alarm.AlarmTime.SubSeconds +
rtc_clock_frequency;

View File

@ -36,7 +36,7 @@
uint32_t stm32_peripherals_get_rtc_freq(void);
void stm32_peripherals_rtc_init(void);
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks);
uint64_t stm32_peripherals_rtc_monotonic_ticks(uint8_t *subticks);
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void));
void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds);

View File

@ -385,7 +385,7 @@ __attribute__((used)) void HardFault_Handler(void) {
}
uint64_t port_get_raw_ticks(uint8_t *subticks) {
return stm32_peripherals_rtc_raw_ticks(subticks);
return stm32_peripherals_rtc_monotonic_ticks(subticks);
}
// Enable 1/1024 second tick.

View File

@ -38,6 +38,8 @@ uint64_t common_hal_time_monotonic_ns(void) {
uint64_t ticks = port_get_raw_ticks(&subticks);
// A tick is 976562.5 nanoseconds so multiply it by the base and add half instead of doing float
// math.
// A subtick is 1/32 of a tick.
// 30518 is 1e9 / 32768
return 976562 * ticks + ticks / 2 + 30518 * subticks;
}