Skip to main content
Visitor II
August 1, 2025
Question

STM32G0B0REx RTC smooth calibration

  • August 1, 2025
  • 1 reply
  • 296 views

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

    Super User
    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?