cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5 - Real confusion about RTC and sub-seconds tracking. Trying to use it to set time from NTP packets.

nethecite
Associate II

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

 

15 REPLIES 15

@TDK wrote:

> 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.


That's a bummer. I can't afford to wait a whole second for the counter to be accurate. But I suppose I could subtract PREDIV_S if it overflows.

 


@TDK wrote:

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.


Sure thing. Please check this code:

#define INITIAL_SUBSECOND_OFFSET 0.333f

// Set the time and date
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};

// 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));
uint32_t subsecond_shift_complement = hrtc.Init.SynchPrediv - subsecond_shift;
printf("SSR debug info ::: subsecond_shift=%lu, complement=%lu\r\n", subsecond_shift, subsecond_shift_complement);

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 = 1;
sTime.Minutes = 23;
sTime.Seconds = 2;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
    Error_Handler();
}

HAL_StatusTypeDef retval = HAL_RTCEx_SetSynchroShift(&hrtc, RTC_SHIFTADD1S_SET, subsecond_shift_complement);
if (retval) {
    Error_Handler();
}

// Immediately try to read 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 received output is:

SSR debug info ::: subsecond_shift=1363, complement=2732
Time 1:23:3 :: SS 6821

The seconds counter was increased by 1, and the SS counter is overflowing. The value of SS counter makes sense if I subtract 4096, it ends up being a value that represents 0.333. But still - the fact that I can only achieve that with incrementing the "seconds" counter by 1 is not desirable for my use-case.

 

Now, if I change one line in the above code to RESET instead of set:

HAL_StatusTypeDef retval = HAL_RTCEx_SetSynchroShift(&hrtc, RTC_SHIFTADD1S_RESET, subsecond_shift_complement);

 

Then the output is:

SSR debug info ::: subsecond_shift=1363, complement=2732
Time 1:23:2 :: SS 6821

The "seconds" counter hasn't increased, SS still overflows, but I suppose if I treat this corner case (whenever SS is over PREDIV_S, then subtract PREDIV_S), then the result would make sense.

It just feels kinda hacky. Am I doing this correctly?

> It just feels kinda hacky. Am I doing this correctly?

Looks like the expected behavior to me. You can postprocess the results from GetTime to address the situation when SS > PREDIV_S. If SS > PREDIV_S, you need to subtract PREDIV_S+1 and subtract 1 second from the seconds time, with all the math that potentially entails. Not ideal, but that's what it does.

If you feel a post has answered your question, please click "Accept as Solution".

The problem with this approach is that if the SS register overflows, it has to go down all the way to 0 in order for the "seconds" counter to advance. So essentially the first second, in my last example (using RESET), now actually takes about 1.66 seconds before it gets updated. I tested that by querying the RTC counters periodically every 500us for 5 seconds. (The querying was done by saving the RTC values in a large array and then printing them all after 5 seconds, so that we don't let the print functions get in the way of RTC timing)

.

If you feel a post has answered your question, please click "Accept as Solution".

Even when I use RTC_SHIFTADD1S_SET instead of RTC_SHIFTADD1S_RESET, which increases the seconds counter by one, the SS overflows causing the first second to actually last ~1.66 seconds. Please see the attached log, where I query the RTC counters for 5 seconds at 500us intervals. You'll notice that the "seconds" counter starts at 3 instead of 2 (as expected, because I used RTC_SHIFTADD1S_SET), but it takes considerably longer to switch from "seconds" 3 to 4, as opposed to 4->5, or 5->6, etc.

If you don't like the inherent mechanism built into the RTC hardware, one possible option is to leave RTC free running and have the difference stored e.g. in the backup registers, then apply the difference each time time is read out.

You can also start a timer with time to the next seconds rollover, stop the RTC, set it with the next second, and then start it when the timer expires.

You can also use the RTC only as a battery-backup of the real time, and upon read out real time, store it as a "local epoch", and start a timer. Then during runtime you wouldn't need to read RTC and do any conversion, just read the "local epoch" and add it to the timer's counter. This allows you to use the native RTC time correction, where the complexity of subseconds being above one second is hidden in the calculation of "local epoch" which is performed only once per reset.

JW