Skip to content

STM32 RTC : bypass shadow registers #7365

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 108 additions & 71 deletions targets/TARGET_STM/rtc_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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!
Expand Down Expand Up @@ -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:
Expand All @@ -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};
Expand All @@ -186,15 +163,13 @@ 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) {
dateStruct.Year = 2 ;
dateStruct.Month = 1 ;
dateStruct.Date = 1 ;
}
#endif

// Setup a tm structure based on the RTC
/* tm_wday information is ignored by _rtc_maketime */
Expand All @@ -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
Expand Down Expand Up @@ -247,40 +268,49 @@ 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");
}
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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that SSR increments and you get stuck in this loop?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSR sub seconds register is reset to PREDIV_S value every second.
This prescaler value is set only during init, so I would say we can't get stuck...

}
#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

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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down
12 changes: 0 additions & 12 deletions targets/TARGET_STM/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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();
}
Expand Down