cancel
Showing results for 
Search instead for 
Did you mean: 

The time is: 33:55!

HTD
Senior III

I think it's related to my previous question:

https://community.st.com/s/question/0D53W00001sGksoSAC/is-it-possible-rtc-on-stm32h7-loses-1-second-on-each-power-cycle

I applied a workaround by practically removing all MX_RTC_Init().

It seems to work, however today my device showed 33:55 at 10:55. Why? It seems like after 23:59 the clock set to 24:00. To 25:00 one hour later.

Is this behavior related to initialization? I'm tempted to just check the TR for values over 23 and subtract 23 when initializing. It would obviously fix the issue, but it seems a little silly 😉

15 REPLIES 15

Here's my hacked MX_RTC_Init():

static void MX_RTC_Init(void)
{
  /* USER CODE BEGIN RTC_Init 0 */
 
  // THE WORKAROUND FOR A 1 SECOND LOSS ON RESTART STARTS HERE:
 
  // The Init structure initialization code is copied from the generated part, it can't be moved since the Cube can't be made to do so.
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  // The copied code ends here. The proper initialization ahead:
  if (hrtc.Instance->DR)
  { // The clock has been set, we skip broken initialization here to avoid resetting fraction of seconds bits.
    if (hrtc.State == HAL_RTC_STATE_RESET) hrtc.Lock = HAL_UNLOCKED;
    hrtc.State = HAL_RTC_STATE_BUSY;
    __HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc);
      hrtc.Instance->CR &= ~(RTC_CR_FMT | RTC_CR_OSEL | RTC_CR_POL); // Clear RTC_CR FMT, OSEL and POL Bits.
      hrtc.Instance->CR |= (hrtc.Init.HourFormat | hrtc.Init.OutPut | hrtc.Init.OutPutPolarity); // Set RTC_CR register.
    HAL_RTC_WaitForSynchro(&hrtc);
    hrtc.State = HAL_RTC_STATE_READY;
    __HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc);
  }
  else
  { // Original initialization otherwise.
    if (HAL_RTC_Init(&hrtc) != HAL_OK)
    {
      Error_Handler();
    }
  }
  return;
 
  // THE WORKAROUND FOR A 1 SECOND LOSS ON RESTART ENDS HERE.
 
  /* USER CODE END RTC_Init 0 */
 
  /* USER CODE BEGIN RTC_Init 1 */
 
  /* USER CODE END RTC_Init 1 */
 
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */
 
  /* USER CODE END RTC_Init 2 */
 
}

It solves the problem entirely. I pinpointed the exact place when the clock broke completely, and after 23:59:59 I got 24:00:00 and still the same date.

The clock behaved that way when I skipped the call to HAL_RTC_WaitForSynchro(). That was it.

However, entering the init mode in line 313 of the file you linked caused the exact problem with losing seconds on restart. When I removed that call but left the HAL_RTC_WaitForSynchro() - the clock works properly after 23:59:59 and doesn't lose seconds anymore. PROBLEM SOLVED.

Show me, how do you set time.

JW

/**
 * @typedef DateTypeDef
 * @struct
 * @brief A simple unpacked universal date structure.
 */
typedef struct
{
  int16_t   y; ///< Year number.
  uint8_t   m; ///< Month number. 1..12.
  uint8_t   d; ///< Day number. 1..31.
} DateTypeDef;
 
/**
 * @typedef TimeTypeDef
 * @struct
 * @brief A precise unpacked local time structure.
 */
typedef struct
{
  uint8_t   h; ///< Hour number. 0..23.
  uint8_t   m; ///< Minute number. 0..59.
  uint8_t   s; ///< Second number. 0..59.
  uint16_t  ms; ///< Milliseconds since full second. 0..999.
  uint16_t  us; ///< Microseconds since full millisecond. 0..999.
  uint16_t  ns; ///< Nanoseconds since full microsecond. 0..999.
} TimeTypeDef;
 
/**
 * @typedef DateTimeTypeDef
 * @struct
 * @brief A simple 10 byte date/time structure containing full date and local time with 1ns precision.
 */
typedef struct
{
  DateTypeDef date;
  TimeTypeDef time;
} DateTimeTypeDef;
 
/**
 * @fn void DateTime2RTC(DateTimeTypeDef*, RTC_DateTypeDef*, RTC_TimeTypeDef)
 * @brief Converts the ISO date/time to RTC date and RTC time.
 * @param dateTime ISO date/time pointer.
 * @param rtcDate RTC date pointer.
 * @param rtcTime RTC time pointer.
 */
void DateTime2RTC(DateTimeTypeDef* dt, RTC_DateTypeDef* rd, RTC_TimeTypeDef* rt)
{
  rd->Year = dt->date.y - 2000;
  rd->Month = dt->date.m;
  rd->Date = dt->date.d;
  rd->WeekDay = dayOfWeek(dt->date.y, dt->date.m, dt->date.d);
  rt->TimeFormat = 0x00u; // the RTC hour format must be set to 24H!
  rt->Hours = dt->time.h;
  rt->Minutes = dt->time.m;
  rt->Seconds = dt->time.s;
  if (rt->SecondFraction != 0)
  {
    if (dt->time.ms != 0)
      rt->SubSeconds = dt->time.ms / (1000.0 / rt->SecondFraction);
    if (dt->time.us != 0)
      rt->SubSeconds += dt->time.us / (1000000.0 / rt->SecondFraction);
    if (dt->time.ns != 0)
      rt->SubSeconds += dt->time.ns / (1000000000.0 / rt->SecondFraction);
  }
}
 
/**
 * @fn HAL_StatusTypeDef RTC_SetDateTime(DateTimeTypeDef*)
 * @brief Sets the current RTC date and time from ISO time.
 * @param dateTime ISO date/time pointer.
 */
HAL_StatusTypeDef RTC_SetDateTime(DateTimeTypeDef* dt)
{
  if (!isValidDateTime(dt)) return HAL_ERROR;
  RTC_DateTypeDef rd;
  RTC_TimeTypeDef rt;
  HAL_StatusTypeDef status;
  HAL_RTC_Init(&hrtc); // Just in case.
  status = HAL_RTC_GetTime(&hrtc, &rt, RTC_FORMAT_BIN);
  if (status != HAL_OK) return status;
  status = HAL_RTC_GetDate(&hrtc, &rd, RTC_FORMAT_BIN);
  if (status != HAL_OK) return status;
  DateTime2RTC(dt, &rd, &rt);
  status = HAL_RTC_SetDate(&hrtc, &rd, RTC_FORMAT_BIN);
  if (status != HAL_OK) return status;
  HAL_RTC_SetTime(&hrtc, &rt, RTC_FORMAT_BIN);
  return status;
}

Here. Without UI, naturally.

In RTC_SetDateTime() you define rt as a local variable of RTC_TimeTypeDef type, thus its fields are uninitialized and may contain "random" values (may be depending on previous run of the program).

HAL_RTC_GetTime() sets the SubSeconds, SecondFraction, Hours, Minutes, Seconds and TimeFormat fields, but not DayLightSaving and StoreOperation fields.

Your DateTime2RTC() function changes only Hours, Minutes, Seconds and SubSeconds in rt.

So, at this point, DayLightSaving and StoreOperation fields are uninitialized.

Then you call HAL_RTC_SetTime() which contains this line:

hrtc->Instance->CR |= (uint32_t)(sTime->DayLightSaving | sTime->StoreOperation);

JW

I revisited the problem. I set all uninitialized fields, however, it didn't solve the problem.

Here's what did work:

HAL_StatusTypeDef RTC_SetDateTime(DateTimeTypeDef* dt)
{
  if (!isValidDateTime(dt)) return HAL_ERROR;
  RTC_DateTypeDef rd;
  RTC_TimeTypeDef rt;
  HAL_StatusTypeDef status;
  DateTime2RTC(dt, &rd, &rt);
  status = HAL_RTC_SetDate(&hrtc, &rd, RTC_FORMAT_BIN);
  if (status != HAL_OK) return status;
  rt.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  rt.StoreOperation = RTC_STOREOPERATION_SET;
  status = HAL_RTC_Init(&hrtc);
  if (status != HAL_OK) return status;
  status = HAL_RTC_SetTime(&hrtc, &rt, RTC_FORMAT_BIN);
  return status;
}

So - calling full HAL_RTC_Init(). As you remember - I don't call full HAL_RTC_Init() on start, to avoid shaving fractions of seconds. But calling it just before resetting the time is a good idea, because I reset seconds anyway. I tested it several times for cases of time 23:59:59:

  • just waiting a minute observing time,
  • resetting the device,
  • powering the device off

The valid outcome is time after 00:00:00, invalid is greater than 24:00:00.

Everything passed.

Piranha
Chief II

With normal code one can just enter initialization mode, set the date and time, exit initialization mode and you are done. With the "helpful" HAL, of course, one has to at least several times more code and setting a date and time as a single operation is not even possible. An example from my code for L43x:

RTC->WPR = 0xCA;
RTC->WPR = 0x53;
 
RTC->ISR = RTC_ISR_FLAGS_ | RTC_ISR_INIT;
while (!(RTC->ISR & RTC_ISR_INITF));
RTC->TR = rTR;
RTC->DR = rDR;
RTC->ISR = RTC_ISR_FLAGS_; // Clear RTC_ISR_INIT
 
RTC->WPR = 0;

Just prepare the data in rTR and rDR variables correctly.