18-电源管理与低功耗模式.md 15 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(扩展篇)V1.0.0 — 第1章:电源控制 + 配套代码15~20" author: "尚硅谷研究院" date: 2026-07-16

created: 2026-07-15

电源管理与低功耗模式

用生活理解:STM32 有多种低功耗模式就像你家里的电器——运行模式是所有电器全开(费电),睡眠模式是关灯但空调继续开(CPU 停,外设继续),停止模式是只留冰箱(维持寄存器),待机模式是拉总闸(只剩下 Vbat 供 RTC)。越省电的模式唤醒越慢。


电源框图

STM32 的电源系统分为多个供电区域:

区域 电压 供电对象 特点
VDD 区域 3.3V I/O 电路、待机电路、电压调节器 数字部分主电源
VDDA 区域 3.3V ADC、温度传感器、复位模块、PLL 模拟部分独立电源(去耦更好)
1.8V 区域 1.8V CPU 内核、SRAM、内部数字外设 由电压调节器从 3.3V 降压得到
备份区域 (VBAT) 3.3V/电池 RTC、LSE 振荡器、BKP 寄存器、PC13~15 VDD 断电时由 VBAT 维持

参考:参考手册 §5(电源控制 PWR 寄存器)、原理图电源部分 参考:AMS1117 数据手册(板上 5V→3.3V LDO)

电压调节器

电压调节器将 VDD(3.3V) 转换为内核所需的 1.8V,有 3 种工作模式:

调节器模式 输出状态 对应的 MCU 模式
运转模式 (MR) 正常输出 1.8V 运行/睡眠
停止模式 (LPR) 低功耗输出 1.8V(电流小) 停止
待机模式 关闭(无输出) 待机(内核掉电)

上电复位(POR)与掉电复位(PDR)

内部复位电路监控 VDD/VDDA 电压:

电压升高到 VPOR(~1.8V) → 解除复位 → MCU 启动
电压下降到 VPDR(~1.5V) → 进入复位 → MCU 停止

两者之间有 40mV 的迟滞,防止电压在阈值附近抖动时频繁复位。

参考:参考手册 §5.2(上电/掉电复位特性)


可编程电压检测器(PVD)

PVD(Programmable Voltage Detector)监控 VDD 电压,当电压低于可编程阈值时,可以产生 中断(不是复位)。

PVD 阈值选择 (PLS[2:0]) 阈值电压
000 2.2V
001 2.3V
010 2.4V
011 2.5V
100 2.6V
101 2.7V
110 2.8V
111 2.9V
// PVD 配置示例:阈值 2.7V,下降沿中断
PWR->CR |= PWR_CR_PLS_2 | PWR_CR_PLS_1;   // PLS[2:0]=110 → 2.8V
PWR->CR |= PWR_CR_PVDE;                    // PVDE=1 使能PVD
// PVD 中断: EXTI16 线
EXTI->RTSR &= ~EXTI_RTSR_TR16;             // 下降沿触发(电压下降)
EXTI->FTSR |= EXTI_FTSR_TR16;
EXTI->IMR |= EXTI_IMR_MR16;
NVIC_EnableIRQ(PVD_IRQn);

// PVD 中断处理
void PVD_IRQHandler(void)
{
    if (EXTI->PR & EXTI_PR_PR16)
    {
        SaveEmergencyData();
        EXTI->PR |= EXTI_PR_PR16;
    }
}

三种低功耗模式对比

特性 睡眠 (Sleep) 停止 (Stop) 待机 (Standby)
CPU 内核 停止 停止 断电
SRAM/寄存器 保持 保持 丢失
备份域 保持 保持 保持
电压调节器 运转模式 低功耗模式 关闭
典型功耗 ~10mA ~30μA ~2μA
唤醒源 任意中断/事件 EXTI 中断 RTC 闹钟、WKUP 引脚、NRST
唤醒时间 ~0.9μs ~5μs ~50ms(需重新初始化)

睡眠模式(Sleep)

CPU 停止,外设继续工作。任何中断或事件都能唤醒。

进入方式

__WFI();          // 等待中断唤醒
__WFE();          // 等待事件唤醒

SLEEP-NOW vs SLEEP-ON-EXIT

SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;  // SLEEPDEEP=0, SLEEPONEXIT=0
// SLEEP-NOW: 执行 WFI 立即进睡眠

SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;    // SLEEPONEXIT=1
// SLEEP-ON-EXIT: 执行 WFI → 最低优先级 ISR 退出后进睡眠

停止模式(Stop)

CPU + 所有外设时钟都停止,但 SRAM 和寄存器内容保留。电压调节器切到低功耗模式。

void enter_stop_mode(void)
{
    SCB->SCR |= SCB_SCR_SLEEPDEEP;    // 深度睡眠
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR &= ~PWR_CR_PDDS;          // PDDS=0 表示停止模式
    PWR->CR |= PWR_CR_LPDS;           // 电压调节器低功耗模式
    __WFI();
}

唤醒后:触发 EXTI 中断后,系统从停止模式恢复,执行 ISR。

待机模式(Standby)

最省电模式。1.8V 内核域完全断电(SRAM 和寄存器内容丢失),仅备份域(VBAT)和待机电路保持。

void enter_standby_mode(void)
{
    SCB->SCR |= SCB_SCR_SLEEPDEEP;    // 深度睡眠
    PWR->CR |= PWR_CR_PDDS;           // PDDS=1 表示待机模式
    PWR->CSR |= PWR_CSR_EWUP;         // 使能 PA0 唤醒功能
    __WFI();
}
// 唤醒后相当于系统复位!从 startup 重新执行。

实验:睡眠/停止/待机模式

所有 6 个项目均来自 stm32/15~20_lowpower_xxx,覆盖寄存器版和 HAL 库版三种低功耗模式。

睡眠模式(Sleep)

项目 15_lowpower_sleep_register(寄存器版)

文件:stm32/15_lowpower_sleep_register/User/main.c

#include "usart.h"
#include "delay.h"
#include "led.h"

void enter_sleep_mode(void);

int main(void)
{
    USART_Init();
    LED_Init();

    printf("尚硅谷低功耗实验:睡眠模式...\n");

    LED_On(LED1);
    Delay_s(2);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入睡眠模式...\n");
        Delay_s(3);
        printf("进入睡眠模式");
        enter_sleep_mode();

        printf("从睡眠模式唤醒...\n");
        Delay_s(2);
    }
}

void enter_sleep_mode(void)
{
    // SLEEP-NOW: SLEEPDEEP=0, 执行 WFI
    SCB->SCR &= ~SCB_SCR_SLEEPDEEP;
    __WFI();
}

项目 16_lowpower_sleep_hal(HAL 库版)

文件:stm32/16_lowpower_sleep_hal/Core/Src/main.c

#include "main.h"
#include "usart.h"
#include "gpio.h"

uint8_t ch;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();

    printf("尚硅谷低功耗实验:睡眠模式...\n");

    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
    HAL_Delay(2000);

    HAL_UART_Receive_IT(&huart1, &ch, 1);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入睡眠模式...\n");
        HAL_Delay(3000);
        printf("进入睡眠模式\n");

        HAL_SuspendTick();                             // 暂停 Systick
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,
                               PWR_SLEEPENTRY_WFI);   // 进睡眠
        HAL_ResumeTick();                              // 恢复 Systick

        printf("从睡眠模式唤醒...\n");
        HAL_Delay(2000);
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        printf("%c", ch);
        HAL_UART_Receive_IT(&huart1, &ch, 1);
    }
}

停止模式(Stop)

项目 17_lowpower_stop_register(寄存器版)

文件:stm32/17_lowpower_stop_register/User/main.c

#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"

void enter_stop_mode(void);
void get_clock_freq(uint32_t *sys_clock, uint32_t *AHB_clock,
                    uint32_t *APB2_clock, uint32_t *APB1_clock);

int main(void)
{
    USART_Init();
    LED_Init();
    KEY_Init();

    printf("尚硅谷低功耗实验:停止模式...\n");

    LED_On(LED_2);
    Delay_s(2);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入停止模式...\n");
        Delay_s(3);
        printf("进入停止模式...\n");
        Delay_ms(1);
        enter_stop_mode();

        uint32_t sys_clock, ahb_clock, apb1_clock, apb2_clock;
        get_clock_freq(&sys_clock, &ahb_clock, &apb2_clock, &apb1_clock);

        // 停止模式唤醒后 HSE/PLL 已关闭,需重新配置时钟
        SystemInit();

        printf("唤醒前的时钟频率:\nsys_clock = %d, ahb_clock = %d, ...\n\n",
                sys_clock, ahb_clock, apb1_clock, apb2_clock);

        get_clock_freq(&sys_clock, &ahb_clock, &apb2_clock, &apb1_clock);
        printf("唤醒后的时钟频率:\nsys_clock = %d, ahb_clock = %d, ...\n\n",
                sys_clock, ahb_clock, apb1_clock, apb2_clock);

        printf("从停止模式唤醒...\n");
        Delay_s(2);
    }
}

void enter_stop_mode(void)
{
    SCB->SCR |= SCB_SCR_SLEEPDEEP;
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR &= ~PWR_CR_PDDS;       // 停止模式
    PWR->CR |= PWR_CR_LPDS;        // 低功耗调节器
    __WFI();
}

void get_clock_freq(uint32_t *sys_clock, uint32_t *AHB_clock,
                    uint32_t *APB2_clock, uint32_t *APB1_clock)
{
    uint32_t clock_src = RCC->CFGR & RCC_CFGR_SWS;
    if (clock_src == RCC_CFGR_SWS_HSE)
        *sys_clock = HSE_VALUE;
    else if (clock_src == RCC_CFGR_SWS_HSI)
        *sys_clock = HSI_VALUE;
    else if (clock_src == RCC_CFGR_SWS_PLL)
    {
        uint32_t mul = ((RCC->CFGR & RCC_CFGR_PLLMULL) >> 18) + 2;
        *sys_clock = HSE_VALUE * mul;
    }

    uint32_t hpre = (RCC->CFGR & RCC_CFGR_HPRE) >> 4;
    if (hpre & 0x8)
    {
        uint32_t n = hpre & 0x7;
        *AHB_clock = (n < 4) ? (*sys_clock >> (n+1)) : (*sys_clock >> (n+2));
    }
    else *AHB_clock = *sys_clock;

    uint32_t ppre1 = (RCC->CFGR & RCC_CFGR_PPRE1) >> 8;
    *APB1_clock = (ppre1 & 0x4) ? (*AHB_clock >> ((ppre1 & 0x3) + 1)) : *AHB_clock;

    uint32_t ppre2 = (RCC->CFGR & RCC_CFGR_PPRE2) >> 11;
    *APB2_clock = (ppre2 & 0x4) ? (*AHB_clock >> ((ppre2 & 0x3) + 1)) : *AHB_clock;
}

项目 18_lowpower_stop_hal(HAL 库版)

文件:stm32/18_lowpower_stop_hal/Core/Src/main.c

#include "main.h"
#include "usart.h"
#include "gpio.h"

uint8_t ch;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();

    printf("尚硅谷低功耗实验:停止模式...\n");

    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
    HAL_Delay(2000);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入停止模式...\n");
        HAL_Delay(3000);
        printf("进入停止模式...\n");

        // HAL 停止模式:低功耗调节器 + WFI 进入
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);

        // 唤醒后需重新配置系统时钟
        SystemClock_Config();

        printf("从停止模式唤醒...\n");
        HAL_Delay(2000);
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        printf("%c", ch);
        HAL_UART_Receive_IT(&huart1, &ch, 1);
    }
}

待机模式(Standby)

项目 19_lowpower_standby_register(寄存器版)

文件:stm32/19_lowpower_standby_register/User/main.c

#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"

void enter_standby_mode(void);

int main(void)
{
    USART_Init();
    LED_Init();
    KEY_Init();

    RCC->APB1ENR |= RCC_APB1ENR_PWREN;

    if (PWR->CSR & PWR_CSR_SBF)
    {
        printf("从待机模式唤醒:\n");
        PWR->CR |= PWR_CR_CSBF;
    }
    if (PWR->CSR & PWR_CSR_WUF)
    {
        printf("产生了唤醒事件:\n");
        PWR->CR |= PWR_CR_CWUF;
    }

    printf("尚硅谷低功耗实验:待机模式...\n");

    LED_On(LED_2);
    Delay_s(2);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入待机模式...\n");
        Delay_s(3);
        printf("进入待机模式,按下KEY唤醒...\n");
        Delay_ms(1);
        enter_standby_mode();

        printf("从待机模式唤醒...\n");
        Delay_s(2);
    }
}

void enter_standby_mode(void)
{
    SCB->SCR |= SCB_SCR_SLEEPDEEP;
    PWR->CR |= PWR_CR_PDDS;           // 待机模式
    PWR->CSR |= PWR_CSR_EWUP;         // 使能 PA0 唤醒
    __WFI();
}

项目 20_lowpower_standby_hal(HAL 库版)

文件:stm32/20_lowpower_standby_hal/Core/Src/main.c

#include "main.h"
#include "usart.h"
#include "gpio.h"

uint8_t ch;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();

    // 检测唤醒标志
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB))
    {
        printf("从待机模式唤醒:\n");
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    }
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU))
    {
        printf("产生了唤醒事件:\n");
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    }

    printf("尚硅谷低功耗实验:待机模式...\n");

    HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
    HAL_Delay(2000);

    while (1)
    {
        printf("正常运行执行完毕,3s后进入待机模式...\n");
        HAL_Delay(3000);
        printf("进入待机模式,按下KEY唤醒...\n");

        // 使能 PA0 唤醒引脚
        HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

        // 进入待机模式(唤醒后相当于复位)
        HAL_PWR_EnterSTANDBYMode();

        printf("从待机模式唤醒...\n");
        HAL_Delay(2000);
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        printf("%c", ch);
        HAL_UART_Receive_IT(&huart1, &ch, 1);
    }
}

核心速查表

操作 寄存器操作 HAL 库函数
进入睡眠 SCB->SCR &= ~SLEEPDEEP; + __WFI() HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI)
进入停止 SLEEPDEEP=1 + PDDS=0 + LPDS=1 + __WFI() HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI)
进入待机 SLEEPDEEP=1 + PDDS=1 + EWUP=1 + __WFI() HAL_PWR_EnterSTANDBYMode()
使能 PA0 唤醒 `PWR->CSR = PWR_CSR_EWUP`
配置 PVD PWR->CR 配置 PLS + PVDE + EXTI16 HAL_PWR_ConfigPVD()
清除唤醒标志 `PWR->CR = PWR_CR_CSBF/CWUF`
检测待机唤醒 PWR->CSR & PWR_CSR_SBF __HAL_PWR_GET_FLAG(PWR_FLAG_SB)
暂停/恢复 Systick HAL_SuspendTick() / HAL_ResumeTick()

常见问题与避坑

  1. 停止模式功耗高 → 检查所有 GPIO 是否关闭上下拉、所有外设时钟是否关闭(RCC->APBxENR=0
  2. 无法唤醒 → 检查 EXTI 配置(必须配置为事件或中断)、NVIC 优先级
  3. 待机后 GPIO 输出乱 → 待机模式 GPIO 复位为默认浮空输入,恢复后需重新初始化
  4. 待机模式功耗还是很高 → 检查 VBAT 引脚是否接 VDD、WKUP 引脚是否有漏电路径
  5. 多次进出停止模式后异常 → 检查唤醒标志是否清除(PWR.CSR.WUF)
  6. HAL 睡眠必须暂停 SystickHAL_SuspendTick() / HAL_ResumeTick() 配对使用,否则 SysTick 中断会立即唤醒
  7. 停止模式唤醒后时钟异常 → 唤醒后 HSE/PLL 已关闭,需调用 SystemInit()HAL_RCC_ClockConfig() 重新配置