2025-03-11 12:53 AM
STM32C031G4
I am trying to adjust the RTC time in an EXTI interrupt routine (its a button press, it should e.g. add an hour).
The RTC is setup using HAL, it succesfully inits with the preset values. After Initialization everything works, the RTC is counting and I can read the time, the initial HAL_RTC_SetTime works. When the button is pressed, the current time is read, modified and then set. However i cannot re-enter the initialization mode to change the RTC time registers with HAL_RTC_SetTime. RTC_EnterInitMode fails with HAL_TIMEOUT. I verified that the RTCAPBEN is set. I did a full SFR register dump diff comparing the initial state and the state when its called from the EXTI interrupt. I could not find a problematic difference.
These are the functions that I call to change or read the time:
HAL_StatusTypeDef SetTime(RTC_TimeTypeDef *sTime,uint32_t Format)
{
if (HAL_RTC_SetTime(lrtc, sTime, Format) == HAL_OK) return HAL_OK;
return HAL_ERROR;
}
HAL_StatusTypeDef GetTime(RTC_TimeTypeDef *sTime,uint32_t Format)
{
__HAL_RTC_WRITEPROTECTION_DISABLE(lrtc);
HAL_RTC_WaitForSynchro(lrtc);
__HAL_RTC_WRITEPROTECTION_ENABLE(lrtc);
if(HAL_RTC_GetTime (lrtc,sTime, Format) != HAL_OK) return HAL_ERROR;
RTC_DateTypeDef dummy;
if(HAL_RTC_GetDate(lrtc,&dummy,Format) != HAL_OK) return HAL_ERROR;
return HAL_OK;
}
These are the initial setup functions:
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
if(hrtc->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
/** Initializes the peripherals clocks
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/* Peripheral clock enable */
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_RTCAPB_CLK_ENABLE();
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
static void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {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.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
HAL_Delay(1);
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc);
if (HAL_RTC_WaitForSynchro(&hrtc) != HAL_OK)
{
Error_Handler();
}
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc);
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0x12;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.SubSeconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x1;
sDate.Year = 0x0;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
/* USER CODE END RTC_Init 2 */
}
These are the HAL functions for setting the time / entering init mode
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
{
uint32_t tmpreg;
HAL_StatusTypeDef status;
/* Check the parameters */
assert_param(IS_RTC_FORMAT(Format));
assert_param(IS_RTC_DAYLIGHT_SAVING(sTime->DayLightSaving));
assert_param(IS_RTC_STORE_OPERATION(sTime->StoreOperation));
/* Process Locked */
__HAL_LOCK(hrtc);
hrtc->State = HAL_RTC_STATE_BUSY;
/* Disable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);
/* Enter Initialization mode */
status = RTC_EnterInitMode(hrtc);
if (status == HAL_OK)
{
if (Format == RTC_FORMAT_BIN)
{
if ((hrtc->Instance->CR & RTC_CR_FMT) != 0U)
{
assert_param(IS_RTC_HOUR12(sTime->Hours));
assert_param(IS_RTC_HOURFORMAT12(sTime->TimeFormat));
}
else
{
sTime->TimeFormat = 0x00U;
assert_param(IS_RTC_HOUR24(sTime->Hours));
}
assert_param(IS_RTC_MINUTES(sTime->Minutes));
assert_param(IS_RTC_SECONDS(sTime->Seconds));
tmpreg = (uint32_t)(((uint32_t)RTC_ByteToBcd2(sTime->Hours) << RTC_TR_HU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sTime->Minutes) << RTC_TR_MNU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sTime->Seconds) << RTC_TR_SU_Pos) | \
(((uint32_t)sTime->TimeFormat) << RTC_TR_PM_Pos));
}
else
{
if ((hrtc->Instance->CR & RTC_CR_FMT) != 0U)
{
assert_param(IS_RTC_HOUR12(RTC_Bcd2ToByte(sTime->Hours)));
assert_param(IS_RTC_HOURFORMAT12(sTime->TimeFormat));
}
else
{
sTime->TimeFormat = 0x00U;
assert_param(IS_RTC_HOUR24(RTC_Bcd2ToByte(sTime->Hours)));
}
assert_param(IS_RTC_MINUTES(RTC_Bcd2ToByte(sTime->Minutes)));
assert_param(IS_RTC_SECONDS(RTC_Bcd2ToByte(sTime->Seconds)));
tmpreg = (((uint32_t)(sTime->Hours) << RTC_TR_HU_Pos) | \
((uint32_t)(sTime->Minutes) << RTC_TR_MNU_Pos) | \
((uint32_t)(sTime->Seconds) << RTC_TR_SU_Pos) | \
((uint32_t)(sTime->TimeFormat) << RTC_TR_PM_Pos));
}
/* Set the RTC_TR register */
hrtc->Instance->TR = (uint32_t)(tmpreg & RTC_TR_RESERVED_MASK);
/* Clear the bits to be configured */
hrtc->Instance->CR &= ((uint32_t)~RTC_CR_BKP);
/* Configure the RTC_CR register */
hrtc->Instance->CR |= (uint32_t)(sTime->DayLightSaving | sTime->StoreOperation);
/* Exit Initialization mode */
status = RTC_ExitInitMode(hrtc);
}
/* Enable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);
if (status == HAL_OK)
{
hrtc->State = HAL_RTC_STATE_READY;
}
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return status;
}
HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc)
{
uint32_t tickstart;
HAL_StatusTypeDef status = HAL_OK;
/* Check if the Initialization mode is set */
if (READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U)
{
/* Set the Initialization mode */
SET_BIT(RTC->ICSR, RTC_ICSR_INIT);
tickstart = HAL_GetTick();
/* Wait till RTC is in INIT state and if Time out is reached exit */
while ((READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U) && (status != HAL_TIMEOUT))
{
if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE)
{
/* New check to avoid false timeout detection in case of preemption */
if (READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U)
{
status = HAL_TIMEOUT;
/* Change RTC state */
hrtc->State = HAL_RTC_STATE_TIMEOUT;
}
else
{
break;
}
}
}
}
return status;
}
While writing this, I found the issue, still going to post this, maybe someone makes the same mistake:
Passing the HAL_RTC_GetTime an uninitialized RTC_TimeTypeDef will cause some fields to be left not initialized after execution. Specifically the fields DayLightSaving and StoreOperation will not be set. This seems to crash the RTC. Adding these two lines in the beginning of my GetTime method solved it:
sTime->DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime->StoreOperation = RTC_STOREOPERATION_RESET;
Solved! Go to Solution.
2025-03-11 12:54 AM
While writing this, I found the issue, still going to post this, maybe someone makes the same mistake:
Passing the HAL_RTC_GetTime an uninitialized RTC_TimeTypeDef will cause some fields to be left not initialized after execution. Specifically the fields DayLightSaving and StoreOperation will not be set. This seems to crash the RTC. Adding these two lines in the beginning of my GetTime method solved it:
sTime->DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime->StoreOperation = RTC_STOREOPERATION_RESET;
2025-03-11 12:54 AM
While writing this, I found the issue, still going to post this, maybe someone makes the same mistake:
Passing the HAL_RTC_GetTime an uninitialized RTC_TimeTypeDef will cause some fields to be left not initialized after execution. Specifically the fields DayLightSaving and StoreOperation will not be set. This seems to crash the RTC. Adding these two lines in the beginning of my GetTime method solved it:
sTime->DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime->StoreOperation = RTC_STOREOPERATION_RESET;