2024-03-22 05:49 AM - last edited on 2024-04-09 06:38 AM by Amel NASRI
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();
}
2024-03-22 06:08 AM
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);
2024-03-24 11:34 PM - edited 2024-03-25 12:24 AM
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...
And you see I have 2 different days. And more often than not I get the wrong data.
2024-03-25 01:25 AM - edited 2024-03-25 01:26 AM
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.)
2024-03-25 06:52 AM
I am sorry. It was my fault. I tried your solution but the problem is still the same.
rtc_time = LL_RTC_TIME_Get(RTC);
rtc_date = LL_RTC_DATE_Get(RTC);
And I tried my solution and the problem is still here.
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;
// [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));
// [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));
MP_BSP_GiveRTCmutex();
}
The data I write to the RTC I write to the console and they are 100% correct.
2024-03-25 07:32 AM
Hmmm...i do it this way:
uint32_t rTR = RTC->TR; // read time register first !
uint32_t rDR = RTC->DR; // read date register
Try : put this in your
BSP_RTC_GetDateTime
(simply direct, not with pointer and memset ..etc. )
2024-03-25 08:13 AM
I modif function to direct console print and read from registers.
void BSP_RTC_GetDateTime(BSP_RTC_Time_t *data)
{
uint32_t rtc_date, rtc_time;
uint32_t TimeOut;
// always clear
memset((void *)data, 0, sizeof(BSP_RTC_Time_t));
//
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;
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 = RTC->TR;
rtc_date = RTC->DR;
//
LL_RTC_ClearFlag_RS(RTC);
//
MP_BSP_GiveRTCmutex();
debugprint("%d.%d.20%02d %d:%d:%d", __LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_DAY(rtc_date)),
__LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MONTH(rtc_date)),
__LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_YEAR(rtc_date)),
__LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_HOUR(rtc_time)),
__LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_MINUTE(rtc_time)),
__LL_RTC_CONVERT_BCD2BIN(__LL_RTC_GET_SECOND(rtc_time)));
}
And output. Today is missing. :beaming_face_with_smiling_eyes:
2024-03-25 08:44 AM
A: you have a time machine !
B: you should set date to 25.
:)
(But seem no "wrong" values there, reading is constant.)
2024-03-25 09:57 AM
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