01 Why go low-power: the mode ladder
The STM32L4R5 is an ultra-low-power Cortex-M4F running up to 120 MHz. Its value is the ability to trade activity for current across a ladder of modes, from full Run down to a few tens of nanoamps in Shutdown. Each rung switches off more clocks, more of the voltage regulator, and eventually most of the RAM — so choosing a mode is really choosing what must survive and what may wake you.
The seven states, deepest wins
Typical current & what is retained
Figures below are typical at TA = 25 °C, VDD = 3.0 V from DS12023; always use the datasheet tables for guaranteed maxima in your temperature range.
| Mode | LPMS | Regulator | Typ. IDD | Retained | Wake exit |
|---|---|---|---|---|---|
| Run @ 80 MHz | — | Main | ≈ 100 µA/MHz | everything | — |
| Sleep | — | Main | ≈ 30 µA/MHz | everything | resume (few cycles) |
| Low-power run | — | LPR | < 10 µA @ 2 MHz | everything | — |
| Stop 0 | 000 | Main | ≈ 114 µA | all SRAM + regs | resume, MSI ~4 MHz |
| Stop 1 | 001 | LPR | ≈ 4.6 µA | all SRAM + regs | resume, MSI ~4 MHz |
| Stop 2 | 010 | LPR (reduced) | ≈ 1.2 µA | all SRAM + regs | resume, MSI ~4 MHz |
| Standby (no RTC) | 011 | off | ≈ 0.12 µA | backup regs | RESET |
| Standby + RTC | 011 | off (LPR if RRS) | ≈ 0.42 µA | backup regs (+SRAM2 if RRS) | RESET |
| Shutdown (no RTC) | 1xx | off | ≈ 0.03 µA | backup regs | RESET |
| Shutdown + RTC | 1xx | off | ≈ 0.30 µA | backup regs | RESET |
In every Stop mode all of SRAM1 (192 KB), SRAM2 (64 KB), SRAM3 (384 KB) and every peripheral register keep their contents — you resume exactly where you left off. In Standby and Shutdown the core is reset: only the RTC backup registers (RTC_BKP0R…BKP31R) survive, plus SRAM2 in Standby if you set the RRS bit. Plan your state accordingly.
02 The mechanism: SLEEPDEEP, LPMS, WFI/WFE
There is exactly one instruction that stops the core — WFI (Wait For Interrupt) or WFE (Wait For Event). Two bits decide what "stop" means: the Cortex-M4 SLEEPDEEP bit in SCB->SCR, and the LPMS[2:0] field in PWR_CR1.
The decision tree
LPMS[2:0] to pick Stop 0/1/2, Standby or Shutdown.So the universal entry sequence is: pick the mode in LPMS, set/clear SLEEPDEEP, make sure no wakeup flag is already pending, then execute WFI. Bracket the store to SCR with a data-sync barrier so it lands before the core sleeps.
/* Cortex-M4 System Control Register — CMSIS names */
/* SCB_SCR_SLEEPONEXIT_Msk (bit 1) : re-sleep on ISR return */
/* SCB_SCR_SLEEPDEEP_Msk (bit 2) : 0 = Sleep, 1 = Stop/Standby */
/* SCB_SCR_SEVONPEND_Msk (bit 4) : pending IRQ generates an event */
static inline void deepsleep(int on)
{
if (on) SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
else SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
__DSB(); /* ensure the SCR write completes before WFI */
}
WFI vs WFE
| WFI | WFE | |
|---|---|---|
| Wakes on | any NVIC-enabled interrupt (and NMI/reset) | an event: SEV, EXTI event-mode line, or a pending IRQ if SEVONPEND=1 |
| Runs the ISR? | yes — vectoring to the handler is how you resume | no — execution just continues after WFE; no handler needed |
| Typical use | Stop/Standby with an EXTI/RTC interrupt configured | tight polling loops, or waking on an EXTI line in event mode (EXTI_EMR1) |
| Gotcha | the peripheral IRQ must be enabled in NVIC or you never return | an already-latched event makes WFE return instantly — issue a dummy SEV; WFE; to clear it first |
Most designs use WFI: enable the wakeup peripheral's EXTI line + NVIC IRQ, then __WFI(). In Stop modes the core is halted but the EXTI controller stays alive on the LSI/LSE/HSI16-on-demand paths, so an EXTI or RTC edge restarts the clocks and vectors the pending interrupt.
03 PWR register map (CR1–CR4, SR1/SR2, SCR)
The power controller lives at 0x4000_7000 on APB1. Enable its clock with RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN before touching any of these, and set PWR_CR1.DBP before writing the RTC/backup domain.
| Register | Offset | Purpose |
|---|---|---|
| PWR_CR1 | 0x00 | LPMS mode select, voltage scaling (VOS), low-power run (LPR), backup unlock (DBP) |
| PWR_CR2 | 0x04 | PVD level, peripheral voltage monitors, independent VDDUSB/VDDIO2 supplies |
| PWR_CR3 | 0x08 | Wakeup-pin enables (EWUPn), SRAM2 retention (RRS), pull config apply (APC), internal wakeup (EIWUL) |
| PWR_CR4 | 0x0C | Wakeup-pin polarity (WPn), VBAT charging (VBE/VBRS) |
| PWR_SR1 | 0x10 | Wakeup flags (WUFn), Standby flag (SBF), internal wakeup flag (WUFI) |
| PWR_SR2 | 0x14 | Regulator/scaling ready flags (VOSF, REGLPF, REGLPS), PVDO, PVMOx |
| PWR_SCR | 0x18 | Clear-wakeup (CWUFn) and clear-Standby (CSBF) — write 1 to clear |
| PWR_PUCRx / PDCRx | 0x20+ | Per-port pull-up / pull-down hold during Standby/Shutdown (needs APC=1) |
| PWR_CR5 | 0x80 | R1MODE: Range 1 "boost" (0) enables 120 MHz; normal Range 1 (1) caps ~80 MHz |
PWR_CR1 — the mode selector
SR2.VOSF=0 after changing.PWR_CR3 — Standby/Shutdown behaviour & wakeup pins
Status & clear registers
04 Run, Sleep and Low-power run/sleep
Sleep is the cheapest win: peripherals keep running (a DMA transfer, a UART receive, a timer PWM) while the core idles. Nothing is lost, wake latency is a few cycles. Low-power run/sleep additionally moves the regulator to the LPR for sub-2-MHz duty.
#include "stm32l4xx.h"
/* Plain Sleep: SLEEPDEEP=0, so WFI only gates the CPU clock.
Any NVIC-enabled interrupt wakes and vectors to its handler. */
static void enter_sleep(void)
{
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; /* Sleep, not Stop */
__DSB();
__WFI(); /* halt until an interrupt */
}
/* Sleep-on-exit: the core automatically re-sleeps every time it
returns from an ISR. Perfect for purely event-driven firmware —
main() sets it once and the while(1) never actually runs. */
static void enable_sleep_on_exit(void)
{
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
__DSB();
__WFI(); /* first sleep; ISRs do the rest */
}
/* Low-power run needs: VOS = Range 2, then SYSCLK <= 2 MHz,
then set LPR. Order matters. */
static void enter_low_power_run(void)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
/* 1. Voltage scaling Range 2 (VOS = 10) */
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS_Msk) | (0x2u << PWR_CR1_VOS_Pos);
while (PWR->SR2 & PWR_SR2_VOSF) { } /* wait until scaling done */
/* 2. Drop SYSCLK to <= 2 MHz here (e.g. MSI range 2 MHz) ... */
/* 3. Enter low-power run */
PWR->CR1 |= PWR_CR1_LPR;
while (!(PWR->SR2 & PWR_SR2_REGLPF)) { } /* LPR now supplying core */
}
static void exit_low_power_run(void)
{
PWR->CR1 &= ~PWR_CR1_LPR;
while (PWR->SR2 & PWR_SR2_REGLPF) { } /* back on main regulator */
/* raise SYSCLK and VOS = Range 1 again as needed */
}
"Low-power sleep" is simply Sleep entered while LPR=1. Because the LPR limits SYSCLK to 2 MHz, Flash is put in power-down and instruction fetch stalls — great for a peripheral waiting on a slow event, but do not expect fast code between wakes. To go back to full speed you must exit LPR (clear LPR, restore VOS Range 1, raise the clock) first.
05 Stop 0 / Stop 1 / Stop 2
Stop modes are the workhorse of battery designs: HSE, HSI16 and the PLL are switched off, but every byte of SRAM and every register is retained, so WFI returns to the very next instruction. The three variants trade wake speed for current — Stop 0 keeps the main regulator (fastest, ~114 µA), Stop 2 drops to a reduced LPR (~1.2 µA).
#include "stm32l4xx.h"
/* lpms: 0 = Stop0, 1 = Stop1, 2 = Stop2 */
static void enter_stop(uint32_t lpms)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
/* 1. Select the Stop level in LPMS[2:0] */
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_LPMS_Msk)
| (lpms & PWR_CR1_LPMS_Msk);
/* 2. SLEEPDEEP = 1 so WFI enters Stop, not Sleep */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
/* 3. Barrier, then sleep. Execution resumes HERE on wakeup. */
__DSB();
__WFI();
/* 4. Restore SLEEPDEEP so a later plain Sleep behaves */
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
}
On wakeup the system clock is whatever STOPWUCK in RCC_CFGR selects — MSI (default, ~4 MHz) or HSI16. The PLL and HSE are off. If your app ran at 120 MHz you must rebuild the clock tree, otherwise you silently continue at 4 MHz.
/* After Stop the tree is MSI-only. Re-arm HSE + PLL and switch back.
In practice just call your generated SystemClock_Config() again. */
static void restore_clock_after_stop(void)
{
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)) { }
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) { }
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW_Msk) | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL) { }
}
/* HAL rolls LPMS + SLEEPDEEP + WFI into one call per mode. */
/* Stop 0 (main regulator kept): */
HAL_PWREx_EnterSTOP0Mode(PWR_STOPENTRY_WFI);
/* Stop 1 (LPR): */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/* Stop 2 (deepest Stop): */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
/* Every one of these returns on wakeup with the clock on MSI, */
/* so re-run your clock config immediately: */
SystemClock_Config();
Stop 0 — need < ~5 µs wake and full-speed restart: main regulator stays warm. Stop 1 — general low-power idle. Stop 2 — longest idles; a few peripherals (some GPIO/EXTI, LPUART, LPTIM, RTC, comparators, I2C3) can still wake you, but fewer than Stop 1. All three preserve SRAM and context.
06 Standby, Shutdown, SRAM2 retention & wakeup pins
Below Stop the regulators shut down and the core is powered off. Wakeup is a reset: firmware restarts at the reset vector with SRAM1/SRAM3 gone. Only the backup domain survives — plus SRAM2 in Standby if you ask for it. These are the modes for shelf-life and long sleep intervals.
#include "stm32l4xx.h"
/* retain_sram2 != 0 keeps the 64-KB SRAM2 alive across Standby
(powered by the LPR) so you can stash state there. */
static void enter_standby(int retain_sram2)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
if (retain_sram2) PWR->CR3 |= PWR_CR3_RRS;
else PWR->CR3 &= ~PWR_CR3_RRS;
/* LPMS = 011 -> Standby */
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_LPMS_Msk)
| (0x3u << PWR_CR1_LPMS_Pos);
/* Clear every stale wakeup flag LAST, per RM0432 */
PWR->SCR = PWR_SCR_CWUF1 | PWR_SCR_CWUF2 | PWR_SCR_CWUF3
| PWR_SCR_CWUF4 | PWR_SCR_CWUF5;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI(); /* powers down; wakeup causes a RESET */
}
/* LPMS = 1xx -> Shutdown (use 100). Deepest; ignores RRS. */
static void enter_shutdown(void)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_LPMS_Msk)
| (0x4u << PWR_CR1_LPMS_Pos);
PWR->SCR = PWR_SCR_CWUF1 | PWR_SCR_CWUF2 | PWR_SCR_CWUF3
| PWR_SCR_CWUF4 | PWR_SCR_CWUF5;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI();
}
Wakeup pins (Standby/Shutdown)
Five dedicated WKUPn pins can pull the device out of Standby or Shutdown. Each is enabled by EWUPn in PWR_CR3 and its edge polarity chosen by WPn in PWR_CR4 (0 = rising, 1 = falling). On STM32L4R5 each WKUP line maps to a fixed pad:
| Wakeup line | Pin | Enable bit (PWR_CR3) | Polarity bit (PWR_CR4) | Flag / clear |
|---|---|---|---|---|
| WKUP1 | PA0 | EWUP1 (bit 0) | WP1 (bit 0) | WUF1 / CWUF1 |
| WKUP2 | PC13 | EWUP2 (bit 1) | WP2 (bit 1) | WUF2 / CWUF2 |
| WKUP3 | PE6 | EWUP3 (bit 2) | WP3 (bit 2) | WUF3 / CWUF3 |
| WKUP4 | PA2 | EWUP4 (bit 3) | WP4 (bit 3) | WUF4 / CWUF4 |
| WKUP5 | PC5 | EWUP5 (bit 4) | WP5 (bit 4) | WUF5 / CWUF5 |
Confirm the pad for your exact package against the "Additional functions" table in DS12023 — the mapping above is the standard STM32L4R5 assignment.
/* Arm WKUP1 (PA0) as a rising-edge Standby/Shutdown wakeup. The pad
itself needs no GPIO clock — the WKUP function is always wired. */
static void configure_wakeup_pin1(void)
{
PWR->CR4 &= ~PWR_CR4_WP1; /* WP1 = 0 -> detect a rising edge */
PWR->SCR = PWR_SCR_CWUF1; /* clear any pending flag first */
PWR->CR3 |= PWR_CR3_EWUP1; /* enable WKUP1 */
}
/* At every boot, tell "cold power-on" from "woke from Standby". */
int main(void)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
if (PWR->SR1 & PWR_SR1_SBF) { /* came out of Standby/Shutdown */
PWR->SCR = PWR_SCR_CSBF; /* clear the Standby flag */
/* SRAM2 is valid only if RRS was set; SRAM1/SRAM3 are cleared.
Backup registers RTC->BKP0R..BKP31R are always valid. */
} else {
/* genuine cold start: full init */
}
configure_wakeup_pin1();
/* ... */
for (;;) { }
}
In Standby/Shutdown, GPIOs are Hi-Z by default and a floating input can burn far more than the 0.1 µA core current. Either drive/terminate them externally, or use PWR_PUCRx/PWR_PDCRx with APC=1 in PWR_CR3 to latch a defined pull-up/pull-down that persists through the low-power mode.
07 RTC wakeup timer: periodic wake from Stop 2
The RTC wakeup timer is the canonical periodic "heartbeat": a down-counter clocked from the RTC that fires an interrupt every N ticks and pulls the core out of Stop (via EXTI line 20) or out of Standby/Shutdown (via the internal wakeup line). Clocked by the 32.768-kHz LSE it costs about a microamp in Stop 2. This is a complete, self-contained example.
#include "stm32l4xx.h"
/* Enable the LSE and route it to the RTC. Do this once at startup. */
static void rtc_lse_init(void)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; /* PWR clock */
PWR->CR1 |= PWR_CR1_DBP; /* unlock backup domain */
/* Start the 32.768 kHz LSE crystal */
RCC->BDCR |= RCC_BDCR_LSEON;
while (!(RCC->BDCR & RCC_BDCR_LSERDY)) { }
/* RTCSEL = 01 (LSE) and switch the RTC on */
RCC->BDCR = (RCC->BDCR & ~RCC_BDCR_RTCSEL_Msk)
| (0x1u << RCC_BDCR_RTCSEL_Pos)
| RCC_BDCR_RTCEN;
}
/* Period = (seconds) using ck_spre = 1 Hz. Valid 1..65536 s.
WUCKSEL = 100b selects ck_spre; counter reload = seconds - 1. */
static void rtc_wakeup_start(uint32_t seconds)
{
/* 1. Unlock RTC write protection */
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
/* 2. Disable the timer, then wait until it is safe to write WUTR */
RTC->CR &= ~RTC_CR_WUTE;
while (!(RTC->ISR & RTC_ISR_WUTWF)) { } /* WUTWF = write allowed */
/* 3. Reload value and clock source */
RTC->WUTR = (seconds - 1u) & 0xFFFFu;
RTC->CR = (RTC->CR & ~RTC_CR_WUCKSEL_Msk)
| (0x4u << RTC_CR_WUCKSEL_Pos); /* ck_spre (1 Hz) */
/* 4. Enable timer + its interrupt */
RTC->CR |= RTC_CR_WUTE | RTC_CR_WUTIE;
/* 5. Re-lock the RTC */
RTC->WPR = 0xFF;
/* 6. RTC wakeup is EXTI line 20 — unmask + rising edge so it can
restart the clocks and vector the IRQ out of Stop. */
EXTI->IMR1 |= (1u << 20); /* interrupt mask (IM20) */
EXTI->RTSR1 |= (1u << 20); /* rising trigger (RT20) */
NVIC_SetPriority(RTC_WKUP_IRQn, 2);
NVIC_EnableIRQ(RTC_WKUP_IRQn);
}
volatile uint32_t ticks;
/* WUTF is an rc_w0 flag and is NOT under RTC write protection,
so clearing it needs no WPR dance. Also clear EXTI line 20. */
void RTC_WKUP_IRQHandler(void)
{
if (RTC->ISR & RTC_ISR_WUTF) {
RTC->ISR &= ~RTC_ISR_WUTF; /* acknowledge wakeup timer */
ticks++;
}
EXTI->PR1 = (1u << 20); /* clear pending (write 1) */
}
int main(void)
{
rtc_lse_init();
rtc_wakeup_start(5); /* wake every 5 seconds */
for (;;) {
/* --- enter Stop 2 --- */
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_LPMS_Msk)
| (0x2u << PWR_CR1_LPMS_Pos);
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI(); /* sleeps ~5 s, wakes in ISR */
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
/* Back on MSI ~4 MHz. Do a short task, then loop.
Call restore_clock_after_stop() if you need full speed. */
}
}
/* Assuming hrtc is initialised by HAL_RTC_Init() with LSE selected. */
RTC_HandleTypeDef hrtc;
/* 5-second period using ck_spre (1 Hz): counter = 5 - 1.
The 4th arg (WakeUpAutoClr) = 0 disables the auto-reload output. */
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 5u - 1u,
RTC_WAKEUPCLOCK_CKSPRE, 0u);
/* HAL enables EXTI line 20 + NVIC for you. Enter Stop 2: */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
SystemClock_Config(); /* rebuild clock after wake */
/* HAL supplies RTC_WKUP_IRQHandler -> HAL_RTCEx_WakeUpTimerIRQHandler.
Handle the tick in the weak callback: */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *h)
{
(void)h;
/* periodic work here */
}
Two clock domains feed WUCKSEL. For 1 s…36 h use ck_spre (1 Hz): WUCKSEL=100, period = (WUT+1) seconds; add WUCKSEL=110 to tack on 216 s. For sub-second/finer resolution use RTCCLK/2/4/8/16 (WUCKSEL=011..000): with LSE/16 one tick is 16/32768 = 488.28 µs, so WUT = round(T / 488.28µs) - 1. Always wait for WUTWF=1 before writing WUTR.
08 Wakeup sources & EXTI mapping
What can wake you depends on the mode. In Stop, wakeups are asynchronous EXTI edges (the clocks restart on the edge). In Standby/Shutdown only a handful of always-on sources remain, and each one causes a reset.
| Source | Stop 0/1/2 | Standby | Shutdown |
|---|---|---|---|
| WKUP1..WKUP5 pins | via EXTI 0–15 (if mapped) | yes (EWUPn) | yes (EWUPn) |
| GPIO EXTI lines 0–15 | yes | no (use WKUP pins) | no |
| RTC alarm A/B | yes (EXTI 18) | yes | yes (LSE) |
| RTC wakeup timer | yes (EXTI 20) | yes | yes (LSE) |
| RTC tamper / timestamp | yes (EXTI 19) | yes | yes (LSE) |
| LPTIM1/2, LPUART1, I2C3 | yes | no | no |
| Comparators COMP1/2 | yes (EXTI 21/22) | no | no |
| IWDG | reset | reset | no (LSI off) |
| NRST pin / BOR | reset | reset | NRST only (BOR off) |
Selected EXTI internal lines (RM0432)
EXTI lines 0–15 are the GPIO edge detectors; from 16 up they are internal peripheral wakeup lines. The load-bearing ones for low power:
| Line | Source | Config register bits |
|---|---|---|
| 0–15 | GPIO PXn edge | IMR1/EMR1, RTSR1/FTSR1, SYSCFG_EXTICR |
| 16 | PVD output | IMR1 bit 16 |
| 18 | RTC alarms A/B | IMR1/RTSR1 bit 18 |
| 19 | RTC tamper / timestamp / LSECSS | IMR1/RTSR1 bit 19 |
| 20 | RTC wakeup timer | IMR1/RTSR1 bit 20 |
| 21 / 22 | COMP1 / COMP2 output | IMR1/RTSR1 bits 21/22 |
| 23+ | I2Cx / USARTx / LPUART1 / LPTIMx / PVMx / USB wakeups | see RM0432 §EXTI (some in IMR2/RTSR2) |
For a peripheral to wake the core from Stop, its EXTI line must be unmasked in EXTI_IMR1 (interrupt) and given a trigger edge in EXTI_RTSR1/FTSR1. Enabling only the peripheral interrupt (e.g. RTC_CR.WUTIE) is not enough — without line 20 unmasked the core stays asleep forever. Standby/Shutdown wakeups do not use the configurable EXTI mask; they go through the internal wakeup line and always reset.
09 Gotchas & common mistakes
Almost every "it won't sleep / won't wake / draws too much" bug is one of these.
SCB->SCR |= SLEEPDEEP makes WFI do plain Sleep instead of Stop/Standby — current stays high and nothing "went to sleep". Conversely, leaving it set turns a later intended Sleep into a Stop.RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN. Enable it once at startup.PWR_CR1.DBP = 1. Without it your RTC config silently no-ops.WUFn (or the RTC WUTF, or EXTI_PR1) is still set, WFI returns immediately. Clear flags as the LAST step before sleeping — and clear EXTI_PR1 and WUTF inside the ISR.WUTIE but not EXTI line 20. Set EXTI_IMR1 bit 20 + EXTI_RTSR1 bit 20 (rising) and enable RTC_WKUP_IRQn in the NVIC.STOPWUCK=1); PLL/HSE are off. Re-run SystemClock_Config() or you unknowingly execute 30× slower.RRS=1). Detect the wake with PWR_SR1.SBF and restore from backup registers.WUTE you must poll RTC_ISR.WUTWF = 1 before writing WUTR/WUCKSEL. Writing early is dropped and the period is wrong (often "wakes once, then never").PWR_PUCRx/PDCRx + APC=1.LPR. Doing it out of order gives undefined behaviour; to leave, clear LPR, wait REGLPF=0, then raise the clock.DBGMCU_CR DBG_STOP/DBG_STANDBY to keep debug alive — but clear them in production, they add current.WFE fall through. The idiom is __SEV(); __WFE(); __WFE(); — the first WFE consumes the self-posted event, the second actually waits.1) PWREN clock on · 2) wakeup source armed (RTC/EXTI) with its EXTI line unmasked + NVIC enabled · 3) all flags cleared · 4) LPMS = 010 · 5) SLEEPDEEP = 1 · 6) __DSB(); __WFI(); · 7) on return clear SLEEPDEEP and rebuild the clock.