diff --git a/ports/stm/peripherals/rtc.c b/ports/stm/peripherals/rtc.c index 15f82ba78dac..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; @@ -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///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(¤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; + 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; 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. 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; }