Skip to main content
Visitor II
October 3, 2025
Question

Smooth RTC calibration issue in STM32l431

  • October 3, 2025
  • 3 replies
  • 196 views

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?

 

 

 

 

 

    This topic has been closed for replies.

    3 replies

    Graduate
    October 3, 2025

    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		*/
    
    		}

     

    Technical Moderator
    October 6, 2025

    Hello @RFlod.2 

    it is recommended to enable the Low-Speed Clock (LSE) Crystal/Ceramic resonator, which is a highly accurate clock source commonly used to drive the RTC for clock/calendar or other timing functions.

    Please refer to the article below:

    How to calibrate the STM32's real-time clock (RTC) - STMicroelectronics Community

    Super User
    October 6, 2025

    Your arguments to HAL_RTCEx_SetSmoothCalib don't make sense to me.

    Try removing all code and hard coding values here that make sense to (a) not adjust it at all and (b) adjust it by +10% or something measureable. You should be able to detect a difference. If not, post the code that replicates the issue.