2025-03-10 7:36 PM
Hi all,
I'd like to use the RTC as a keeper of real-world clock. It would get synchronized with a (S)NTP server once a day. The RTC would get queried multiple times a day for the current time. The main challenge is that I'd like the timestamps to have a resolution of about 1ms. This should be possible according to the manual, but I'm not sure how to utilize the HAL to do so (if it's even supported by the HAL). When reading some HAL functions, it's mentioned that HAL_RTC_SetTime doesn't set the sub-seconds register.
Is it correct to assume I'll need to use the low-level drivers to implement this functionality?
Thanks
2025-03-10 7:44 PM
The sub-seconds register is not directly writeable.
After writing date/time, you can use the RTC->SHIFTR register to add/subtract fractions of a second from the current time. The function HAL_RTCEx_SetSynchroShift does this.
No low level calls necessary.
2025-03-10 8:06 PM
Thank you, TDK, that is a relief to hear. I'll try it out.
Another confusing thing with regards to RTC and sub-seconds is the relation between the synchronous predivider (PREDIV_S) and the update frequency of the RTC.
By default, when:
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
Then if I set an initial time of 0:
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
And query the time every 5 seconds, I get the expected result of seeing the seconds counter increase by 5.
while(1) {
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
printf("%02d:%02d:%02d\r\n",sTime.Hours, sTime.Minutes, sTime.Seconds);
HAL_Delay(5000);
}
However, according to the manual, the PREDIV_S value must be updated in order to control the precision of the sub-seconds register. So when I increased it to 16383 (to support a resolution of 60us, for example), I noticed that the seconds counter reported in my test above has incremented at a significantly lower rate.
Is it possible to set PREDIV_S/A so that the counter keeps running as normal, but I can still do reads at sub-second resolution?
2025-03-10 8:53 PM
The prescalers must be changed so that the CK_SPRE happens at 1 Hz.
If you are using a 32.768 kHz clock, then you must have (PREDIV_A + 1) * (PREDIV_S + 1) = 32768. So increase one and decrease the other. PREDIV_S is not an independent knob to adjust precision.
2025-03-11 6:20 AM
Thanks again for this information. Yes, I read that in the reference manual, however, it is still unclear to me how everything ties up together: fCK_SPRE, fCK_APRE, and how the RTC advances its internal hour/minute/seconds counters.
Is there a configuration that would allow the RTC to advance the "seconds" register every 1 second, while still keeping the SSR register moving at a resolution that allows, say 0.25ms accuracy (PREDIV_S set to 4095)?
Based on your comment, it looks like the answer is "no".
My intended use case is to get NTP packets with 32 bits of unix time (number of seconds since 1970), and 32 bits of fraction of a second. I'm hoping to somehow store this in the RTC so that it can keep track of this with a desired sub-second resolution. All I need for the RTC is to produce a current timestamp when I ask for it, but it doesn't need to be in human-readable format.
Perhaps the RTC isn't the right peripheral for this?
2025-03-11 6:25 AM
> Is there a configuration that would allow the RTC to advance the "seconds" register every 1 second, while still keeping the SSR register moving at a resolution that allows, say 0.25ms accuracy (PREDIV_S set to 4095)?
Well, from my post, I said you need to have (PREDIV_A + 1) * (PREDIV_S + 1) = 32768 if your clock is 32.768 kHz.
If you want PREDIV_S = 4095, then let's do the math here:
(PREDIV_A + 1) * (4095 + 1) = 32768
PREDIV_A + 1 = 32768 / 4096 = 8
PREDIV_A = 7
So yes, you can have PREDIV_S = 4095 and PREDIV_A = 7.
2025-03-12 10:04 AM
Thanks again, TDK, for your tremendous help. I guess my math was wrong. With your recommended settings (PREDIV_A = 7, PREDIV_S = 4095), the RTC indeed advances the seconds counter as expected, while maintaining the desired SSR resolution.
One last question, hopefully, is about the function you mentioned, HAL_RTCEx_SetSynchroShift.
No matter what I try, I can't seem to correctly utilize it to set the SSR register at a pre-defined value.
For example, I'm trying to set the RTC with a time of 00:00:00, with a sub-second of 0.1 second (100ms). After setting the time via HAL_RTC_SetTime, the SSR counter is reset (as expected). After setting the time, I try calling HAL_RTCEx_SetSynchroShift to set the SSR at 0.1 seconds, but since the SSR goes "down", and HAL_RTCEx_SetSynchroShift modifies SHIFTR which causes a time subtraction, the end result is that the number is being added to the SSR register instead of subtracted from it.
Here is my test code:
// Part of init
hrtc.Init.AsynchPrediv = 7;
hrtc.Init.SynchPrediv = 4095;
// ... etc etc ...
// Initialize RTC and set the time and date
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x1;
sDate.Year = 25;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) {
Error_Handler();
}
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
Error_Handler();
}
// Calculate the sub-second shift to match the provided milliseconds (INITIAL_SUBSECOND_OFFSET)
uint32_t subsecond_shift = (uint32_t)(INITIAL_SUBSECOND_OFFSET * (float)(hrtc.Init.SynchPrediv + 1));
// Use HAL_RTCEx_SetSynchroShift to apply the precise sub-second value
HAL_StatusTypeDef retval = HAL_RTCEx_SetSynchroShift(&hrtc, RTC_SHIFTADD1S_RESET, subsecond_shift);
printf("SSR debug info ::: subsecond_shift=%lu, delta = %lu, retval=0x%x\r\n",subsecond_shift, hrtc.Init.SynchPrediv - subsecond_shift, retval);
// Immediately try to read date and time:
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
printf("Time %d:%d:%d :: SS %lu\r\n",sTime.Hours, sTime.Minutes, sTime.Seconds, sTime.SubSeconds);
The observed output is:
SSR debug info ::: subsecond_shift=409, delta = 3686, retval=0x0
Time 0:0:0 :: SS 4474
This again is expected, because SS starts at 4095, and HAL_RTCEx_SetSynchroShift adds to it (since it delays time). I couldn't find a way to subtract from SSR, or to advance time. The reference manual says it can only be done with setting ADD1S, but I don't want to add a second to the clock.
Are you aware of a way to use HAL_RTCEx_SetSynchroShift to set a specific sub-second without shifting the rest of the clock?
2025-03-12 10:37 AM
From the source code documentation:
/**
* @brief Configure the Synchronization Shift Control Settings.
* @note When REFCKON is set, firmware must not write to Shift control register.
* @PAram hrtc RTC handle
* @PAram ShiftAdd1S Select to add or not 1 second to the time calendar.
* This parameter can be one of the following values:
* @arg RTC_SHIFTADD1S_SET: Add one second to the clock calendar.
* @arg RTC_SHIFTADD1S_RESET: No effect.
* @PAram ShiftSubFS Select the number of Second Fractions to substitute.
* This parameter can be one any value from 0 to 0x7FFF.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_RTCEx_SetSynchroShift(RTC_HandleTypeDef *hrtc, uint32_t ShiftAdd1S, uint32_t ShiftSubFS)
So if you want to add 0.25 seconds to the current time, add 1 second and subtract 0.75 seconds.
You can only shift SSR, but if you do things quickly enough it will be at the exact value.
2025-03-12 5:51 PM
I appreciate your help, TDK, but I still can't get the desired results, and the reference manual doesn't seem to make much sense to me.
@TDK wrote:So if you want to add 0.25 seconds to the current time, add 1 second and subtract 0.75 seconds.
Do you mean that I should first call:
HAL_StatusTypeDef retval = HAL_RTCEx_SetSynchroShift(&hrtc, RTC_SHIFTADD1S_SET, 0 );
To advance the clock by a second, and then another call to subtract 0.75?
In this case I find that the SSR register exceeds its expected boundaries (I expect it to have a value between 0..4095, but it goes above 4095), and the seconds counter still stays at 1 (I was hoping it would go back to 0).
According to the manual:
But it also says that ADD1S will advance the clock by a whole second, which is not desirable for my use-case.
I tried following the formula in the manual and ran:
HAL_StatusTypeDef retval = HAL_RTCEx_SetSynchroShift(&hrtc, RTC_SHIFTADD1S_SET, (uint32_t)((1.0f-INITIAL_SUBSECOND_OFFSET)*((float)hrtc.Init.SynchPrediv + 1.0f)) );
And in this case the SSR register also exceeded 4095, and the seconds counter was also set to 1.
From the manual, it is unclear if there is a way to advance the clock (sub-seconds) without increasing the seconds counter. Should I manually update the clock and reduce 1 second after calling the above function?
2025-03-12 7:34 PM - edited 2025-03-12 7:37 PM
> To advance the clock by a second, and then another call to subtract 0.75?
Yes, either in the same call or in two different calls.
> I appreciate your help, TDK, but I still can't get the desired results, and the reference manual doesn't seem to make much sense to me.
Show the exact values you're setting in the code and the exact values you're getting after you set those values. Pick a time (e.g. 1:23:02.333) and try to set it in code with and show what you get instead.
> From the manual, it is unclear if there is a way to advance the clock (sub-seconds) without increasing the seconds counter.
There is not. Adding 1 second adds 1 second to the second field (which can roll over to update minutes/hours/etc). Subtracting subseconds adds to the SSR field which can cause it to be temporarily above PREDIV_S + 1. This corrects itself within the next second.