2025-10-03 6:11 AM
I cannot get smooth calibration to work in STM32l431. This is the code:
void HW_RTC_Init(void)
{
LL_RTC_TimeTypeDef time;
LL_RTC_DateTypeDef date;
LL_RCC_LSI_Enable();
while(LL_RCC_LSI_IsReady() == 0)
{
}
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);
__HAL_RCC_RTC_ENABLE();
LL_RTC_WaitForSynchro(RTC);
//Set the default date to 2000-01-01 00:00:00:000
date.Year = 0;
date.Day = 1;
date.Month = 1;
date.WeekDay = 6;
time.Hours = 0;
time.Minutes = 0;
time.Seconds = 0;
/* Configure RTC */
RTCHandle.Instance = RTC;
/* Set the RTC time base to 1s */
/* Configure RTC prescaler and RTC data registers as follow:
- Hour Format = Format 24
- Asynch Prediv = Value according to source clock
- Synch Prediv = Value according to source clock
- OutPut = Output Disable
- OutPutPolarity = High Polarity
- OutPutType = Open Drain */
RTCHandle.Init.HourFormat = RTC_HOURFORMAT_24;
RTCHandle.Init.AsynchPrediv = 127;
RTCHandle.Init.SynchPrediv = 255;
RTCHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
RTCHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTCHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
HAL_RTC_Init(&RTCHandle);
LL_RTC_TIME_SetFormat(RTC, LL_RTC_HOURFORMAT_24HOUR);
LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, &time);
LL_RTC_DATE_Init(RTC, LL_RTC_FORMAT_BIN, &date);
rtcSet = false;
}
void GPS_CalibrateRTCClock()
{
int32_t frequency;
// GPS clock
printf("GPS %u %u => RTC %u %u\n\r", gpsCalibStart, gpsCalibEnd, rtcCalibStart, rtcCalibEnd);
int32_t gpsDiff = (int32_t)(gpsCalibEnd - gpsCalibStart);
printf("Diff GPS: %d seconds\n\r", gpsDiff);
// RTC
int32_t rtcDiff = (int32_t)(rtcCalibEnd - rtcCalibStart);
printf("Diff STM32: %d seconds\n\r", rtcDiff);
if(gpsDiff < 300 || rtcDiff < 200 || rtcCalibrationDone == true)
{
return;
}
rtcCalibrationDone = true;
int diff = gpsDiff - rtcDiff;
int percent = 100 * 100 * diff / rtcDiff; // procent med 2 decimaler
printf("=> %d seconds = %d %% \n\r", diff, percent);
int originalFrequency = 32000; // LSI frequency på RTC
int preferedFrequency = originalFrequency + (originalFrequency * percent) / 10000;
//int preferedFrequency = originalFrequency - (originalFrequency * percent) / 10000;
printf("Prefered frequency %d\n\r", preferedFrequency);
rtcCalibrationDone = HW_RTC_CalculateCalibrationParameters(originalFrequency, preferedFrequency);
}
When I run the code I get the following output:
GPS 1759492828 1759493307 => RTC 1759500022 1759500494
Diff GPS: 479 seconds
Diff STM32: 472 seconds
=> 7 seconds = 148 % // this is 1.48%
Prefered frequency 32473
Smooth Pos 30 Neg 55 => 32473 Hz // 1.48% faster than 32000 Hz (LSI clock)
When apply this values I do not see any change in RTC clock compared to GPS clock
In desperation I tested to set an insane value
Setting Pos 500 Neg 55 => 42332 Hz
to verify if I can get RTC clock to go faster then GPS reference clock but result is same:
GPS: 2025-10-03 12:50:50
RTC: 2025-10-03 14:50:43
Timediff 7 second
GPS: 2025-10-03 13:03:26
RTC: 2025-10-03 13:03:07
Timediff 19 second
RTC clock has drifted slower then GPS clock for 12 seconds in 13 minutes.
What am I doing wrong here?
2025-10-03 6:42 AM - edited 2025-10-03 6:50 AM
We use H7xx but should be the same hw.
We use high level HAL to make things easy - there is very min difference to LL in performance:
/* set RTC cal to stored value */
HAL_RTCEx_SetSmoothCalib(&hrtc, hSntpRtcApp.freqErrorCorrection & 0x1ff, hSntpRtcApp.freqErrorCorrection & 0x8000, 0);
/* set Date */
HAL_RTC_SetDate(&hrtc, &sdatestructure, RTC_FORMAT_BIN);
in functions like
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* mb_update_RTC_Time_Date PORTABLE C */
/* */
/* AUTHOR */
/* */
/* Mike Bargauan */
/* */
/* DESCRIPTION */
/* */
/* This function updates STM32 RTC time & date */
/* using global variable hSntpRtcApp.processedTimestamp. */
/* */
/* INPUT */
/* */
/* none */
/* */
/* OUTPUT */
/* */
/* none - Completion status managed inside */
/* */
/* GLOBALS referenced */
/* SntpRtcApp SNTP RTC App strucrute */
/* hrtc RTC structure */
/* */
/* CALLS */
/* */
/* seconds2tm Convert uint64 to struct tm */
/* snprintf Easy way to convert to BCT */
/* HAL_RTC_SetDate */
/* HAL_RTC_SetTime */
/* errorSntp */
/* */
/* CALLED BY */
/* */
/* mb_sntp_client_receive_time_update */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* */
/**************************************************************************/
void mb_update_RTC_Time_Date(sntpRtcClient_t * hSRA) {
uint32_t tb;
RTC_DateTypeDef sdatestructure = { 0 };
RTC_TimeTypeDef stimestructure = { 0 };
/* reduce used subsecond bits number from SNTP to RTC - 16 to 8 */
tb = (uint32_t)(hSRA->referenceTimestamp);
/* convert from UTC to HAL structs */
mb_utc_to_HalRtcTime(tb, &sdatestructure, &stimestructure);
if (HAL_RTC_SetDate(&hrtc, &sdatestructure, RTC_FORMAT_BIN) != HAL_OK) {
errorSntp("ERROR in HAL_RTC_SetDate of rtcU64TimeUpdate - CONTINUE\n");
}
if (HAL_RTC_SetTime(&hrtc, &stimestructure, RTC_FORMAT_BIN) != HAL_OK) {
errorSntp("ERROR in HAL_RTC_SetTime of rtcU64TimeUpdate - CONTINUE\n");
}
}
For trimming, we make coarse adjustement with above mb_update_RTC_Time_Date(), then we move to max correction untill less than 12 msec error, than we trim the correction from 0 to what required one step every minute, at beginnung, one ste every 1 hour at the end and works fine.
You can have a look at one board live online
http://o6.mb-international.net/GetSntpRtcStatusHtml.html ipv6 or
http://a.mb-international.net:9743/GetSntpRtcStatusHtml.html ipv4
We fine tune with SNTP internet.
we are inside a ThreadX thread and code use some global to store values:
if(event & SNTP_RTC_SNTP_MSG_RCVD){
debugSntp("SNTP event - sntp reply received\n");
mb_sntp_receive_process(&hSntpRtcApp.nx_sntp_client_udp_socket);
/* we were able to send and got a reply - reset retry counter */
hSntpRtcApp.retrySend = 0;
if (hSntpRtcApp.status == NX_SUCCESS){
/* inform other process we have a valid RTC */
hSntpRtcApp.valid = 1;
/* good packet - set error to wait for next request */
hSntpRtcApp.status = SNTP_RTC_WAIT_RESEND;
/* set time before send new request to stored value */
hSntpRtcApp.loopTime = hSntpRtcApp.sendRequestTime;
/* good packet - did we update RTC? */
if(hSntpRtcApp.rtcUpdated == 1){
/* we had an RTC update - wait loopTime timeout before sending next SNTP request */
/* set error value to compare next */
hSntpRtcApp.zeroError = hSntpRtcApp.diffVal;
/* we are not zeroing */
hSntpRtcApp.zeroingDiff = SNTP_DRIFT_STOP;
/* we use stored freq correction */
HAL_RTCEx_SetSmoothCalib(&hrtc, hSntpRtcApp.freqErrorCorrection & 0x1ff,
hSntpRtcApp.freqErrorCorrection & 0x8000, 0);
/* reset stored loop time to min */
hSntpRtcApp.sendRequestTime = SNTP_MIN_WAIT_TIME_REQUEST;
/* set time before send new request to min value */
hSntpRtcApp.loopTime = SNTP_MIN_WAIT_TIME_REQUEST;
/* we were able to send and got a valid recply - reset retry errors countrer */
hSntpRtcApp.retryError = 0;
debugSntp("SNTP - reset after RTC update\n");
}else{
/* good packet - no update - set loopTime to sendRequestTime */
hSntpRtcApp.loopTime = hSntpRtcApp.sendRequestTime;
/* correct zero error with freq adjust if not already */
if(hSntpRtcApp.zeroingDiff == SNTP_DRIFT_STOP){
/* diff negative - positive adjust - diff large enough to correct ? */
if(hSntpRtcApp.diffVal < (- SNTP_MAX_CENTER_ERROR)){
/* start zeroingDiff positive */
hSntpRtcApp.zeroingDiff = SNTP_DRIFT_DOWN;
/* set RTC cal to max positive */
HAL_RTCEx_SetSmoothCalib(&hrtc, 511, RTC_SMOOTHCALIB_PLUSPULSES_RESET, 0);
/* reset loop time to min */
hSntpRtcApp.sendRequestTime = SNTP_MIN_WAIT_TIME_REQUEST;
/* set time before send new request to min value */
hSntpRtcApp.loopTime = SNTP_MIN_WAIT_TIME_REQUEST;
debugSntp("SNTP - start zeroingDiff positive - cal 0x%lx\n", hrtc.Instance->CALR);
}
/* diff positive -negative adjust - diff large enough to correct ? */
if(hSntpRtcApp.diffVal > SNTP_MAX_CENTER_ERROR){
/* start zeroingDiff negative */
hSntpRtcApp.zeroingDiff = SNTP_DRIFT_UP;
/* set RTC cal to max negative */
HAL_RTCEx_SetSmoothCalib(&hrtc, 0, RTC_SMOOTHCALIB_PLUSPULSES_SET, 0);
/* reset loop time to min */
hSntpRtcApp.sendRequestTime = SNTP_MIN_WAIT_TIME_REQUEST;
/* set time before send new request to min value */
hSntpRtcApp.loopTime = SNTP_MIN_WAIT_TIME_REQUEST;
debugSntp("SNTP - start zeroingDiff negative - cal 0x%lx\n", hrtc.Instance->CALR);
}
}else{
/* zeroing - check if small enough to stop */
if((abs((int32_t)hSntpRtcApp.diffVal)) < (SNTP_MAX_CENTER_ERROR / 2)){
/* stop zeroingDiff */
hSntpRtcApp.zeroingDiff = SNTP_DRIFT_STOPPING;
/* set RTC cal to stored value */
HAL_RTCEx_SetSmoothCalib(&hrtc, hSntpRtcApp.freqErrorCorrection & 0x1ff, hSntpRtcApp.freqErrorCorrection & 0x8000, 0);
/* reset loop time - redundant */
hSntpRtcApp.sendRequestTime = SNTP_MIN_WAIT_TIME_REQUEST;
/* set time before send new request to min value */
hSntpRtcApp.loopTime = SNTP_MIN_WAIT_TIME_REQUEST;
debugSntp("SNTP - stop zeroingDiff - cal 0x%lx\n", hrtc.Instance->CALR);
}
}
/* no update, if no centering, trim center freq or increase loop time */
if(hSntpRtcApp.zeroingDiff == SNTP_DRIFT_STOP){
if(hSntpRtcApp.zeroError != hSntpRtcApp.diffVal){
if(hSntpRtcApp.zeroError > hSntpRtcApp.diffVal){
/* increase freq */
hSntpRtcApp.freqErrorCorrection ++;
if(hSntpRtcApp.freqErrorCorrection > 511){
hSntpRtcApp.freqErrorCorrection = 511;
}
HAL_RTCEx_SetSmoothCalib(&hrtc, hSntpRtcApp.freqErrorCorrection & 0x1ff, hSntpRtcApp.freqErrorCorrection & 0x8000, 0);
debugSntp("SNTP - increased frequency - cal %lx\n", hrtc.Instance->CALR);
}
if(hSntpRtcApp.zeroError < hSntpRtcApp.diffVal){
/* decrease freq */
hSntpRtcApp.freqErrorCorrection --;
if(hSntpRtcApp.freqErrorCorrection < -511){
hSntpRtcApp.freqErrorCorrection = -511;
}
HAL_RTCEx_SetSmoothCalib(&hrtc, hSntpRtcApp.freqErrorCorrection & 0x1ff, hSntpRtcApp.freqErrorCorrection & 0x8000, 0);
debugSntp("SNTP - decreased frequency - cal %lx\n", hrtc.Instance->CALR);
}
}else{
/* increase loop delay */
hSntpRtcApp.sendRequestTime *= 2;
if(hSntpRtcApp.sendRequestTime > SNTP_MAX_WAIT_TIME_REQUEST){
hSntpRtcApp.sendRequestTime = SNTP_MAX_WAIT_TIME_REQUEST;
}
hSntpRtcApp.loopTime = hSntpRtcApp.sendRequestTime;
}
}
/* prepare for next request */
/* set error value to compare next */
hSntpRtcApp.zeroError = hSntpRtcApp.diffVal;
if(hSntpRtcApp.zeroingDiff == SNTP_DRIFT_STOPPING){
hSntpRtcApp.zeroingDiff = SNTP_DRIFT_STOP;
}
hSntpRtcApp.retryError = 0;
}
}else{ /* we had an error in reply - retry sending request or try another server */
if((hSntpRtcApp.retryError++) > SNTP_MAX_SEND_RETRY){
/* too many retries - try with another server */
debugSntp("SNTP WARNING - too many (%d) errors with this server 0x%lx\n",
hSntpRtcApp.retryError, hSntpRtcApp.sanityTest);
if((status = tx_event_flags_set(&hSntpRtcApp.sntp_rtc_client_events,SNTP_RTC_LINK_CHANGED, TX_OR)) != TX_SUCCESS){
Error_HandlerT("SNTP ERROR - App_Sntp_Rtc_Thread_Entry - tx_event_flags_set\n");
}
hSntpRtcApp.status = SNTP_RTC_WAIT_LINK;
}else{
/* retry send */
debugSntp("SNTP WARNING - bad SNTP event or packet, error 0x%lx\n", hSntpRtcApp.sanityTest);
hSntpRtcApp.sendRequestTime = hSntpRtcApp.sendRequestTime >> 1;
if(hSntpRtcApp.sendRequestTime < SNTP_MIN_WAIT_TIME_REQUEST){
hSntpRtcApp.sendRequestTime = SNTP_MIN_WAIT_TIME_REQUEST;
}
if(hSntpRtcApp.valid == 1){
/* we have a valid RTC - we can wait */
hSntpRtcApp.loopTime = SNTP_MIN_WAIT_TIME_REQUEST;
}else{
/* we want minumum time as RTC is not set */
hSntpRtcApp.loopTime = SNTP_WAIT_FOR_REPLY;
}
hSntpRtcApp.status = SNTP_RTC_WAIT_CONGESTION;
}
}
/* if we had an update */
if(hSntpRtcApp.rtcUpdated){
/* read timedate registers */
mb_rtc_get_timeDate(&hRtcServer.td);
/* fill HAL struct and strings */
mb_timeDate_2_Hal();
}
monitorSntp("SNTP\t%s %s Diff %ld Cal 0x%lx Interval %ld Mode %d \n", hRtcServer.dateString, hRtcServer.timeString,
(uint32_t)hSntpRtcApp.diffVal, hrtc.Instance->CALR, hSntpRtcApp.loopTime, hSntpRtcApp.zeroingDiff);
logSntp("%s %s SNTP\tD %ld\tZ %d\tcal 0x%lx\tL %ld\n", hRtcServer.dateString, hRtcServer.timeString,
(uint32_t)hSntpRtcApp.diffVal, hSntpRtcApp.zeroingDiff, hrtc.Instance->CALR, hSntpRtcApp.loopTime);
#if HISTORY_SNTP
hSntpRtcApp.historyLast = (hSntpRtcApp.historyLast + 1) & ( HISTORY_SNTP_SIZE - 1);
hSntpRtcApp.historyTime[hSntpRtcApp.historyLast] = (uint32_t)(hSntpRtcApp.referenceTimestamp);
hSntpRtcApp.historyError[hSntpRtcApp.historyLast] = (int16_t)hSntpRtcApp.diffVal;
hSntpRtcApp.historyCalibration[hSntpRtcApp.historyLast] = (int16_t)hSntpRtcApp.freqErrorCorrection;
if(hSntpRtcApp.historyLast == hSntpRtcApp.historyStart){
hSntpRtcApp.historyStart = (hSntpRtcApp.historyStart + 1) & ( HISTORY_SNTP_SIZE - 1);
}
if(hSntpRtcApp.historyTime[(hSntpRtcApp.historyStart - 1) & ( HISTORY_SNTP_SIZE - 1)] == 0){
hSntpRtcApp.historyStart = (hSntpRtcApp.historyStart + 1) & ( HISTORY_SNTP_SIZE - 1);
}
#endif /* HISTORY_SNTP */
}