How can I calibrate the RTC using TXCO?
A while ago I asked myself the same question, and it took a while until I've found a solution, since there are many options documented in the reference manual and the topic can be quite confusing.
In my case, I needed to calibrate the RTC on a board without a HSE resonator (it was a small wrist watch prototype with only an LSE crystal (32.768 kHz), the board didn't have a HSE, and was clocked from the unstable RC internal oscillator). But the method that worked for me should be applicable in your situation as well.
The keys to my RTC calibration method were:
- A fast, stable reference clock. I've used a STM32F4 discovery board (with an 8 MHz HSE crystal, and a APB clock speed of 25 MHz (PLL'ed up), used by timers for the measurement described below).
- A very accurate 1PPS calibration input (1 Pluse Per Second). - I've used a GPS module with a 1PPS output on one of the pins.
The calibration steps were as follows in my case:
- Count the number of system clock ticks between multiple 1PPS events on a pin connected to the 1PPS signal source (GPS module in my case) with the Input Capture method of a Counter/Timer (choose the right timer width (16 vs 32 bit) and clock division (preferably no division to get maximum resolution)). Then take the average, to know the deviation from the assumed clock speed (25 MHz in my case). It should be stable, and not vary much during the measurement (32 seconds calibration time was enough).
- Then count the number of system clock ticks between multiple 1 Hz Calibration output ticks (driven by LSE of the target MCU) (using Input Capture on a different pin), and take the average.
- Now calculate the LSE correction required. And use the RTC calibration registers on the target STM32 to adjust the RTC (see also RTC Smooth Calibration in the reference manual or this Application Note (AN3371)).
- Redo steps 1 and 2 to verify the correction.
Make sure to undo any RTC correction before starting the calibration:
if (HAL_RTCEx_SetSmoothCalib(
&hrtc,
RTC_SMOOTHCALIB_PERIOD_32SEC,
RTC_SMOOTHCALIB_PLUSPULSES_RESET,
0) != HAL_OK) {
error_handler();
}
start_calibration();
To calculate the correction, I've use something similar to this:
const float corr = calib_avg_ext_rtc / calib_avg_1pps;
log("correction:\r\n%f / %f =\r\n%.20f\r\n\r\n",
calib_avg_ext_rtc, calib_avg_1pps, corr);
const float c = corr - 1.0f;
if (corr > 1.0f) {
log("RTC crystal vibrates too fast!\r\n");
log("correction: %.20f ppm\r\n", -1.0e6f * c);
}
else if (corr < 1.0f) {
log("RTC crystal vibrates too slow!\r\n");
log("correction: %.20f ppm\r\n", 1.0e6f * c);
}
else {
log("your RTC crystal is running exactly at the right speed. - STRANGE!\r\n");
}
const int16_t calib32s = (int16_t)roundf(c * 32*32768);
if (calib32s != 0) {
log("correction value (ticks +/- 32 s interval):\r\n");
if (corr < 1.0f) {
log("MINUS %d\r\n", -calib32s); /* clock TOO SLOW, REMOVE cycles */
log("\r\nuse this on the target:\r\n");
log("HAL_RTCEx_SetSmoothCalib(&hrtc, RTC_SMOOTHCALIB_PERIOD_32SEC,\
RTC_SMOOTHCALIB_PLUSPULSES_RESET, %d);",
-calib32s);
}
else {
log("PLUS %d\r\n", calib32s); /* clock TOO FAST, ADD cycles */
log("\r\nuse this on the target:\r\n");
log("HAL_RTCEx_SetSmoothCalib(&hrtc, RTC_SMOOTHCALIB_PERIOD_32SEC,\
RTC_SMOOTHCALIB_PLUSPULSES_SET, 0x1FF - %d);",
calib32s);
}
}
else {
log("calibration complete! NO FURTHER CORRECTION REQUIRED\r\n");
}
After measuring the required correction, I've copy-pasted the code manually and had to flash the target board to verify the calibration. Later on, it would nicer to have the option to do the calibration while the device is already deployed...
I've used this on an STM32L011, but the method is more or less independent from the type of MCU. In my case, I used the 8 MHz HSE and a 32-bit counter on the STM32F4 discovery board. But this method should work in modified form also without additional hardware (besides the 1PPS source), when a fast, stable HSE is present. - I managed to get less then 1 PPM deviation, and the RTC kept time accurately for several of days (changes in temperature can influence the accuracy quite a bit, as I had to find out).
The findings were also discussed in the STM32 forum, but the page is down right now, so I can't point you to that...