cancel
Showing results for 
Search instead for 
Did you mean: 

RTC reset after 49 days

DBhut.1
Senior

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

12 REPLIES 12
DBhut.1
Senior

 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?

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.

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