on 2021-11-16 2:12 AM - edited on 2025-11-28 7:42 AM by Amel NASRI
The STM32’s integrated RTC (Real-Time Clock) peripheral can be used to wake it up from one of the various low-power modes. It is common for battery powered applications to have a power scheme where the application will run for a short period of time and then enter a low power mode to save power and extend battery life. The RTC can be used to wake up the STM32 from a low-power mode, even in the lowest power mode like Standby.
In this article we will present the basic configuration of the RTC (Section 3.1), and two schemes of configuring the RTC peripheral of the STM32 to wake up the STM32 periodically from a low-power mode like STOP2:
The basic configuration of the RTC (Section 3.1) is shared between these two implementations, but they must be configured separately.
The RTC in the STM32 can be configured to generate EXTI interrupts, which wake-up the MCU if it is in a low-power mode. The RTC clock source for the STM32L476, the microcontroller used in this example on the Nucleo-L476RG board, can be provided by either the low-speed internal (LSI) oscillator or the low-speed external (LSE) oscillator. The Nucleo board includes a 32.768 kHz crystal connected to the LSE oscillator pins. For this exercise, the LSE oscillator is selected as the clock source for the RTC because it offers greater precision than the LSI oscillator.
The RTC features the Internal WakeUp Counter, which can be configured to count to a specific value while the MCU is in a low-power mode. Then, after this value is reached, the RTC generates an EXTI interrupt, waking the MCU up.
When configuring the RTC to generate interrupts with the WakeUp counter after a specified amount of time, it is necessary to calculate the counter value based on the clock provided to the RTC. The prescaler of the RTC clock is set to 16 in this example. The equations to calculate the wake-up counter settings are as follows:
In this example, the counter is set to trigger after 10 seconds:
This configuration is utilized in Section 3.2.
In some applications, it may be necessary for the microcontroller unit (MCU) to remain in a low-power mode until a specified timestamp. To achieve this, the calendar and alarm features of the real-time clock (RTC) peripheral can be used.
The calendar only needs to be configured once when powering on the MCU, and its contents are retained throughout resets as long as the MCU is powered by VDD or VBAT, where available. Note that STM32CubeMX-generated code, by default, sets the calendar to the time provided in the peripheral setup window upon every MCU reset. For proper timekeeping, a custom implementation by the application is necessary to set the internal time and date of the RTC calendar.
An alarm can be set to trigger an EXTI interrupt when the calendar's timestamp matches the one specified by the alarm. Certain fields (date, hours, seconds, etc.) can be configured to be masked when checking for this equality, meaning that the RTC will ignore them during this check.
In Section 3.3 of this article, as an example, the RTC is configured to generate an alarm on the 5th second of every minute according to its internal reference time. This is achieved by enabling masking of all the time fields except for the Seconds field.
Now the RTC is enabled and can be further configured to use one of the two aforementioned schemes for generating the wake-up interrupts. These schemes are presented below and are to be implemented separately. Section 3.2 will describe the usage of the Internal WakeUp Counter, and Section 3.3 will describe the usage of the Calendar and Alarm features of the RTC.
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
HAL_SuspendTick();
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500B, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
/* Enter STOP 2 mode */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
SystemClock_Config();
HAL_ResumeTick();
/* USER CODE END WHILE */ /* USER CODE BEGIN WHILE */
while (1)
{
/* Toggle LED */
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_SuspendTick();
/* Enter STOP 2 mode */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
SystemClock_Config();
HAL_ResumeTick();
/* USER CODE END WHILE */
Thanks, nice article. ICNR, the calculation of the WakeUpCounter results exactly in 0x5000 (20480 dec) time. It is easier (for me) to calculate as follows:
The XTAL 'counts' 32768 per second. We divide by 16 i.e. 2048 oszillations (counts) per second. 10 sec: Multiplied with 10 is 20480 or 0x5000.
This is a useful article. But, when implementing this on my design, it does not work 100% :
If other code (in the main loop while(1)) uses critical sections (to achieve thread safety from ISR), the MCU does not stay asleep, but wakes up immediately..
If you have no other code disabling/enabling interrupts, then the MCU goes into sleep without problems.
See this question : STM32WLE immediately exist Stop2 when using UTILS_ENTER_CRITICAL_SECTION()
Hi, the above problem was due to a certain function disabling the interrupts, and then the WFI does not stop the MCU.
After correcting this, the above example works perfect. Thank you
Hi Laura,
Nice example does it also apply to the stm32wle5J?
And is it possible to use a time based wakeup and a external interrupt wakeup as well?
Thanks for your time.
Greetings Dom
Ok I tried it out of the blue and it generates an error. It says:
09:29:23 **** Incremental Build of configuration Debug for project Seeed-LoRa-E5 ****
make -j16 all
arm-none-eabi-gcc "../Core/Src/main.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DCORE_CM4 -DUSE_HAL_DRIVER -DSTM32WLE5xx -c -I../Core/Inc -I"C:/Users/Dominik/Downloads/Seeed-LoRa-E5-master/Drivers/BSP/STM32WLxx_Nucleo" -I../Drivers/STM32WLxx_HAL_Driver/Inc -I../Drivers/STM32WLxx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32WLxx/Include -I../Drivers/CMSIS/Include -I../LoRaWAN/App -I../LoRaWAN/Target -I../Utilities/trace/adv_trace -I../Utilities/misc -I../Utilities/sequencer -I../Utilities/timer -I../Utilities/lpm/tiny_lpm -I../Middlewares/Third_Party/LoRaWAN/LmHandler/Packages -I../Middlewares/Third_Party/SubGHz_Phy -I../Middlewares/Third_Party/SubGHz_Phy/stm32_radio_driver -I../Middlewares/Third_Party/LoRaWAN/Crypto -I../Middlewares/Third_Party/LoRaWAN/Mac/Region -I../Middlewares/Third_Party/LoRaWAN/Mac -I../Middlewares/Third_Party/LoRaWAN/LmHandler -I../Middlewares/Third_Party/LoRaWAN/Utilities -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/main.d" -MT"Core/Src/main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/main.o"
../Core/Src/main.c: In function 'main':
../Core/Src/main.c:106:34: error: 'hrtc' undeclared (first use in this function)
106 | HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500B, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
| ^~~~
../Core/Src/main.c:106:34: note: each undeclared identifier is reported only once for each function it appears in
../Core/Src/main.c:106:5: error: too few arguments to function 'HAL_RTCEx_SetWakeUpTimer_IT'
106 | HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500B, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../Drivers/STM32WLxx_HAL_Driver/Inc/stm32wlxx_hal_rtc.h:738,
from ../Core/Inc/stm32wlxx_hal_conf.h:281,
from ../Drivers/STM32WLxx_HAL_Driver/Inc/stm32wlxx_hal.h:29,
from ../Core/Inc/main.h:30,
from ../Core/Src/main.c:20:
../Drivers/STM32WLxx_HAL_Driver/Inc/stm32wlxx_hal_rtc_ex.h:1141:19: note: declared here
1141 | HAL_StatusTypeDef HAL_RTCEx_SetWakeUpTimer_IT(RTC_HandleTypeDef *hrtc, uint32_t WakeUpCounter, uint32_t WakeUpClock, uint32_t WakeUpAutoClr);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
make: *** [Core/Src/subdir.mk:76: Core/Src/main.o] Error 1
"make -j16 all" terminated with exit code 2. Build might be incomplete.
09:29:23 Build Failed. 3 errors, 0 warnings. (took 486ms)
I´m not sure it is undeclared?
Greetings Dom
hrtc is a 'handle' to the RTC object. It is declared in main.c
if you want to use it in other source code files, you have to add
extern RTC_HandleTypeDef hrtc;and for the RTC_HandleTypeDef to be a known type, you have to include eg. main.h
Hmm I added the code like so:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
extern RTC_HandleTypeDef hrtc;
/* USER CODE END Includes */After that there is an error about too few arguments within the call:
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500B, RTC_WAKEUPCLOCK_RTCCLK_DIV16);Which I do understand because when following the expression via (Ctrl- right click) I do see that there should be:
HAL_StatusTypeDef HAL_RTCEx_SetWakeUpTimer_IT(RTC_HandleTypeDef *hrtc, uint32_t WakeUpCounter, uint32_t WakeUpClock, uint32_t WakeUpAutoClr)four I guess. But it is not autogenerated be Cube MX?
Regards Dom
What I need to do if I want to wake up the mcu at specific time.For example if 24hrs format for RTC is configured.I want mcu to wake up at 6am and 12pm.Do some job and again go to sleep.