From 10529932361900c9472b262241f955f433c5278b Mon Sep 17 00:00:00 2001 From: jeromecoutant Date: Fri, 29 Jun 2018 14:03:32 +0200 Subject: [PATCH] STM32 RTC : bypass shadow registers - RTC_SSR for the subseconds - RTC_TR for the time - RTC_DR for the date These registers were accessed through shadow registers which are synchronized with PCLK1 (APB1 clock). They are now accessed directly in order to avoid waiting for the synchronization duration. --- targets/TARGET_STM/rtc_api.c | 179 +++++++++++++++++++++-------------- targets/TARGET_STM/sleep.c | 12 --- 2 files changed, 108 insertions(+), 83 deletions(-) diff --git a/targets/TARGET_STM/rtc_api.c b/targets/TARGET_STM/rtc_api.c index d048add6966..7a0e99a1e30 100644 --- a/targets/TARGET_STM/rtc_api.c +++ b/targets/TARGET_STM/rtc_api.c @@ -33,6 +33,14 @@ #include "rtc_api_hal.h" #include "mbed_mktime.h" #include "mbed_error.h" +#include "mbed_critical.h" + +#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM +volatile uint32_t LP_continuous_time = 0; +volatile uint32_t LP_last_RTC_time = 0; +#endif + +static int RTC_inited = 0; static RTC_HandleTypeDef RtcHandle; @@ -41,18 +49,15 @@ void rtc_init(void) RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + if (RTC_inited) { + return; + } + RTC_inited = 1; + // Enable access to Backup domain __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnableBkUpAccess(); -#if DEVICE_LPTICKER - if ((rtc_isenabled()) && ((RTC->PRER & RTC_PRER_PREDIV_S) == PREDIV_S_VALUE)) { -#else /* DEVICE_LPTICKER */ - if (rtc_isenabled()) { -#endif /* DEVICE_LPTICKER */ - return; - } - #if MBED_CONF_TARGET_LSE_AVAILABLE RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // Mandatory, otherwise the PLL is reconfigured! @@ -114,48 +119,20 @@ void rtc_init(void) error("RTC initialization failed"); } - rtc_synchronize(); // Wait for RSF - - if (!rtc_isenabled()) { - rtc_write(0); +#if !(TARGET_STM32F1) && !(TARGET_STM32F2) + /* STM32F1 : there are no shadow registers */ + /* STM32F2 : shadow registers can not be bypassed */ + if (HAL_RTCEx_EnableBypassShadow(&RtcHandle) != HAL_OK) { + error("EnableBypassShadow error"); } +#endif /* TARGET_STM32F1 || TARGET_STM32F2 */ } void rtc_free(void) { - // Disable access to Backup domain - HAL_PWR_DisableBkUpAccess(); + /* RTC clock can not be reset */ } -/* - ST RTC_DateTypeDef structure - WeekDay 1=monday, 2=tuesday, ..., 7=sunday - Month 0x1=january, 0x2=february, ..., 0x12=december - Date day of the month 1-31 - Year year 0-99 - - ST RTC_TimeTypeDef structure - Hours 0-12 if the RTC_HourFormat_12 is selected during init - 0-23 if the RTC_HourFormat_24 is selected during init - Minutes 0-59 - Seconds 0-59 - TimeFormat RTC_HOURFORMAT12_AM/RTC_HOURFORMAT12_PM - SubSeconds time unit range between [0-1] Second with [1 Sec / SecondFraction +1] granularity - SecondFraction range or granularity of Sub Second register content corresponding to Synchronous pre-scaler factor value (PREDIV_S) - DayLightSaving RTC_DAYLIGHTSAVING_SUB1H/RTC_DAYLIGHTSAVING_ADD1H/RTC_DAYLIGHTSAVING_NONE - StoreOperation RTC_STOREOPERATION_RESET/RTC_STOREOPERATION_SET - - struct tm - tm_sec seconds after the minute 0-61 - tm_min minutes after the hour 0-59 - tm_hour hours since midnight 0-23 - tm_mday day of the month 1-31 - tm_mon months since January 0-11 - tm_year years since 1900 - tm_wday days since Sunday 0-6 - tm_yday days since January 1 0-365 - tm_isdst Daylight Saving Time flag -*/ /* Information about STM32F0, STM32F2, STM32F3, STM32F4, STM32F7, STM32L0, STM32L1, STM32L4: @@ -172,7 +149,7 @@ Information about STM32F1: For date, there is no specific register, only a software structure. It is then not a problem to not use shifts. */ - +#if TARGET_STM32F1 time_t rtc_read(void) { RTC_DateTypeDef dateStruct = {0}; @@ -186,7 +163,6 @@ time_t rtc_read(void) HAL_RTC_GetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN); HAL_RTC_GetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN); -#if TARGET_STM32F1 /* date information is null before first write procedure */ /* set 01/01/1970 as default values */ if (dateStruct.Year == 0) { @@ -194,7 +170,6 @@ time_t rtc_read(void) dateStruct.Month = 1 ; dateStruct.Date = 1 ; } -#endif // Setup a tm structure based on the RTC /* tm_wday information is ignored by _rtc_maketime */ @@ -215,11 +190,57 @@ time_t rtc_read(void) return t; } +#else /* TARGET_STM32F1 */ + +time_t rtc_read(void) +{ + struct tm timeinfo; + + /* Since the shadow registers are bypassed we have to read the time twice and compare them until both times are the same */ + uint32_t Read_time = RTC->TR & RTC_TR_RESERVED_MASK; + uint32_t Read_date = RTC->DR & RTC_DR_RESERVED_MASK; + + while ((Read_time != (RTC->TR & RTC_TR_RESERVED_MASK)) || (Read_date != (RTC->DR & RTC_DR_RESERVED_MASK))) { + Read_time = RTC->TR & RTC_TR_RESERVED_MASK; + Read_date = RTC->DR & RTC_DR_RESERVED_MASK; + } + + /* Setup a tm structure based on the RTC + struct tm : + tm_sec seconds after the minute 0-61 + tm_min minutes after the hour 0-59 + tm_hour hours since midnight 0-23 + tm_mday day of the month 1-31 + tm_mon months since January 0-11 + tm_year years since 1900 + tm_yday information is ignored by _rtc_maketime + tm_wday information is ignored by _rtc_maketime + tm_isdst information is ignored by _rtc_maketime + */ + timeinfo.tm_mday = RTC_Bcd2ToByte((uint8_t)(Read_date & (RTC_DR_DT | RTC_DR_DU))); + timeinfo.tm_mon = RTC_Bcd2ToByte((uint8_t)((Read_date & (RTC_DR_MT | RTC_DR_MU)) >> 8)) - 1; + timeinfo.tm_year = RTC_Bcd2ToByte((uint8_t)((Read_date & (RTC_DR_YT | RTC_DR_YU)) >> 16)) + 68; + timeinfo.tm_hour = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_HT | RTC_TR_HU)) >> 16)); + timeinfo.tm_min = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8)); + timeinfo.tm_sec = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_ST | RTC_TR_SU)) >> 0)); + + // Convert to timestamp + time_t t; + if (_rtc_maketime(&timeinfo, &t, RTC_4_YEAR_LEAP_YEAR_SUPPORT) == false) { + return 0; + } + + return t; +} + +#endif /* TARGET_STM32F1 */ + void rtc_write(time_t t) { RTC_DateTypeDef dateStruct = {0}; RTC_TimeTypeDef timeStruct = {0}; + core_util_critical_section_enter(); RtcHandle.Instance = RTC; // Convert the time into a tm @@ -247,6 +268,17 @@ void rtc_write(time_t t) timeStruct.StoreOperation = RTC_STOREOPERATION_RESET; #endif /* TARGET_STM32F1 */ +#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM + /* Need to update LP_continuous_time value before new RTC time */ + uint32_t current_lp_time = rtc_read_lp(); + + /* LP_last_RTC_time value is updated with the new RTC time */ + LP_last_RTC_time = timeStruct.Seconds + timeStruct.Minutes * 60 + timeStruct.Hours * 60 * 60; + + /* Save current SSR */ + uint32_t Read_SubSeconds = (uint32_t)(RTC->SSR); +#endif /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */ + // Change the RTC current date/time if (HAL_RTC_SetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN) != HAL_OK) { error("HAL_RTC_SetDate error\n"); @@ -254,24 +286,24 @@ void rtc_write(time_t t) if (HAL_RTC_SetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN) != HAL_OK) { error("HAL_RTC_SetTime error\n"); } + +#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM + while (Read_SubSeconds != (RTC->SSR)) { + } +#endif /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */ + + core_util_critical_section_exit(); } int rtc_isenabled(void) { #if !(TARGET_STM32F1) - return (((RTC->ISR & RTC_ISR_INITS) == RTC_ISR_INITS) && ((RTC->ISR & RTC_ISR_RSF) == RTC_ISR_RSF)); + return ((RTC->ISR & RTC_ISR_INITS) == RTC_ISR_INITS); #else /* TARGET_STM32F1 */ return ((RTC->CRL & RTC_CRL_RSF) == RTC_CRL_RSF); #endif /* TARGET_STM32F1 */ } -void rtc_synchronize(void) -{ - RtcHandle.Instance = RTC; - if (HAL_RTC_WaitForSynchro(&RtcHandle) != HAL_OK) { - error("rtc_synchronize error\n"); - } -} #if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM @@ -279,8 +311,6 @@ static void RTC_IRQHandler(void); static void (*irq_handler)(void); volatile uint8_t lp_Fired = 0; -volatile uint32_t LP_continuous_time = 0; -volatile uint32_t LP_last_RTC_time = 0; static void RTC_IRQHandler(void) { @@ -311,31 +341,34 @@ static void RTC_IRQHandler(void) uint32_t rtc_read_lp(void) { - RTC_TimeTypeDef timeStruct = {0}; - RTC_DateTypeDef dateStruct = {0}; - - RtcHandle.Instance = RTC; - HAL_RTC_GetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN); + struct tm timeinfo; - /* Reading RTC current time locks the values in calendar shadow registers until Current date is read - to ensure consistency between the time and date values */ - HAL_RTC_GetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN); + /* Since the shadow registers are bypassed we have to read the time twice and compare them until both times are the same */ + /* We don't have to read date as we bypass shadow registers */ + uint32_t Read_SecondFraction = (uint32_t)(RTC->PRER & RTC_PRER_PREDIV_S); + uint32_t Read_time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); + uint32_t Read_SubSeconds = (uint32_t)(RTC->SSR); - if (timeStruct.SubSeconds > timeStruct.SecondFraction) { - /* SS can be larger than PREDIV_S only after a shift operation. In that case, the correct - time/date is one second less than as indicated by RTC_TR/RTC_DR. */ - timeStruct.Seconds -= 1; + while ((Read_time != (RTC->TR & RTC_TR_RESERVED_MASK)) || (Read_SubSeconds != (RTC->SSR))) { + Read_time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); + Read_SubSeconds = (uint32_t)(RTC->SSR); } - uint32_t RTC_time_s = timeStruct.Seconds + timeStruct.Minutes * 60 + timeStruct.Hours * 60 * 60; // Max 0x0001-517F => * 8191 + 8191 = 0x2A2E-AE80 + + timeinfo.tm_hour = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_HT | RTC_TR_HU)) >> 16)); + timeinfo.tm_min = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8)); + timeinfo.tm_sec = RTC_Bcd2ToByte((uint8_t)((Read_time & (RTC_TR_ST | RTC_TR_SU)) >> 0)); + + uint32_t RTC_time_s = timeinfo.tm_sec + timeinfo.tm_min * 60 + timeinfo.tm_hour * 60 * 60; // Max 0x0001-517F => * 8191 + 8191 = 0x2A2E-AE80 if (LP_last_RTC_time <= RTC_time_s) { LP_continuous_time += (RTC_time_s - LP_last_RTC_time); } else { + /* Add 24h */ LP_continuous_time += (24 * 60 * 60 + RTC_time_s - LP_last_RTC_time); } LP_last_RTC_time = RTC_time_s; - return LP_continuous_time * PREDIV_S_VALUE + timeStruct.SecondFraction - timeStruct.SubSeconds; + return LP_continuous_time * PREDIV_S_VALUE + Read_SecondFraction - Read_SubSeconds; } void rtc_set_wake_up_timer(timestamp_t timestamp) @@ -377,7 +410,11 @@ void rtc_fire_interrupt(void) void rtc_deactivate_wake_up_timer(void) { RtcHandle.Instance = RTC; - HAL_RTCEx_DeactivateWakeUpTimer(&RtcHandle); + __HAL_RTC_WRITEPROTECTION_DISABLE(&RtcHandle); + __HAL_RTC_WAKEUPTIMER_DISABLE(&RtcHandle); + __HAL_RTC_WAKEUPTIMER_DISABLE_IT(&RtcHandle, RTC_IT_WUT); + __HAL_RTC_WRITEPROTECTION_ENABLE(&RtcHandle); + NVIC_DisableIRQ(RTC_WKUP_IRQn); } #endif /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */ diff --git a/targets/TARGET_STM/sleep.c b/targets/TARGET_STM/sleep.c index 168b9c70bc1..04462e963f6 100644 --- a/targets/TARGET_STM/sleep.c +++ b/targets/TARGET_STM/sleep.c @@ -35,7 +35,6 @@ #include "mbed_critical.h" #include "mbed_error.h" -extern void rtc_synchronize(void); extern void save_timer_ctx(void); extern void restore_timer_ctx(void); @@ -203,17 +202,6 @@ void hal_deepsleep(void) restore_timer_ctx(); -#if DEVICE_RTC - /* Wait for RTC RSF bit synchro if RTC is configured */ -#if (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7) - if (READ_BIT(RCC->BDCR, RCC_BDCR_RTCSEL)) { -#else /* (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7) */ - if (__HAL_RCC_GET_RTC_SOURCE()) { -#endif /* (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7) */ - rtc_synchronize(); - } -#endif - // Enable IRQs core_util_critical_section_exit(); }