STM32 stop mode & EXTI wakeup without HAL
Low-power modes come in handy (apart from obvious power saving) when the electromagnetic emissions of an MCU have to be reduced. For example when an MCU is placed close to antenna switching relays - you definitely do not want to receive the noise from the MCU in your HF transceiver (even if the clock frequency does not fall in an amateur band).
The trouble with low-power modes is that they are notoriously hard to debug. Often the clocks and voltage regulators have to be (re)configured. The debugger can prevent from entering into some of the modes or change their behavior. The debugger may also not be available because various clocks are stopped and supply rail lose power. Instead you have to rely on an ammeter or a simple LED to indicate which state the MCU is in.
This is a minimum example of stop mode usage in an STM32L011 with wakeup on GPIO (via EXTI). There are several low-power modes in this MCU. The stop mode is the lowest power of them all that preserves RAM and GPIO state. High frequency oscillators are stopped so there should be totally no HF noise emited by the MCU.
Initialization
In active mode I want the MCU to run at 16 MHz from the high speed internal oscillator (HSI). Since I am not using any libraries there are two things that have to be set up an power-on: voltage regulator and system clock.
The voltage regulator has several power modes that support different system clock speeds. I am using the highest power/speed setting.
The code first configures the voltage regulator, then enables the high speed oscillator, waits until it is ready and switches system clock to run from that oscillator. It could be called for example somewhere early in main()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Stop mode entry
This code configures EXTI for falling edge PA9 interrupt. The application also uses PA9 as a regular interrupt. Some consideration is given to the moment right after wakeup but before ISR entry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
power_init()
is called after exit from the stop mode. This is not strictly necessary as register values are generally preserved. Howeverm the regulator can be configured to enter the lowest power mode on stop mode. This means that after wakeup it would also be in that 1.5 V mode and running the device at 16 MHz would lead to an overclock. In my particular application I am not very concerned about the overall energy consumption and wakeup time so I explicitly reinitialize the regulator and clocks on wakeup. The important part is to always clear the wakeup flag (via PWR_CR_CWUF_Msk
) in the PWR->CR
register. Otherwise the device will immediately wake up after attempting to enter the stop mode again.
The application also uses PA9 as a regular interrupt source (the interrupt has to be of course enabled separately in the NVIC). In active mode this is not a concern. However, when exiting the stop mode via interrupt, the ISR could run using the wrong clock settings, hence the __disable_irq()
and __enable_irq()
to first exit the stop mode, reinitialize the clocks and only then service the interrupt.