Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
STM32F407 Low Power Modes
Master Sleep, Stop, and Standby modes — understand PWR_CR, WFI/WFE instructions, wakeup sources, voltage regulator options, and how to minimize current consumption in battery-powered designs.
01 Low Power Mode Overview
The STM32F407 supports three low-power modes, each trading wakeup latency for current reduction:
| Mode | CPU | Peripherals | SRAM | VDD core reg | Typical I_DD | Wakeup time |
|---|---|---|---|---|---|---|
| Run | 168 MHz | All on | On | On (full) | ~140 mA | — |
| Sleep | Off | On (clocked) | On | On (full) | ~1 mA typical | Immediate |
| Stop | Off | Off (no AHB/APB) | On | LP regulator | ~0.5 mA (LP) | ~5 µs (HSI restart) |
| Standby | Off | Off (except RTC/IWDG/PA0) | Off* | Off (only VBAT) | ~2.4 µA | ~50 µs (full reset) |
*In Standby, SRAM content is lost. Standby is essentially a partial power-off. On wakeup, the chip goes through Reset_Handler again.
Figures are typical from STM32F407 datasheet. Actual values depend on VDD, temperature, active peripherals, and GPIO state.
02 PWR_CR — Power Control Register
All low-power mode configuration is done through PWR_CR at 0x40007000. You must enable the PWR peripheral clock first (RCC_APB1ENR bit 28).
| Bits | Name | Description |
|---|---|---|
| 16 | UDEN[1:0] | Under-drive enable (bits 17:16) for Stop mode voltage reduction |
| 14 | VOS[1:0] | Voltage scaling (bits 15:14): 11=Scale 1 (1.2V, 168MHz), 10=Scale 2 (1.1V, 144MHz), 01=Scale 3 (1.0V, 120MHz) |
| 9 | FPDS | Flash power-down in Stop mode: 1=Flash off (slower wakeup, lower power) |
| 8 | DBP | Disable backup domain write protection: set 1 to write RTC/BKP registers |
| 7:5 | PLS[2:0] | PVD (programmable voltage detector) level |
| 4 | PVDE | PVD enable |
| 3 | CSBF | Clear standby flag: write 1 to clear SBF in PWR_CSR |
| 2 | CWUF | Clear wakeup flag: write 1 to clear WUF in PWR_CSR |
| 1 | PDDS | Power-down deepsleep: 0=Stop mode, 1=Standby mode (when SLEEPDEEP=1) |
| 0 | LPDS | Low-power deepsleep: 0=main regulator, 1=low-power regulator in Stop mode |
PWR_CSR (at 0x40007004) provides status: WUF (wakeup flag), SBF (standby flag), PVDO (PVD output), EWUP (enable PA0 wakeup pin).
03 Sleep Mode
Sleep mode stops the CPU clock but keeps all peripherals running. DMA, timers, ADC, UART — all continue. The CPU wakes on any interrupt. This is the lowest-overhead power saving: just execute WFI (Wait For Interrupt) in your idle loop.
C — Sleep mode
#define SCB_SCR (*(volatile uint32_t *)0xE000ED10)
/* Sleep mode entry — CPU stops, peripherals continue */
void Enter_Sleep(void) {
/* Ensure SLEEPDEEP = 0 (sleep, not deep sleep) */
SCB_SCR &= ~(1<<2); /* SLEEPDEEP = 0 */
/* Execute WFI — CPU halts until next interrupt */
__asm__ volatile("wfi");
/* Returns here after interrupt is serviced */
}
/* Typical idle loop pattern */
int main(void) {
SystemInit();
/* Configure peripherals, enable interrupts... */
while (1) {
/* Check flags set by ISRs */
if (data_ready) {
process_data();
data_ready = 0;
}
/* Sleep until next interrupt — saves ~90% CPU power */
Enter_Sleep();
}
}
SCB_SCR.SLEEPONEXIT=1 to automatically enter sleep whenever an ISR returns (without executing any main-code at all). The CPU only runs ISRs — perfect for interrupt-driven designs.04 Stop Mode
Stop mode halts all clocks (HSI, HSE, PLL) but keeps SRAM content and registers. The voltage regulator is still on (or in low-power mode if LPDS=1). Wakeup sources: EXTI lines, RTC alarm, USB, Ethernet. On wakeup, HSI is selected as SYSCLK — you must re-run SystemInit to restore 168 MHz.
C — Stop mode with EXTI wakeup on PA0
#define RCC_APB1ENR (*(volatile uint32_t *)0x40023840)
#define PWR_CR (*(volatile uint32_t *)0x40007000)
#define PWR_CSR (*(volatile uint32_t *)0x40007004)
#define SCB_SCR (*(volatile uint32_t *)0xE000ED10)
/* EXTI0 on PA0 — Discovery USER button */
#define EXTI_IMR (*(volatile uint32_t *)0x40013C00)
#define EXTI_RTSR (*(volatile uint32_t *)0x40013C08)
void EXTI0_Setup_Wakeup(void) {
volatile uint32_t *RCC_AHB1ENR = (volatile uint32_t *)(0x40023830);
volatile uint32_t *SYSCFG_EXTICR1 = (volatile uint32_t *)(0x40013808);
volatile uint32_t *RCC_APB2ENR_R = (volatile uint32_t *)(0x40023844);
*RCC_AHB1ENR |= (1<<0); /* GPIOAEN */
*RCC_APB2ENR_R |= (1<<14); /* SYSCFGEN */
/* PA0 input (MODER=00 default) */
/* SYSCFG: EXTI0 → GPIOA (EXTICR1[3:0] = 0000) — already default */
*SYSCFG_EXTICR1 &= ~(0xF<<0); /* EXTI0 = GPIOA */
/* EXTI0: unmask + rising trigger */
EXTI_IMR |= (1<<0);
EXTI_RTSR |= (1<<0);
/* Enable EXTI0 IRQ (IRQ6) in NVIC */
volatile uint32_t *NVIC_ISER0 = (volatile uint32_t *)0xE000E100;
*NVIC_ISER0 |= (1<<6);
}
void EXTI0_IRQHandler(void) {
volatile uint32_t *EXTI_PR = (volatile uint32_t *)(0x40013C14);
*EXTI_PR = (1<<0); /* clear pending bit */
/* Woke from Stop mode — re-init clocks below */
}
void Enter_Stop(void) {
/* Enable PWR clock */
RCC_APB1ENR |= (1<<28); /* PWREN */
/* Clear wakeup flag */
PWR_CR |= (1<<2); /* CWUF */
/* Configure Stop mode:
PDDS=0 (Stop, not Standby)
LPDS=1 (low-power voltage regulator — saves ~0.3 mA vs main regulator)
FPDS=1 (Flash off in stop — saves ~10 µA) */
PWR_CR &= ~(1<<1); /* PDDS = 0 → Stop */
PWR_CR |= (1<<0); /* LPDS = 1 → LP regulator */
PWR_CR |= (1<<9); /* FPDS = 1 → Flash off */
/* Set SLEEPDEEP in System Control Register */
SCB_SCR |= (1<<2); /* SLEEPDEEP = 1 */
/* Enter Stop — CPU halts here */
__asm__ volatile("wfi");
/* ── WOKEN UP ── */
/* Clear SLEEPDEEP so next WFI is just sleep, not deep sleep */
SCB_SCR &= ~(1<<2);
/* After Stop, HSI is SYSCLK at 16MHz — restore PLL/168MHz */
SystemInit();
}
int main(void) {
SystemInit();
EXTI0_Setup_Wakeup();
while (1) {
/* Do work... */
Enter_Stop();
/* Resumed at 168MHz after button press */
}
}
05 Standby Mode
Standby is the deepest low-power mode — the main voltage regulator is off, all SRAM contents are lost, and the CPU essentially undergoes a power-cycle on wakeup. Only the RTC, IWDG, backup SRAM, and PA0 wakeup pin are powered.
C — Enter Standby, wakeup on PA0
#define PWR_CSR (*(volatile uint32_t *)0x40007004)
void Enter_Standby(void) {
RCC_APB1ENR |= (1<<28); /* PWREN */
/* Enable PA0 wakeup pin */
PWR_CSR |= (1<<8); /* EWUP = 1 */
/* Clear wakeup and standby flags */
PWR_CR |= (1<<2); /* CWUF */
PWR_CR |= (1<<3); /* CSBF */
/* Set PDDS = 1 (Standby, not Stop) */
PWR_CR |= (1<<1); /* PDDS */
/* SLEEPDEEP in SCB */
SCB_SCR |= (1<<2);
/* Enter Standby — ~2.4 µA until PA0 wakeup */
__asm__ volatile("wfi");
/* Wakeup resets chip — code below is never reached */
while(1);
}
/* In main: check if we're waking from Standby */
void check_standby_wakeup(void) {
RCC_APB1ENR |= (1<<28);
if (PWR_CSR & (1<<1)) { /* SBF = standby flag */
/* We just woke from Standby — SRAM was cleared */
PWR_CR |= (1<<3); /* clear SBF */
/* Restore state from backup SRAM or Flash */
}
}
06 WFI vs WFE Instructions
| Instruction | Name | Wakeup condition | Use case |
|---|---|---|---|
| WFI | Wait For Interrupt | Any enabled interrupt (NVIC pending bit set) | Standard sleep — wake on timer, EXTI, UART, etc. |
| WFE | Wait For Event | Any event (SEVONPEND, SEV instruction, or external event) | Multi-processor sync, low-latency polling without interrupt overhead |
| SEV | Send Event | — | Wakes a CPU sleeping on WFE (multicore) |
07 Wakeup Sources by Mode
| Wakeup Source | Sleep | Stop | Standby |
|---|---|---|---|
| Any NVIC interrupt | ✓ | ✓ (via EXTI) | ✗ |
| EXTI line (GPIO) | ✓ | ✓ | ✗ (PA0 only via EWUP) |
| PA0 Wakeup pin (EWUP) | ✓ | ✓ | ✓ |
| RTC Alarm A/B | ✓ | ✓ | ✓ |
| RTC Wakeup timer | ✓ | ✓ | ✓ |
| RTC Tamper/Timestamp | ✓ | ✓ | ✓ |
| IWDG reset | ✓ | ✓ | ✓ (causes reset) |
| USB OTG wakeup | ✓ | ✓ | ✗ |
| Ethernet wakeup | ✓ | ✓ | ✗ |
| NRST pin | ✓ | ✓ | ✓ |
08 RTC Wakeup Timer
The RTC has a built-in auto-reload wakeup counter that works independently of the main clock. It runs on the LSE (32.768 kHz external crystal) or LSI (~32 kHz internal RC). Configure it to wake the chip periodically from Stop or Standby mode.
C — RTC wakeup timer configuration
/* RTC registers */
#define RTC_BASE 0x40002800UL
#define RTC_TR (*(volatile uint32_t *)(RTC_BASE+0x00))
#define RTC_CR (*(volatile uint32_t *)(RTC_BASE+0x08))
#define RTC_ISR (*(volatile uint32_t *)(RTC_BASE+0x0C))
#define RTC_PRER (*(volatile uint32_t *)(RTC_BASE+0x10))
#define RTC_WUTR (*(volatile uint32_t *)(RTC_BASE+0x14))
#define RTC_WPR (*(volatile uint32_t *)(RTC_BASE+0x24))
/* RCC backup domain + PWR */
#define RCC_BDCR (*(volatile uint32_t *)0x40023870)
#define RCC_CSR (*(volatile uint32_t *)0x40023874)
void RTC_WakeupTimer_Init(uint16_t seconds) {
/* 1. Enable backup domain access */
RCC_APB1ENR |= (1<<28); /* PWREN */
PWR_CR |= (1<<8); /* DBP — disable write protection */
/* 2. Enable LSE (32.768 kHz crystal) */
RCC_BDCR |= (1<<0); /* LSEON */
while (!(RCC_BDCR & (1<<1))); /* wait LSERDY */
/* 3. RTC clock = LSE */
RCC_BDCR &= ~(3<<8);
RCC_BDCR |= (1<<8); /* RTCSEL = 01 = LSE */
RCC_BDCR |= (1<<15); /* RTCEN */
/* 4. Unlock RTC write protection */
RTC_WPR = 0xCA;
RTC_WPR = 0x53;
/* 5. Disable wakeup timer to configure */
RTC_CR &= ~(1<<10); /* WUTE = 0 */
while (!(RTC_ISR & (1<<2))); /* wait WUTWF */
/* 6. Set wakeup clock: ck_spre (1 Hz from RTC prescaler)
WUCKSEL = 100 → ck_spre, WUTR counts in seconds */
RTC_CR &= ~(7<<0);
RTC_CR |= (4<<0); /* WUCKSEL = 100 = ck_spre (1 Hz) */
/* 7. Set wakeup period: N seconds */
RTC_WUTR = seconds - 1; /* counter counts down from WUTR to 0 */
/* 8. Enable wakeup timer + interrupt */
RTC_CR |= (1<<14); /* WUTIE = interrupt enable */
RTC_CR |= (1<<10); /* WUTE = enable */
/* 9. Lock RTC */
RTC_WPR = 0xFF;
/* 10. Enable EXTI20 (RTC wakeup) for Stop mode wakeup */
volatile uint32_t *EXTI_IMR = (volatile uint32_t *)(0x40013C00);
volatile uint32_t *EXTI_RTSR = (volatile uint32_t *)(0x40013C08);
*EXTI_IMR |= (1<<20);
*EXTI_RTSR |= (1<<20);
/* Enable RTC_WKUP IRQ (IRQ3) in NVIC */
volatile uint32_t *NVIC_ISER0 = (volatile uint32_t *)0xE000E100;
*NVIC_ISER0 |= (1<<3);
}
void RTC_WKUP_IRQHandler(void) {
volatile uint32_t *EXTI_PR = (volatile uint32_t *)(0x40013C14);
RTC_WPR = 0xCA; RTC_WPR = 0x53;
RTC_ISR &= ~(1<<10); /* clear WUTF */
RTC_WPR = 0xFF;
*EXTI_PR = (1<<20); /* clear EXTI20 pending */
/* Woken from Stop mode — SystemInit() called next in Enter_Stop() */
}
09 Full Stop + RTC Wakeup Example
C — Wake every 10 seconds, sample ADC, sleep again
int main(void) {
SystemInit(); /* 168 MHz */
RTC_WakeupTimer_Init(10); /* wake every 10 seconds */
while (1) {
/* ── Active phase ── */
ADC1_Init();
uint16_t raw = ADC1_Read();
transmit_over_uart(raw);
/* ── Enter Stop mode (~0.5 mA until RTC fires) ── */
Enter_Stop();
/* Returns here 10 seconds later, at 168 MHz */
}
}
10 Low Power Tips
- Floating inputs consume current through the Schmitt trigger. Configure unused GPIOs as analog mode (MODER=11) to disable digital buffers.
- Avoid pull-up/pull-down resistors on unused pins — each resistor leaks ~50–100 µA if pin voltage is opposite polarity.
- Disable unused peripheral clocks in RCC_APB1ENR/APB2ENR/AHB1ENR before sleeping.
- Reduce SYSCLK before Stop entry if short wakeup latency is not needed — switching from 168 MHz to 16 MHz (HSI only) reduces dynamic current during active phase.
- Use voltage scaling:
PWR_CR.VOSset to Scale 3 (1.0V) for low-speed operation (<120 MHz) — saves ~10% CPU core power.
- Use a precision ammeter in series with VDD or measure voltage across a known shunt resistor.
- The Discovery board’s user LED (PD12–PD15) each draws ~3 mA — turn off all LEDs before measuring.
- The ST-LINK portion of the Discovery board draws current independently — measure on a custom PCB without ST-LINK for accurate MCU-only values.
STM32F407 Tutorial Series — Tutorial 21: Low Power | ← Startup | Watchdog →
Bare-metal register-level programming · No HAL library · STM32F407VGT6
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments