From 0b2f8575423177d5c336c5d64dc85befa11ad878 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 12 May 2023 00:28:37 -0400 Subject: [PATCH 1/2] STM: monotonic time even when RTC is changed --- ports/stm/peripherals/rtc.c | 100 +++++++++++++++++++++--------------- ports/stm/peripherals/rtc.h | 2 +- ports/stm/supervisor/port.c | 2 +- 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/ports/stm/peripherals/rtc.c b/ports/stm/peripherals/rtc.c index 15f82ba78dac..c6cccfc93ba9 100644 --- a/ports/stm/peripherals/rtc.c +++ b/ports/stm/peripherals/rtc.c @@ -47,6 +47,11 @@ 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; + volatile bool alarmed_already[2]; bool peripherals_wkup_on = false; @@ -59,6 +64,8 @@ uint32_t stm32_peripherals_get_rtc_freq(void) { } void stm32_peripherals_rtc_init(void) { + rtc_ticks_offset = 0; + // RTC oscillator selection is handled in peripherals///clocks.c __HAL_RCC_RTC_ENABLE(); hrtc.Instance = RTC; @@ -74,49 +81,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(); @@ -164,6 +131,57 @@ uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) { 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) { + return stm32_peripherals_rtc_raw_ticks(subticks) - rtc_ticks_offset; +} + +#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}; + + uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks(NULL); + + 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 + } + + rtc_ticks_offset = stm32_peripherals_rtc_raw_ticks(NULL) - current_monotonic_ticks; + ; +} +#endif + void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)) { wkup_callback = callback; } diff --git a/ports/stm/peripherals/rtc.h b/ports/stm/peripherals/rtc.h index 474cba1881cd..93febbb5ccd1 100644 --- a/ports/stm/peripherals/rtc.h +++ b/ports/stm/peripherals/rtc.h @@ -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); diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index 1fb3f363774f..0a8d9a5b1902 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -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. From e8067851f5958c72ec828b14186affc22b8a5d60 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 12 May 2023 14:39:57 -0400 Subject: [PATCH 2/2] fix subticks handling --- ports/stm/peripherals/rtc.c | 47 +++++++++++++++++++++++++++-------- shared-module/time/__init__.c | 2 ++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/ports/stm/peripherals/rtc.c b/ports/stm/peripherals/rtc.c index c6cccfc93ba9..7c8cb65e17cd 100644 --- a/ports/stm/peripherals/rtc.c +++ b/ports/stm/peripherals/rtc.c @@ -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; @@ -51,6 +53,8 @@ volatile uint32_t cached_hours_minutes = 0; // 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]; @@ -65,6 +69,7 @@ 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///clocks.c __HAL_RCC_RTC_ENABLE(); @@ -124,17 +129,27 @@ STATIC 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) { - return stm32_peripherals_rtc_raw_ticks(subticks) - rtc_ticks_offset; + 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 @@ -160,8 +175,10 @@ void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) { RTC_DateTypeDef date = {0}; RTC_TimeTypeDef time = {0}; - uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks(NULL); + uint8_t current_monotonic_subticks; + uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks(¤t_monotonic_subticks); + // SubSeconds will always be set to zero. time.Hours = tm->tm_hour; time.Minutes = tm->tm_min; time.Seconds = tm->tm_sec; @@ -177,8 +194,16 @@ void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) { // todo - throw an exception } - rtc_ticks_offset = stm32_peripherals_rtc_raw_ticks(NULL) - current_monotonic_ticks; - ; + 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 @@ -195,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) { @@ -223,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; @@ -239,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; diff --git a/shared-module/time/__init__.c b/shared-module/time/__init__.c index b4575b18a2cf..7d4f585ea1c9 100644 --- a/shared-module/time/__init__.c +++ b/shared-module/time/__init__.c @@ -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; }