Skip to main content
Visitor II
March 22, 2024
Question

STM32H753 and RTC return wrong day

  • March 22, 2024
  • 2 replies
  • 2717 views

In my project I want to use RTC on STM32H753 chip. And when reading the date, I get the wrong data in the position of days. And I can't fix this problem. When I debug, the registers are set successfully. :face_with_rolling_eyes:

Init function:

 

 

void BSP__RTC_InitAndRead(void)
{
	FLAG GoReadRTC;
	
	// defaults
	memset((void*) &BSP_RTC_Time, 0, sizeof(BSP_RTC_Time_t));
	
	// note. RTC clock enable is in H7_BSP_MCUCLK

	// assume RTC has a valid timestamp in it
	GoReadRTC = TRUE;

	// HSE?
	if (FALSE == HSE_FAILURE_status)
	{
		// yes, HSE is ready

		// check clock source
		if (LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_HSE)
		{
			// we will reset the RTC
			GoReadRTC = FALSE;

			// Set by software to select the clock source for the RTC. These bits can be written only one time
			// (except in case of failure detection on LSE). These bits must be written before LSECSSON is
			// enabled. The BDRST bit can be used to reset them, then it can be written one time again.
			// LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_HSE); -> see few lines lower

		};

		// (re-)enable it again
		LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_HSE); // re-enable...
		LL_RCC_SetRTC_HSEPrescaler(LL_RCC_RTC_HSE_DIV_25); 	// always repeat the HSE prescaler configuration

	} else
	{

		if (LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_LSI)
		{

			// full configuration
			GoReadRTC = FALSE;
		}

		LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); // re-enable...

	}

	// After a system reset, the application can read the INITS flag in the RTC_ISR register to
	// check if the calendar has been initialized or not. If this flag equals 0, the calendar has not
	// been initialized since the year field is set at its Backup domain reset default value (0x00).


	// 1: if not active INITS
	// rtc is not config
	// 2: if active RCC_CSR bit PORRSTF 
	// Start up RTC
	if ( (0 == LL_RTC_IsActiveFlag_INITS(RTC)) ||
		 (resetReason & RCC_RSR_PORRSTF)
		)
	{
		GoReadRTC = FALSE;
	}

	//

	if (GoReadRTC)
	{

		// always to be sure
		EnableBKPdomain();

		// Enable RTC Clock
		LL_RCC_EnableRTC();

		// To read the calendar after initialization,
		// the software must first check that the RSF flag is set in the RTC_ISR register.
		// -> always done while reading from RTC -> OK

		// info
		BSP_RTC_was_running = TRUE;

		// set info
		BSP_RTC_Status = TRUE;

		// set info: HW is ready
		BSP_RTCready = TRUE;

		// application can now read RTC contents any time it wants

	} else
	{
		// info
		BSP_RTC_was_running = FALSE;

		// set info
		BSP_RTC_Status = FALSE;

		// INIT REQUIRED
		LL_RTC_InitTypeDef RTC_InitStruct = {0};
		LL_RTC_TimeTypeDef RTC_TimeStruct = {0};
		LL_RTC_DateTypeDef RTC_DateStruct = {0};

		// A software reset, triggered by setting BDRST bit in the RCC Backup Domain Control
		// Register (RCC_BDCR). All RTC registers and the RCC_BDCR register are reset to
		// their default values. When a Backup domain reset occurs, the RTC is stopped and all
		// the RTC registers are set to their reset values.
		// *** The backup RAM is not affected. ***
		LL_RCC_ForceBackupDomainReset();
		LL_RCC_ReleaseBackupDomainReset();

		// because of BKP Domain reset -> we have to re-enable the access to the BKP Domain
		// (previously enabled in SystemClock_Config)
		EnableBKPdomain();

		if (FALSE == HSE_FAILURE_status) {
 LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_HSE);

 LL_RCC_SetRTC_HSEPrescaler(LL_RCC_RTC_HSE_DIV_25);

 } else {
 LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);
 }

		// deinit
		LL_RCC_EnableRTC(); // musi se zavolat pred deinitem !
		LL_RTC_DeInit(RTC);

		// RTC CLOCK
		LL_RTC_StructInit(&RTC_InitStruct);
		RTC_InitStruct.HourFormat = LL_RTC_HOURFORMAT_24HOUR;
		if (FALSE == HSE_FAILURE_status)
		{
			// HSE / 25 => 1 MHz
			RTC_InitStruct.AsynchPrescaler = 125 - 1; // ck_apre = 8000
			RTC_InitStruct.SynchPrescaler = 8000 - 1; // (1MHz / 125) - 1 = 7999 // ck_spre = 1 Hz
		} else
		{
			// LSI -> approx 32kHz
			RTC_InitStruct.AsynchPrescaler = 0x1F; // 31+1
			RTC_InitStruct.SynchPrescaler = 0x3E8; // 999+1
		}
		LL_RTC_Init(RTC, &RTC_InitStruct);

		// Enable RTC Clock
		LL_RCC_EnableRTC();

		// Initialize RTC and set the Time and Date
		LL_RTC_TIME_StructInit(&RTC_TimeStruct);
		RTC_TimeStruct.Hours = 0x0;
		RTC_TimeStruct.Minutes = 0x0;
		RTC_TimeStruct.Seconds = 0x0;
		LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &RTC_TimeStruct);

		LL_RTC_DATE_StructInit(&RTC_DateStruct);
		RTC_DateStruct.WeekDay = LL_RTC_WEEKDAY_MONDAY;
		RTC_DateStruct.Month = LL_RTC_MONTH_JANUARY;
		RTC_DateStruct.Day = 0x1;
		RTC_DateStruct.Year = 22; // 0..99
		LL_RTC_DATE_Init(RTC, LL_RTC_FORMAT_BIN, &RTC_DateStruct);

		// set info: HW is ready
		BSP_RTCready = TRUE;

	}

	// disable ALARM
	if (BSP_RTCready)
	{
		LL_RTC_DisableWriteProtection(RTC);
		LL_RTC_ClearFlag_ALRA(RTC);
		LL_RTC_DisableIT_ALRA(RTC);
		LL_RTC_ALMA_Disable(RTC);
		LL_RTC_EnableWriteProtection(RTC);
	}

	if (BSP_RTCready)
	{
		// start - make sure to fill the GlobalTime once, right now
		BSP_gt_last_msec = BSP_GetTick() + 1024;
		BSP_RTCtime_Is_Requested(TRUE);
	}

}

 

 

And my reading function 

 

 

void BSP_RTC_GetDateTime(BSP_RTC_Time_t *data)
{
	uint32_t rtc_date, rtc_time;
	uint32_t rtc_date2, rtc_time2;
	uint32_t TimeOut;

	//
	if (FALSE == IsPtrValid((void *)data))
	{
		return;
	}

	// always clear
	memset((void *)data, 0, sizeof(BSP_RTC_Time_t));

	// RTC initialized ?
	if (FALSE == BSP_RTCready)
	{
		return;
	}
	
	//
	MP_BSP_GetRTCmutex();

	// RSF must be cleared by software after the first calendar read, and
	// then the software must wait until RSF is set before reading again the RTC_SSR, RTC_TR
	// and RTC_DR registers.
	LL_RTC_ClearFlag_RS(RTC);
	TimeOut = 0x00FFFFFF;
	while (0 == LL_RTC_IsActiveFlag_RS(RTC))
	{
		if (0 == TimeOut) break; // pokud to dojede na timeout, je spatne inicializovany clock pro RTC !
		TimeOut--;
	}

	// The software must read all the registers twice, and then
	// compare the results to confirm that the data is coherent and correct.

	/* Read time and date registers in BCD format */
	rtc_time = LL_RTC_TIME_Get(RTC);
	rtc_time2 = LL_RTC_TIME_Get(RTC);
	if (rtc_time != rtc_time2)
	{
		rtc_time = LL_RTC_TIME_Get(RTC);
	}

	rtc_date = LL_RTC_DATE_Get(RTC);
	rtc_date2 = LL_RTC_DATE_Get(RTC);
	if (rtc_date != rtc_date2)
	{
		rtc_date = LL_RTC_DATE_Get(RTC);
	}

	//
	LL_RTC_ClearFlag_RS(RTC);

	//
	MP_BSP_GiveRTCmutex();
	
	//
	memset((void*)data, 0, sizeof(BSP_RTC_Time_t));

	//
	data->year = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_YEAR(rtc_date));
	data->month = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MONTH(rtc_date));
	data->day = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_DAY(rtc_date));
	//
	data->hours = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_HOUR(rtc_time));
	data->minutes = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MINUTE(rtc_time));
	data->seconds = __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_SECOND(rtc_time));

}

 

 

 

and for setting rtc is here c code

 

 

void BSP_RTC_SetDateTime(const BSP_RTC_Time_t *data)
{
	LL_RTC_TimeTypeDef RTC_TimeStruct;
	LL_RTC_DateTypeDef RTC_DateStruct;

	// RTC initialized ? or invalid ARG ?
	if ((FALSE == BSP_RTCready)||(FALSE == IsPtrValid((void *)data)))
	{
		return;
	}

	// go

	//
	MP_BSP_GetRTCmutex();

	// write in BIN format
	LL_RTC_TIME_StructInit(&RTC_TimeStruct);
	RTC_TimeStruct.TimeFormat = LL_RTC_TIME_FORMAT_AM_OR_24;
	RTC_TimeStruct.Hours = data->hours;
	RTC_TimeStruct.Minutes = data->minutes;
	RTC_TimeStruct.Seconds = data->seconds;

	// write in BIN format
	LL_RTC_DATE_StructInit(&RTC_DateStruct);
	RTC_DateStruct.Day = data->day;
	RTC_DateStruct.Month = data->month;
	RTC_DateStruct.Year = data->year;
	RTC_DateStruct.WeekDay = data->day_in_week;

	// write in BIN format
 if (LL_RTC_DATE_Init(RTC, LL_RTC_FORMAT_BIN, &RTC_DateStruct) != 0) {
 errprint(">> Write date failed");
 }

 if (LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &RTC_TimeStruct) != 0) {
 errprint(">> Write time failed");
 }

	// we got new time -> RTC is valid
	BSP_RTC_Status = TRUE;

	//
	MP_BSP_GiveRTCmutex();

}

 

 

 

 

 

    This topic has been closed for replies.

    2 replies

    Super User
    March 22, 2024

    First, you should re-order the reading , read this from HAL lib:

    >

    @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values

    in the higher-order calendar shadow registers to ensure consistency between the time and date values.

    <

    So read xx_time , xx_date

     

    	/* Read time and date registers in BCD format */
    
    	rtc_time = LL_RTC_TIME_Get(RTC);
     rtc_date = LL_RTC_DATE_Get(RTC);
    	rtc_time2 = LL_RTC_TIME_Get(RTC);
     rtc_date2 = LL_RTC_DATE_Get(RTC);
    

     

    Visitor II
    March 25, 2024

    Hi AScha.3

    since i am not using HAL so I copied these functions and used my internal function.

     

    void BSP_RTC_GetDateTime(BSP_RTC_Time_t *data)
    {
     // always clear
     memset((void*) data, 0, sizeof(BSP_RTC_Time_t));
    
     MP_BSP_GetRTCmutex();
    
     uint32_t tmpreg;
    
     // [DATE]
    
     uint32_t rtc_dr_reserved_mask = (RTC_DR_YT | RTC_DR_YU | RTC_DR_WDU | RTC_DR_MT | RTC_DR_MU | RTC_DR_DT | RTC_DR_DU);
    
     /* Get the DR register */
     tmpreg = (uint32_t) (RTC->DR & rtc_dr_reserved_mask);
    
     /* Fill the structure fields with the read parameters */
     data->year = (uint8_t) ((tmpreg & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos);
     data->month = (uint8_t) ((tmpreg & (RTC_DR_MT | RTC_DR_MU)) >> RTC_DR_MU_Pos);
     data->day = (uint8_t) ((tmpreg & (RTC_DR_DT | RTC_DR_DU)) >> RTC_DR_DU_Pos);
     data->day_in_week = (uint8_t) ((tmpreg & (RTC_DR_WDU)) >> RTC_DR_WDU_Pos);
    
     uint8_t tmp;
     /* Convert the date structure parameters to Binary format */
     tmp = ((data->year & 0xF0U) >> 4U) * 10U;
     data->year = (tmp + (data->year & 0x0FU));
    
     tmp = ((data->month & 0xF0U) >> 4U) * 10U;
     data->month = (tmp + (data->month & 0x0FU));
    
     tmp = ((data->day & 0xF0U) >> 4U) * 10U;
     data->day = (tmp + (data->day & 0x0FU));
    
     // [TIME]
    
     uint32_t rtc_tr_reserved_mask = (RTC_TR_PM | RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU | RTC_TR_ST | RTC_TR_SU);
    
     /* Get subseconds structure field from the corresponding register*/
     data->subseconds = (uint32_t) (RTC->SSR);
    
     uint32_t SecondFraction = (uint32_t)(RTC->PRER & RTC_PRER_PREDIV_S);
    
     /* Get the TR register */
     tmpreg = (uint32_t) (RTC->TR & rtc_tr_reserved_mask);
    
     /* Fill the structure fields with the read parameters */
     data->hours = (uint8_t) ((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos);
     data->minutes = (uint8_t) ((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos);
     data->seconds = (uint8_t) ((tmpreg & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos);
    
     tmp = ((data->hours & 0xF0U) >> 4U) * 10U;
     data->hours = (tmp + (data->hours & 0x0FU));
    
     tmp = ((data->minutes & 0xF0U) >> 4U) * 10U;
     data->minutes = (tmp + (data->minutes & 0x0FU));
    
     tmp = ((data->seconds & 0xF0U) >> 4U) * 10U;
     data->seconds = (tmp + (data->seconds & 0x0FU));
    
     MP_BSP_GiveRTCmutex();
    }

     

     

    And my debug log:

     

    BSP_RTC_Time_t RTCtimeLog;
    BSP_RTC_GetDateTime(&RTCtimeLog);
    DbgPrint("RTC time label : %d.%d.20%02d %02d:%02d:%02d", RTCtimeLog.day, RTCtimeLog.month, RTCtimeLog.year, RTCtimeLog.hours, RTCtimeLog.minutes, RTCtimeLog.seconds);

     

     

    logs...

    1. [99563][D][TIME--T] RTC time label : 25.3.2024 06:24:25
    2. [95683][D][TIME--T] RTC time label : 24.3.2024 06:24:18

    And you see I have 2 different days. And more often than not I get the wrong data.

    Super User
    March 25, 2024

    Hi,

    but still you not obey the sequence, how to read rtc.

    Just try only using this:

     

    	/* Read time and date registers in BCD format */
    
     rtc_time = LL_RTC_TIME_Get(RTC);
     rtc_date = LL_RTC_DATE_Get(RTC);

     

    I am also not using HAL for this, also not LL , i read registers direct. :)

    (and never had a problem with the rtc.)

    Super User
    March 25, 2024

    So, if I understand it correctly, you're not able to set the RTC date/time if you run the code; but you are able to set it if single-stepping, correct?

    This might indicate timing issues. You are performing lots of steps some of which I am not convinced they are needed, others are hidden behind LL calls I am not going to decipher. Assuming you've already set up LSE to run, you need to (I don't use H7 so some steps may be different in details):

    1. enable RTC APB access in RCC (if given STM32 has this feature)

    2. enable backup-domain access by setting PWR_CR1.DBP; this assumes PWR has already enabled clock in RCC

    3. enable RTC write access by "unlocking" it, see RTC_WPR

    4. place RTC into the INIT state, see RTC_ISR.INIT/INITF

    5. set RTC_TR and RTC_DR

    6. clear the readback status bit, RTC_ISR.RSF

    7. exit INIT state by clearing RTC_ISR.INIT

    8. wait until RSF gets set, then you can read RTC_TR/RTC_DR (in this order, as @AScha.3 said above)

    There might be short delays needed between some of these steps.  My guess is between 1. and anything else, but it may be elsewhere. Try inserting short delays at strategic points and observe, if the RTC write is successful.

    JW