Skip to main content
Visitor II
August 1, 2025
Question

STM32G0B0REx RTC smooth calibration

Dear all, 

I'm calibrating the STM32G0B0RE RTC clocked by an external 32.768 oscillator using the following guide:

https://community.st.com/t5/stm32-mcus/how-to-calibrate-the-stm32-s-real-time-clock-rtc/ta-p/744958

Below is my debug code:

int main(void)
{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_RTC_Init();
 MX_USART1_UART_Init();
 MX_I2C1_Init();
 /* USER CODE BEGIN 2 */
 char msg[256];

 strcpy(msg, "********************************************* RTC trimming *********************************************\r\n\r\n");

 HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
 strcpy(msg, "Output waveform is 512Hz on PA4 (RTC_OUT2)\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
 strcpy(msg, "You need to insert the time of 32-wave interval. (32 rising edge and 32 falling edge)\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
 strcpy(msg, "Waiting \"TIME=value\" command [ms] to apply trimming... (i.e., TIME=62.4995)\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 uint8_t rx_buffer[64];
 uint16_t rx_len = 0;

 while (1)
 {
 uint8_t rx;
 if (HAL_UART_Receive(&huart1, &rx, 1, HAL_MAX_DELAY) == HAL_OK)
 {
 rx_buffer[rx_len ++] = rx;

 if (rx_len > 1 && rx_buffer[rx_len - 2] == '\r' && rx_buffer[rx_len - 1] == '\n') // end of line
 {
 rx_buffer[rx_len - 1] = 0; // null-terminate
 rx_len = 0;

 if (strncmp((char*)rx_buffer, CMD_PREFIX, strlen(CMD_PREFIX)) == 0)
 {
 char *value_str = strchr((char*)rx_buffer, '='); // Cerca il simbolo '='

 if(value_str != NULL)
 {
 value_str++; // Skip '=' character to point to the number

 double time = atof(value_str); // Convert string to double (time in ms)

 // Print the inserted time period
 sprintf(msg, "> Time period inserted is %.6f ms.\r\n", time);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Sample interval is fixed at 32
 strcpy(msg, "> Sample interval number is 32.\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Calculate average frequency in Hz: frequency = 1 / (period / 32)
 // period is in ms, convert to seconds dividing by 1000
 double frequencyAverageHz = 32.0 / (time / 1000.0);

 sprintf(msg, "> Frequency average is %.6f Hz.\r\n", frequencyAverageHz);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // The expected RTC frequency (target)
 double rtcFrequencyHz = 32768.26215;

 sprintf(msg, "> RTC frequency is %.6f Hz.\r\n", rtcFrequencyHz);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Calculate PPM error based on average frequency (512 Hz is reference)
 double ppmError = ((frequencyAverageHz - 512.0) / 512.0) * 1e6;

 sprintf(msg, "> PPM (Part Per Million) error is %.6f.\r\n", ppmError);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Use wider types for CALM and CALP to avoid truncation errors
 uint16_t calmInt = 0;
 uint8_t calpInt = 0;

 // Desired frequency (nominal LSE)
 double fcal = 32768.0;

 // Actual frequency before calibration, based on measured average frequency times sample count (64)
 double frtcclk = frequencyAverageHz * 64.0;

 // Decide CALP value based on whether measured frequency is lower or higher than desired
 // If measured freq < desired freq, CALP=1 to add pulses (increase freq)
 // Else CALP=0 to decrease or no change
 if(frtcclk < fcal)
 {
 calpInt = 1;
 }
 else
 {
 calpInt = 0;
 }

 double calp = (double)calpInt;

 // Calculate ratio (fcal / frtcclk - 1)
 double ratio = (fcal / frtcclk) - 1.0;

 // Calculate CALM using formula from STM32 ref manual, rearranged for stability
 double calm = (calp * 512.0 - ratio * pow(2, 20) + ratio * calp * 512.0) / (1.0 + ratio);

 // Clamp CALM to valid range 0..0x1FF (9 bits)
 if(calm < 0.0) calm = 0.0;
 if(calm > 0x1FF) calm = 0x1FF;

 // Use uint16_t to hold CALM to preserve full range
 calmInt = (uint16_t)calm;

 sprintf(msg, "> Calculated CALM is %.6f.\r\n", calm);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 sprintf(msg, "> Obtained CALM = 0x%03X and CALP = 0x%02X.\r\n", calmInt, calpInt);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 strcpy(msg, "> ...\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Recalculate frequency after calibration with formula from ref manual
 double fcalRecalculated = frtcclk * (1.0 + (calpInt * 512.0 - calmInt) / (pow(2, 20) + calmInt - calpInt * 512.0));

 sprintf(msg, "> Calculated new frequency trimmed FCAL is %.6f Hz.\r\n", fcalRecalculated);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 sprintf(msg, "> Calculated new frequency average is %.6f Hz.\r\n", fcalRecalculated / 64);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Recalculated ppm error using FCAL and 512 Hz reference
 double ppmErrorRecalculated = ((fcalRecalculated / 64.0 - 512.0) / 512.0) * 1e6;

 sprintf(msg, "> Calculated new PPM (Part Per Million) error is %.6f.\r\n", ppmErrorRecalculated);
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 strcpy(msg, "> ...\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

 // Set smooth calibration register on RTC
 HAL_StatusTypeDef status = HAL_RTCEx_SetSmoothCalib(&hrtc,
 RTC_SMOOTHCALIB_PERIOD_32SEC,
 (calpInt == 1) ? RTC_SMOOTHCALIB_PLUSPULSES_SET : RTC_SMOOTHCALIB_PLUSPULSES_RESET,
 calmInt);

 if(status == HAL_OK)
 {
 sprintf(msg, "> RTC smooth calibration register updated successfully [0x%04X].\r\n\r\n", (unsigned int)hrtc.Instance->CALR);
 }
 else
 {
 strcpy(msg, "> ERROR updating RTC smooth calibration register!\r\n\r\n");
 }

 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
 }
 }
 else
 {
 strcpy(msg, "Unknown command received.\r\n");
 HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
 }
 }

 if (rx_len >= sizeof(rx_buffer))
 rx_len = 0; // avoid overflow
 }
 }
 }
 /* USER CODE END 3 */
}

The following output is generated via the UART interface:

 Moreno_Ortolan_0-1754035040817.png

Afterward, I monitor the signal on the RTC_OUT2 pin, but the waveform remains unchanged, even after a long wait.

So, how can I verify if the RTC has been trimmed correctly?
Is there anything wrong with my procedure?

Thanks and best regards,


Moreno

This topic has been closed for replies.

1 reply

TDK
August 1, 2025

Your instruments are going to detect a change from 511.982 Hz to 512.000 Hz? What are you using to measure? What frequency is the measurement?

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