Skip to main content
DBhut.1
Senior
March 3, 2023
Question

RTC reset after 49 days

  • March 3, 2023
  • 12 replies
  • 2799 views

Hello,

I am using STM32L412CBT6 MCU with STM32CubeIDE and STM32CubeFW_L4 V1.17.2.

I am using en.i-cube_lrwan for our project. I am using same structure of libraries used in this en.i-cube_lrwan package.

I am using below libraries for rtc:

1)rtc_if.c

2) rtc.c

3) stm32_systime.c

4) stm32_timer.c

I use the SysTimeSet() function for time sync, and to get time I used SysTimeGet() function.

For sync time, I have to send command through uart, containing current time, which will call SysTimeSet() function.

I have set date time of 13 January 2023 in all devices, and on today (3 March 2023), all devices shows time of 13 January 2023.

So, what causes this type of issue after 49 days?.

Please help us to solve this problem, this is very critical issue for us.

Thank You

This topic has been closed for replies.

12 replies

waclawek.jan
Super User
March 3, 2023

How is VBAT connected? Was there a VDD failure? Is 13.Jan 2023 hardcoded in your program? Upon which conditions does your program set this datum into RTC?

JW

Tesla DeLorean
Guru
March 3, 2023

49 days is typically associated with 32-bit rollover of a milli second tick count. Bad logic and math can cause failures.

​

I​f it is critical, review and understand the code, all the source is available to you and your team.

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
DBhut.1
DBhut.1Author
Senior
March 3, 2023

VBAT is connected to a coin cell. and there is not MCU reset happens after 13 Jan 2023 and works on 3.2V AA battery. and this date is not hardcoded, as mention in post, I have used uart command to set date and time.

We have reviewed the code, and doesn't find any issue with configuration.

and as In STM32L412 there is no 32bit counter for RTC, So there is n chances of 49 days overflow.

Tesla DeLorean
Guru
March 3, 2023

When doing math on time and dates make sure you have enough bits to carry the range of intermediate values, and the units used.

​

Probably not the RTC itself, but calendaring or computing of elapsed time.

​

Look Harder

Look Critically

​

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
waclawek.jan
Super User
March 3, 2023

Oh, so, do you actually read RTC or not?

JW

DBhut.1
DBhut.1Author
Senior
March 3, 2023

RTC is read by below function:

SysTime_t SysTimeGet( void )
{
 SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
 SysTime_t sysTime = { .Seconds = 0, .SubSeconds = 0 };
 SysTime_t DeltaTime;
 
 calendarTime.Seconds = UTIL_SYSTIMDriver.GetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
 
 DeltaTime.SubSeconds = (int16_t)UTIL_SYSTIMDriver.BKUPRead_SubSeconds();
 DeltaTime.Seconds = UTIL_SYSTIMDriver.BKUPRead_Seconds();
 
 sysTime = SysTimeAdd( DeltaTime, calendarTime );
 
 return sysTime;
}

In which Getcalender function points to below function:

uint32_t RTC_IF_GetTime(uint16_t *mSeconds)
{
 RTC_TimeTypeDef RTC_TimeStruct ;
 RTC_DateTypeDef RTC_DateStruct;
 uint32_t ticks;
 
 uint64_t calendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct);
 
 uint32_t seconds = (uint32_t)(calendarValue >> RTC_N_PREDIV_S);
 
 ticks = (uint32_t) calendarValue & RTC_PREDIV_S;
 
 *mSeconds = RTC_IF_Convert_Tick2ms(ticks);
 
 return seconds;
}

waclawek.jan
Super User
March 3, 2023

How is SysTime_t defined?

What does SysTimeAdd() do?

> all devices shows time of 13 January 2023.

How do those devices show time?

As Clive said above, if you count delta time in milliseconds, rounding it down to 32-bits at any point results in wraparound after 49 days.

JW

DBhut.1
DBhut.1Author
Senior
March 3, 2023

delta time is fixed time stored in rtc backup register(epoch time)

typedef struct SysTime_s

{

uint32_t Seconds;

int16_t SubSeconds;

}SysTime_t;

All deives have oled display to show date/time, and can synced only via uart command.

delta time is in seconds, which will wrapped in more than 140 years.

waclawek.jan
Super User
March 3, 2023

Then maybe the calculation inside RTC_GetCalendarValue() or SysTimeAdd() overflows.

JW

DBhut.1
DBhut.1Author
Senior
March 4, 2023

 we have found that issue is with

rtc_if.c librray, in which RTC_GetCalendarValue() function returns uint32_t (Seconds(22 bit)+millisecond(10 bit))

So 22 bit overflows in 4194304 seconds 48.54 days. So to solve this issue, have to make RTC_GetCalendarValue() function return uint64_t, and wherever it is used.

So,

1) Have anybody face this type of issue before, as it seems to be common issue, if using this library.

2) Is this ok to change uint32_t to uint64_t variable, as mentioned above.

3) Is there anything missing?

waclawek.jan
Super User
March 4, 2023

The vast majority of users on this forum comes here only to ask a question so they don't read other's threads. In other words, it's quite unlikely you will answer to your question 1 here.

I downloaded the en.i-cube_lrwan package and had a look. The function in question appears to be dumbly reduplicated in all project and I don't intend to review them all, so I had a look only at [en.i-cube_lrwan.zip]\STM32CubeExpansion_LRWAN_V2.1.0\Projects\B-L072Z-LRWAN1\Applications\LoRaWAN\LoRaWAN_AT_Slave\Core\Src\rtc_if.c:

/*!
 * @brief get current time from calendar in ticks
 * @param pointer to RTC_DateStruct
 * @param pointer to RTC_TimeStruct
 * @retval time in ticks
 */
static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct)
{
 uint32_t calendarValue = 0;
 uint32_t first_read;
 uint32_t correction;
 
 /* Get Time and Date*/
 HAL_RTC_GetTime(&hrtc, RTC_TimeStruct, RTC_FORMAT_BIN);
 
 /* make sure it is correct due to asynchronus nature of RTC*/
 do
 {
 first_read = LL_RTC_TIME_GetSubSecond(RTC);
 HAL_RTC_GetDate(&hrtc, RTC_DateStruct, RTC_FORMAT_BIN);
 HAL_RTC_GetTime(&hrtc, RTC_TimeStruct, RTC_FORMAT_BIN);
 
 } while (first_read != LL_RTC_TIME_GetSubSecond(RTC));
 
 /* calculte amount of elapsed days since 01/01/2000 */
 calendarValue = DIVC((DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR) * RTC_DateStruct->Year, 4);
 
 correction = ((RTC_DateStruct->Year % 4) == 0) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM ;
 
 calendarValue += (DIVC((RTC_DateStruct->Month - 1) * (30 + 31),
 2) - (((correction >> ((RTC_DateStruct->Month - 1) * 2)) & 0x3)));
 
 calendarValue += (RTC_DateStruct->Date - 1);
 
 /* convert from days to seconds */
 calendarValue *= SECONDS_IN_1DAY;
 
 calendarValue += ((uint32_t)RTC_TimeStruct->Seconds +
 ((uint32_t)RTC_TimeStruct->Minutes * SECONDS_IN_1MINUTE) +
 ((uint32_t)RTC_TimeStruct->Hours * SECONDS_IN_1HOUR)) ;
 
 calendarValue = (calendarValue << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_TimeStruct->SubSeconds);
 
 return (calendarValue);
}

Indeed, the return value is bound to overflow 32 bits sooner or later, and, as you've said, within 49 days if RTC_N_PREDIV_S is set to 10 (=> RTC_PREDIV_S=2^10-1) (btw. these are quite surprisingly defined in ../Inc/main.h but at least it's documented by a comment where this file is #included, as "CubeMX generated" :) ). You can increase the time to failure by decreasing RTC_N_PREDIV_S.

You can change it to 64-bit - and to avoid unnecessary usage of 64-bit, it's enough to change the return type and the last calculation - but 32-bit value for subsecond-("tick-")resolution time is used at many places - as RtcTimerContext.Rtc_Time and all related calculations - so they need to be fixed all.

These:

#define USEC_NUMBER 1000000
#define MSEC_NUMBER (USEC_NUMBER/1000)
 
#define COMMON_FACTOR 3
#define CONV_NUMER (MSEC_NUMBER>>COMMON_FACTOR)
#define CONV_DENOM (1<<(RTC_N_PREDIV_S-COMMON_FACTOR))
 
uint32_t RTC_IF_Convert_ms2Tick(uint32_t timeMilliSec)
{
 return (uint32_t)((((uint64_t)timeMilliSec) * CONV_DENOM) / CONV_NUMER);
}
 
uint32_t RTC_IF_Convert_Tick2ms(uint32_t tick)
{
 return (((uint64_t)(tick) * CONV_NUMER) / CONV_DENOM);
}

are bizarre, too, although working within some arbitrary precision limits.

JW

PS. Hi @Amel NASRI​ , can please this bug be looked at/fixed? Thanks.

DBhut.1
DBhut.1Author
Senior
March 4, 2023

thanks @waclawek.jan for brief response.

I have made another function to return uint64_t value, which is used by SysTimeGet().

static uint64_t RTC_GetCalendarValueU64(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct)
{
 uint64_t calendarValue = 0;
 uint32_t first_read;
 uint32_t correction;
 
 /* Get Time and Date*/
 HAL_RTC_GetTime(&hrtc, RTC_TimeStruct, RTC_FORMAT_BIN);
 
 /* make sure it is correct due to asynchronus nature of RTC*/
 do
 {
 first_read = LL_RTC_TIME_GetSubSecond(RTC);
 HAL_RTC_GetDate(&hrtc, RTC_DateStruct, RTC_FORMAT_BIN);
 HAL_RTC_GetTime(&hrtc, RTC_TimeStruct, RTC_FORMAT_BIN);
 
 } while (first_read != LL_RTC_TIME_GetSubSecond(RTC));
 
 /* calculte amount of elapsed days since 01/01/2000 */
 calendarValue = DIVC((DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR) * RTC_DateStruct->Year, 4);
 
 correction = ((RTC_DateStruct->Year % 4) == 0) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM ;
 
 calendarValue += (DIVC((RTC_DateStruct->Month - 1) * (30 + 31),
 2) - (((correction >> ((RTC_DateStruct->Month - 1) * 2)) & 0x3)));
 
 calendarValue += (RTC_DateStruct->Date - 1);
 
 /* convert from days to seconds */
 calendarValue *= SECONDS_IN_1DAY;
 
 calendarValue += ((uint32_t)RTC_TimeStruct->Seconds +
 ((uint32_t)RTC_TimeStruct->Minutes * SECONDS_IN_1MINUTE) +
 ((uint32_t)RTC_TimeStruct->Hours * SECONDS_IN_1HOUR)) ;
 
 calendarValue = (calendarValue << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_TimeStruct->SubSeconds);
 
 return (calendarValue);
}

call this in RTC_IF_GetTime() function.

So not have to changes at many places, and all functionality works fine.

Thank You