16-PWR电源控制.md 10 KB

16-PWR电源控制

原理

STM32 电源系统

           3.3V
            │
  ┌─────────┼──────────┐
  │         │          │
  VDD      VDDA      VREF      VBAT
  (主电源)  (模拟电源)  (参考电压)  (备份电池)
  │         │          │          │
  │    ┌────┘          │     ┌────┘
  │    │               │     │
  ├─ 内置稳压器 ── 1.8V内核 ─┤
  │                         │
  └─── BKP + RTC ───────────┘
电源引脚 说明
VDD 3.3V 主数字电源
VDDA 3.3V 模拟电源 (ADC/DAC/PLL)
VREF ADC 参考电压 (部分封装内部接 VDDA)
VBAT 备份电池 (RTC + BKP 后备供电)

修改主频流程

1. 配置 Flash 等待周期 (72MHz 需 2 个等待周期)
2. 修改 HCLK 预分频 (AHB)
3. 修改 APB1/APB2 预分频
4. 修改 PLL 倍频系数 (或直接使用 HSI/HSE)

3 种低功耗模式

                     ┌─────────────────┐
                     │    睡眠 Sleep    │
                     │ CPU 停,外设正常  │
                     │ 任意中断唤醒     │
                     └────────┬────────┘
                              │
                     ┌────────▼────────┐
                     │    停止 Stop     │
                     │  HSI/HSE/PLL 停  │
                     │ SRAM/寄存器保留   │
                     │ EXTI/外部中断唤醒 │
                     └────────┬────────┘
                              │
                     ┌────────▼────────┐
                     │   待机 Standby   │
                     │  全部停 (包括调压器)│
                     │ SRAM/寄存器丢失   │
                     │ WKUP/RTC/复位唤醒│
                     └─────────────────┘
特性 Sleep Stop Standby
CPU
HSI/HSE/PLL 运行
电压调节器 运行 低功耗/运行
SRAM/寄存器 保留 保留 丢失
备份域 保留 保留 保留
唤醒源 任意中断 EXTI (GPIO/RTC) WKUP/RTC/NRST
唤醒后 从中断处继续 重新配置时钟 相当于复位

电流对比 (典型值)

  • 运行模式:约 50mA (72MHz)
  • Sleep:约 20mA
  • Stop:约 20μA (调压器低功耗)
  • Standby:约 2μA

API 表格

PWR

函数 说明
void PWR_DeInit(void) 复位 PWR 寄存器
void PWR_BackupAccessCmd(FunctionalState NewState) 使能备份域访问
void PWR_PVDCmd(FunctionalState NewState) 使能电源电压检测
void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel) 配置 PVD 阈值
void PWR_WakeUpPinCmd(FunctionalState NewState) 使能 WKUP 引脚 (PA0)
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint32_t PWR_STOPEntry) 进入停止模式
void PWR_EnterSTANDBYMode(void) 进入待机模式
void PWR_EnterSleepMode(void) 进入睡眠模式 (实际用 __WFI() 更方便)

PWR_EnterSTOPMode 参数

参数 可选值 说明
PWR_Regulator PWR_Regulator_ON 调压器运行 (唤醒快)
PWR_Regulator_LowPower 调压器低功耗 (省电)
PWR_STOPEntry PWR_STOPEntry_WFI WFI 指令进入
PWR_STOPEntry_WFE WFE 指令进入

其他配合函数

函数 说明
__WFI(void) 汇编 WFI 指令,进入 Sleep/Stop
__WFE(void) 汇编 WFE 指令
__SEV(void) 汇编 SEV 指令 (发送事件)
SCB->SCR 系统控制寄存器 (SLEEPONEXIT 位)

完整代码示例

修改主频 (72MHz → 36MHz)

/*------------------------------------------------
 * SetClock_36MHz.c
 *------------------------------------------------*/

#include "stm32f10x.h"

void SetSysClockTo36MHz(void)
{
    /* 1. Flash 等待周期:36MHz 需 1 个等待周期 */
    FLASH_SetLatency(FLASH_Latency_1);

    /* 2. 修改 AHB 预分频:HCLK = SYSCLK / 2 = 36MHz */
    RCC->CFGR &= ~RCC_CFGR_HPRE;
    RCC->CFGR |= RCC_CFGR_HPRE_Div2;

    /* 3. APB1 = HCLK / 1 = 36MHz (APB1 最高 36MHz) */
    RCC->CFGR &= ~RCC_CFGR_PPRE1;
    RCC->CFGR |= RCC_CFGR_PPRE1_Div1;

    /* 4. APB2 = HCLK / 1 = 36MHz */
    RCC->CFGR &= ~RCC_CFGR_PPRE2;
    RCC->CFGR |= RCC_CFGR_PPRE2_Div1;
}

int main(void)
{
    SetSysClockTo36MHz();

    /* 此时系统时钟为 36MHz */
    while (1);
}

睡眠模式 + 外部中断唤醒

/*------------------------------------------------
 * SleepMode_Example.c
 * 睡眠模式,PA0 外部中断唤醒
 *------------------------------------------------*/

#include "stm32f10x.h"

void GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;       // 上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void EXTI_Config(void)
{
    EXTI_InitTypeDef EXTI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    EXTI_InitStruct.EXTI_Line = EXTI_Line0;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);

    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line0);
        /* 唤醒后从此继续执行 (不是 main 开头) */
    }
}

int main(void)
{
    GPIO_Config();
    EXTI_Config();

    while (1)
    {
        /* 进入睡眠模式 (Sleep) */
        __WFI();
        /* 中断唤醒后继续往下执行 */
    }
}

停止模式 + WKUP 唤醒

/*------------------------------------------------
 * StopMode_Example.c
 * 停止模式,PA0 (WKUP) 上升沿唤醒
 * 注意:唤醒后必须重新配置系统时钟!
 *------------------------------------------------*/

#include "stm32f10x.h"

void SystemClock_Reconfig(void)
{
    /* 重新配置系统时钟到 72MHz */
    /* 省略标准 SystemInit() 调用,或调用 HSI/HSE+PLL 启动函数 */
    SystemInit();
}

int main(void)
{
    /* 使能 WKUP 引脚 (PA0) */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    PWR_WakeUpPinCmd(ENABLE);

    while (1)
    {
        /* 进入停止模式 (调压器低功耗, WFI) */
        PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);

        /* ---- 唤醒后从这里继续 ---- */

        SystemClock_Reconfig();                     // ★ 必须重新配置时钟
        /* 继续其他操作 */
    }
}

待机模式 + RTC 闹钟唤醒

/*------------------------------------------------
 * StandbyMode_Example.c
 * 待机模式,RTC 闹钟 5 秒后唤醒
 * 注意:待机唤醒 = 复位,从 main 开头执行!
 *------------------------------------------------*/

#include "stm32f10x.h"

void RTC_ConfigForStandby(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);

    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
    {
        /* 初始化 RTC: LSE, 1Hz */
        RCC_LSEConfig(RCC_LSE_ON);
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
        RCC_RTCCLKCmd(ENABLE);
        RTC_WaitForSynchro();
        RTC_WaitForLastTask();

        RTC_SetPrescaler(32767);
        RTC_WaitForLastTask();

        /* 设置闹钟 (当前时间 + 5 秒) */
        RTC_SetAlarm(RTC_GetCounter() + 5);
        RTC_WaitForLastTask();

        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
    }
    else
    {
        /* 已配置,等 RTC 同步 */
        RTC_WaitForSynchro();
        /* 唤醒后 (待机=复位) 需重新设置闹钟 */
        RTC_SetAlarm(RTC_GetCounter() + 5);
        RTC_WaitForLastTask();
    }
}

int main(void)
{
    /* 重启后检查是否是待机唤醒 */
    if (RCC_GetFlagStatus(RCC_FLAG_SFTRST) || RCC_GetFlagStatus(RCC_FLAG_PORRST) ||
        RCC_GetFlagStatus(RCC_FLAG_PINRST))
    {
        /* 上电复位,正常启动 */
    }

    if (RCC_GetFlagStatus(RCC_FLAG_WU) != RESET)
    {
        /* 待机唤醒复位 */
        RCC_ClearFlag();
    }

    SystemInit();

    RTC_ConfigForStandby();

    /* 使能 WKUP (待机模式也需) */
    PWR_WakeUpPinCmd(ENABLE);

    /* 5 秒后闹钟触发唤醒 */
    PWR_EnterSTANDBYMode();

    /* ---- 不会执行到这里 ---- */
    while (1);
}

常见坑点

  1. 待机模式相当于复位

    • 从 Standby 唤醒后 MCU 从 main 开头执行(不是中断处)
    • SRAM 和寄存器内容全部丢失
    • 备份域 (BKP/RTC) 内容保留
  2. 停止模式唤醒后需重配时钟

    • Stop 模式 HSI/HSE/PLL 都停了
    • 唤醒后必须重新调用 SystemInit() 配置系统时钟
    • 否则外设时钟异常 (默认 HSI 8MHz)
  3. WKUP 引脚是 PA0

    • PWR_WakeUpPinCmd(ENABLE) 固定使用 PA0
    • 用于 Standby 唤醒(上升沿)
    • 在 Stop 模式也可用作唤醒源
  4. SLEEPONEXIT 位

    • SCB->SCR |= 1 << 1 设置 SLEEPONEXIT
    • 中断退出时自动进入睡眠 (用于中断驱动的应用)
    • 不清除该位时,中断唤醒后继续执行主循环
  5. PVD (可编程电压检测)

    • 可配置电压阈值 (2.2V~2.9V)
    • 电压低于阈值时产生 PVD 中断
    • 用于断电保护 (保存数据到 Flash)
  6. 停止模式调试

    • 调试器在停止模式下可能断开 (SWD 停)
    • 可用 DBGMCU_Config(DBGMCU_STOP, ENABLE) 保持调试
    • 进入停止模式前可设断点观察
  7. PWR 时钟必须使能

    • 调用 PWR_EnterSTOPMode() / PWR_EnterSTANDBYMode()
    • 必须使能 PWR 时钟: RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE)
  8. WFI 与 WFE 区别

    • WFI (Wait For Interrupt): 有中断挂起时退出
    • WFE (Wait For Event): 有事件挂起时退出
    • __SEV() 发送事件唤醒 WFE