What is STM32F407 Low Power Modes-Embedded C Course Online

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Tutorial 21 — Power Management

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.

~1 mA Sleep mode~0.5 mA Stop mode~2.4 µA Standby modeWFI/WFE InstructionsRTC Wakeup source

01 Low Power Mode Overview

The STM32F407 supports three low-power modes, each trading wakeup latency for current reduction:

ModeCPUPeripheralsSRAMVDD core regTypical I_DDWakeup time
Run168 MHzAll onOnOn (full)~140 mA—
SleepOffOn (clocked)OnOn (full)~1 mA typicalImmediate
StopOffOff (no AHB/APB)OnLP regulator~0.5 mA (LP)~5 µs (HSI restart)
StandbyOffOff (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.

Diagram: 01 Low Power Mode Overview
Run (168MHz)
~140 mA
Sleep
~1 mA
Stop (LP)
~0.5 mA
Standby
~2.4 µA

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).

BitsNameDescription
16UDEN[1:0]Under-drive enable (bits 17:16) for Stop mode voltage reduction
14VOS[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)
9FPDSFlash power-down in Stop mode: 1=Flash off (slower wakeup, lower power)
8DBPDisable backup domain write protection: set 1 to write RTC/BKP registers
7:5PLS[2:0]PVD (programmable voltage detector) level
4PVDEPVD enable
3CSBFClear standby flag: write 1 to clear SBF in PWR_CSR
2CWUFClear wakeup flag: write 1 to clear WUF in PWR_CSR
1PDDSPower-down deepsleep: 0=Stop mode, 1=Standby mode (when SLEEPDEEP=1)
0LPDSLow-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();
    }
}
Sleep-on-exit: Set 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.

SRAM is lost in Standby! Save any state to backup SRAM (4KB at 0x40024000, kept by VBAT) or to external Flash/EEPROM before entering Standby.

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

InstructionNameWakeup conditionUse case
WFIWait For InterruptAny enabled interrupt (NVIC pending bit set)Standard sleep — wake on timer, EXTI, UART, etc.
WFEWait For EventAny event (SEVONPEND, SEV instruction, or external event)Multi-processor sync, low-latency polling without interrupt overhead
SEVSend Event—Wakes a CPU sleeping on WFE (multicore)
WFI with pending interrupt: If an interrupt was pending before WFI executes, WFI returns immediately without sleeping. Always clear pending interrupt flags before entering sleep if you don’t want an immediate wakeup.

07 Wakeup Sources by Mode

Wakeup SourceSleepStopStandby
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

GPIO Power
  • 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.
Clock Reduction Before Sleep
  • 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.VOS set to Scale 3 (1.0V) for low-speed operation (<120 MHz) — saves ~10% CPU core power.
Measuring Real Consumption
  • 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

Leave a Reply

Your email address will not be published. Required fields are marked *