Can't set RTC to a certain milliseconds value on STM32L4: always reset to 0
Hello,
I'm trying to use the RTC of my NUCLEO-L476RG including the sub-seconds feature.
I've activated the RTC clock source in the .ioc and configured LSE as clock source (32.768KHz).
I've set up an initial value to the RTC and I'm reading it out every 100ms:
void Set_RTC_Time(void) {
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
sTime.Hours = 10;
sTime.Minutes = 30;
sTime.Seconds = 0;
sDate.WeekDay = RTC_WEEKDAY_THURSDAY;
sDate.Month = RTC_MONTH_OCTOBER;
sDate.Date = 3;
sDate.Year = 24;
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
}
// Function to get RTC time with sub-second precision
void Get_RTC_Time_With_Milliseconds(void) {
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
uint32_t subsecond = 0;
float fraction = 0;
// Retrieve current RTC time and date
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// Calculate the milliseconds from subseconds
subsecond = hrtc.Instance->SSR;
fraction = ((float)(hrtc.Init.SynchPrediv - subsecond) / (hrtc.Init.SynchPrediv + 1)) * 1000;
// Print the time to UART with millisecond precision
printf("Date: %02d-%02d-20%02d, Time: %02d:%02d:%02d.%03d\r\n",
sDate.Date, sDate.Month, sDate.Year,
sTime.Hours, sTime.Minutes, sTime.Seconds,
(int)fraction);
}
This is the output:
Date: 03-10-2024, Time: 10:30:00.000
Date: 03-10-2024, Time: 10:30:00.386
Date: 03-10-2024, Time: 10:30:00.488
Date: 03-10-2024, Time: 10:30:00.593
Date: 03-10-2024, Time: 10:30:00.695
Date: 03-10-2024, Time: 10:30:00.800
Date: 03-10-2024, Time: 10:30:00.906
Date: 03-10-2024, Time: 10:30:00.007
Then, with a button press I want to either reset the date, time and milliseconds to a certain value or just set the milliseconds to a certain value (the purpose is synchronization with GPS or with other boards). However, the milliseconds value is always reset to 0. Here is the code:
void Update_RTC_From_GPS(GPS_Time_t *gps_time) {
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
uint32_t subsecond = 0;
// Set the GPS time values
sTime.Hours = gps_time->hours;
sTime.Minutes = gps_time->minutes;
sTime.Seconds = gps_time->seconds;
// Calculate the sub-seconds to achieve millisecond precision
// SubSeconds should match the fraction of the second
subsecond = (uint32_t)((((float)gps_time->milliseconds / 1000.0) * (hrtc.Init.SynchPrediv + 1)));
// Set the calculated sub-second value
sTime.SubSeconds = hrtc.Init.SynchPrediv - subsecond;
sDate.Date = gps_time->day;
sDate.Month = gps_time->month;
sDate.Year = gps_time->year;
// Update RTC with the new time and date values
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
printf("Failed to update RTC Time\r\n");
} else if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) {
printf("Failed to update RTC Date\r\n");
} else {
printf("RTC Updated to Date: %02d-%02d-20%02d, Time: %02d:%02d:%02d.%03d\r\n",
gps_time->day, gps_time->month, gps_time->year,
gps_time->hours, gps_time->minutes, gps_time->seconds,
gps_time->milliseconds);
}
}
void Adjust_RTC_On_PPS(uint16_t milliseconds) {
RTC_TimeTypeDef sTime = {0};
// Retrieve the current time
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
// Calculate sub-second ticks corresponding to the specified milliseconds
uint32_t subsecond_ticks = (uint32_t)(((float)milliseconds / 1000.0) * (hrtc.Init.SynchPrediv + 1));
// Set the new SubSeconds value based on the exact PPS timing
sTime.SubSeconds = hrtc.Init.SynchPrediv - subsecond_ticks;
// Re-set the RTC to match the exact second of the PPS
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
printf("Failed to adjust RTC on PPS\r\n");
} else {
printf("RTC adjusted to exact millisecond on PPS (%d)\r\n", milliseconds);
}
}
GPS_Time_t GPS_CurrentTime = {
.hours = 11,
.minutes = 12,
.seconds = 13,
.milliseconds = 456,
.day = 3,
.month = RTC_MONTH_OCTOBER,
.year = 24,
};
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == B1_Pin) {
static int togglebutton = 0;
if (togglebutton == 0) {
printf("GPS\n");
Update_RTC_From_GPS(&GPS_CurrentTime);
} else {
printf("PPS\n");
Adjust_RTC_On_PPS(300);
}
togglebutton = !togglebutton;
}
}
This is the result:
Date: 03-10-2024, Time: 10:30:03.578
Date: 03-10-2024, Time: 10:30:03.683
Date: 03-10-2024, Time: 10:30:03.785
GPS
RTC Updated to Date: 03-16-2024, Time: 11:12:13.456
Date: 03-10-2024, Time: 11:12:13.039
Date: 03-10-2024, Time: 11:12:13.144
Date: 03-10-2024, Time: 11:12:13.250
Date: 03-10-2024, Time: 11:12:13.351
PPS
RTC adjusted to exact millisecond on PPS (300)
Date: 03-10-2024, Time: 11:12:14.023
Date: 03-10-2024, Time: 11:12:14.128
Date: 03-10-2024, Time: 11:12:14.230
Date: 03-10-2024, Time: 11:12:14.335
As you can see, the date and time change works, but the milliseconds resets to 0. I've also tried setting the sub-seconds with the shift register, but with no luck:
void Adjust_RTC_On_PPS(uint16_t milliseconds) {
uint32_t shiftValue;
// Calculate shift value for subseconds
shiftValue = ((hrtc.Init.SynchPrediv + 1) * milliseconds) / 1000;
// Set the SUBFS field in RTC_SHIFTR
hrtc.Instance->SHIFTR = (shiftValue << RTC_SHIFTR_SUBFS_Pos) | RTC_SHIFTR_ADD1S;
// Wait for the shift operation to complete
while ((hrtc.Instance->ISR & RTC_ISR_SHPF) != 0) {}
printf("RTC adjusted to exact millisecond on PPS (%03d ms)\r\n", milliseconds);
}
Can anyone help to resolve this issue?
Thanks in advance,
Bruno
