The title for this blog can be a bit misleading but let me assure that all these three things have something in common. The RTC clock allows you to measure real time and by assumption this measurement should be very precise. And the key word is assumption because depending on what kind of clock source you will chose you can get completely different results. All of it is dependent on the clock source, thus the internal oscillator. Additionally, since RTC shares the same special register space I will also discuss so-called backup registers. Let us get started.
RTC Clock
Here I want to focus on the one thing that propellers the RTC peripheral the clock. I will not get into details how it works and how to use it since it is pretty straightforward if you are eager to read errata ;). Then, the clock. RTC peripheral (Real-Time Clock) requires a clock signal to operate. With STM32 MCU families this means that it can be either the internal oscillator or external one. ST refers to them as LSI and LSE, Low Speed Internal and Low Speed External respectively. The former uses RC oscillator to generate the internal low speed clock signal. The advantage of this one is the obvious – there is no need to connect an external device (crystal oscillator) to MCU clock inputs. The later relays on the external crystal oscillator entirely, thus you need to connect this component, and a couple of capacitors, to microcontroller pins.
Let us have a closer look at the low-speed internal RC oscillator. As a reference please have a look at manual for STM32F103C8T6. For your convenience I attach it below.
As you can see the internal RC circuit characterises with broad frequency values. The default reference value should be equal to 32 kHz. However, the minimum value is 17 kHz while the maximum value is 47 kHz which gives a tolerance equal to +/- 15 kHz. What does it mean? The clock can run more than 1.5x times faster or slower. It also means that the LSI should not be used for RTC applications where you relay on RTC to be accurate. However, this phenomenon can be mitigated depending on what family of ST microcontroller you are using.
The 15 kHz deviation from 32 kHz is equal to 46.875%. In turn, it gives value of +/- 468750 ppm. This number is significant because of two reasons. The first one; well, it is significant, it is simply too big to overlook. The usual tolerance of a crystal oscillator for RTC clock is close to +/- 40 ppm, or even less +/- 20 ppm. As you can see the tolerance of build-in LSI is higher by four orders of magnitude. The second thing is related to calibration. RTC peripheral allows you to adjust it by 0.954 ppm in range of -487.1 ppm to +488.5 ppm. However, depending on what family of ST microcontrollers you are working with the calibration procedure might not be even possible. For example, it is not available for STM32F103xx family.
Generally, you should be able to adjust the RTC clock with two prescalers. The first asynchronous prescaler has its division factor set to 128 (by default, value in register is 127). The second synchronous prescaler is set to 256 (value in register is 255). Let us have a look on some formulas
f_async = f_rtc (div_async + 1)
where f_rtc is the input frequency of RTC clock. In our example we assume it is 32.768 kHz. This formula yields after substitution f_async = 256 Hz. It is worth to note that this value should be as low as possible in order to save power, thus the divider div_async should be as high as possible. After the documentation
When both prescalers are used, it is recommended to configure the asynchronous prescaler
to a high value to minimize consumption
To calculate final frequency which advances RTC every 1 second we can use following formula
f_sync = f_async / (div_sync + 1).
This yields 256 Hz / (255 + 1) = 1 Hz.
Additionally, there is a special register called RTC shift control register. It allows for subtraction or addition of a fraction of a second (on the level of synchronous clock signal). This way we gain another degree of freedom when manipulating the RTC clock frequency.
Calibration
The calibration procedure allows to adjust the divider value. For this purpose an external timer needs to be used. The general idea is following:
- configure a timer in Input Capture mode,
- remap timer input to LSI clock output,
- use IC interrupt to calculate elapsed period,
- use the period to update divider in order to get more accurate reading.
This procedure is valid for STM32F1 family where two 16-bit registers are available. However, only 20 bits are used to divide the input clock. With other families like STM32F3 where there are two prescalers, asynchronous and synchronous, the calibration procedure is a bit tricky. Then the fine tuning procedure would look like following:
- adjust async and sync dividers as close as possible to generate 1 Hz clock signal,
- configure a timer in Input Capture mode,
- connect internally IC with RTC clock generated every 1 second,
- use IC interrupt to determine the offset,
- use register to adjust shift value.
Remark
While using above described procedures remember that the calibration will give you RTC clock signal with tolerance of the clock source which was used to calibrate it.
Backup registers
One of the cool features of the STM32 MCU are backup registers. The backup registers allow you to retain restricted amount of data in case of following events:
- wakeup from standby mode,
- system reset,
- power reset.
This is because the backup registers belong to the backup domain and are preserved in case of system reset or even full power down while battery supply is present.
Backup registers are useful when you need to preserve some configuration parameters or a token to resume from the point you have left off. It is pretty easy to determine the cause of system reset. For example, to determine if the source of reset was coming from watchdog you can check a flag
__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST);
when it returns true, it means that an independent watchdog reset the MCU.
Why and how backup registers are connected to the RTC clock. Well, the registers of RTC clock reside in the same backup domain that the RTC clock registers do. This means that access to them has to be unlocked in order to change its content.
Unlocking access to backup registers can be done with
HAL_PWR_EnableBkUpAccess();
After all changes to backup registers (including RTC clock registers) are done it is advised to lock write access to them. It can be done with
HAL_PWR_DisableBkUpAccess();
To write something to the register (a 32-bit value), one can use the following function call
HAL_RTCEx_BKUPWrite(&hrtc, register, value);
where
- register can be a value of RTC_BKP_DR0, …, RTC_BKP_DR31 which can simply be translated to values from 0 up to 31,
- value, a 32-bit value to be written to the backup domain register.
In turn, to read data from the register you can use
HAL_RTCEx_BKUPRead(&hrtc, register);
It returns a 32 bit value.
In summary, the STM32 MCU allows you to use up to 32 custom registers to hold instantaneous configuration of embedded software. It can be useful to restore some parameters in case of MCU reset or power down event (only when backup voltage has been applied to the microcontroller).