第2章:低功耗模式寄存器配置实战——以STM32为例配置PWR寄存器进入停止模式

低功耗设计,说白了就是让单片机在不需要干活的时候「睡一会儿」。我刚开始做嵌入式项目时,总觉得功耗无所谓,直到有一次做电池供电的传感器节点,两节AA电池撑不过三天……嗯,从那以后,我每次选型第一件事就是看低功耗模式怎么配。

这一章,咱们就拿STM32F4系列开刀,手把手配置PWR寄存器,让芯片进入停止模式(Stop Mode)。你想想看,一个跑在168MHz的Cortex-M4核心,功耗能从几百毫瓦降到微安级别,这中间的差距,就是寄存器那几位的事。

2.1 停止模式的核心原理

停止模式是什么?简单说:内核时钟停了,但SRAM和寄存器内容还在。外部中断一来,芯片就能「秒醒」。我在项目中遇到过需要低功耗待机同时保持快速响应的场景,停止模式就是最佳选择。

STM32的停止模式,本质上是关闭了HSE、HSI、PLL这些高频时钟源,但保留了LSE或LSI给RTC用。核心电压域(VDD)从正常模式降到1.2V左右,但备份域(VBAT)完全不受影响。

关键点:停止模式下,CPU、Flash、SRAM的供电并未切断,只是时钟停了。所以唤醒后不需要重新初始化外设,直接继续执行——这叫「暂停-恢复」机制。

2.2 PWR寄存器家族一览

配置停止模式,主要操作PWR(Power Controller)模块的这几个寄存器。我习惯把它们分成三组:控制组、状态组、配置组。

寄存器 偏移地址 关键位 作用
PWR_CR 0x00 LPDS、PDDS、CWUF 低功耗深度睡眠、电源下降、清除唤醒标志
PWR_CSR 0x04 EWUP、WUF、PVDO 唤醒引脚使能、唤醒标志、电源电压检测
PWR_CR2 0x08 VOS 调节器电压输出等级(F4系列特有)

注意看,PWR_CR的bit1(PDDS)决定了进入的是停止模式还是待机模式。PDDS=0是停止模式,PDDS=1是待机模式。这个位我当年看手册时漏掉了,结果芯片直接进了待机模式,所有寄存器全丢了,调试了一下午才找到原因。

2.3 进入停止模式的完整步骤

配置流程其实就四步,但每一步都有坑。我建议你按这个顺序来:

  1. 配置电源调节器模式——设置PWR_CR的LPDS位,决定调节器是正常模式还是低功耗模式
  2. 选择唤醒源——使能EXTI中断线,配置GPIO为中断模式
  3. 清除唤醒标志——写PWR_CR的CWUF位,清除之前残留的WUF标志
  4. 执行WFI或WFE指令——触发CPU进入停止模式

我的习惯:在进入停止模式前,先把所有不用的外设时钟关掉。RCC->AHB1ENR、RCC->APB1ENR、RCC->APB2ENR这些寄存器,能清0的位全清0。这样进入停止模式后,漏电流还能再降一个数量级。

2.4 代码实战:配置PWR进入停止模式

下面这段代码,是我从实际项目中抽出来的。它配置了PA0引脚作为外部唤醒源,然后让芯片进入停止模式。你直接复制到工程里就能用。

// 头文件包含
#include "stm32f4xx.h"

// 进入停止模式函数
void Enter_Stop_Mode(void)
{
    // 第一步:使能PWR时钟
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    
    // 第二步:配置电源调节器为低功耗模式
    // LPDS = 1:调节器在停止模式下进入低功耗状态
    // PDDS = 0:选择停止模式(非待机模式)
    PWR->CR |= PWR_CR_LPDS;
    PWR->CR &= ~PWR_CR_PDDS;
    
    // 第三步:清除唤醒标志
    // 写1清除WUF,防止上次唤醒残留导致立即唤醒
    PWR->CR |= PWR_CR_CWUF;
    
    // 第四步:配置EXTI唤醒源(PA0)
    // 使能SYSCFG时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // 配置PA0为EXTI0输入
    SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI0;
    SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA;
    
    // 配置EXTI0上升沿触发
    EXTI->IMR |= EXTI_IMR_MR0;      // 不屏蔽中断线0
    EXTI->RTSR |= EXTI_RTSR_TR0;    // 上升沿触发
    
    // 第五步:执行WFI指令进入停止模式
    __WFI();
    
    // 唤醒后从这里继续执行
    // 注意:唤醒后需要重新配置系统时钟!
}

我曾经踩过的坑:唤醒后系统时钟默认使用HSI(16MHz),如果你之前用的是HSE+PLL跑到168MHz,唤醒后外设时序全乱。所以唤醒后的第一件事,就是重新初始化RCC时钟树。我建议把SystemClock_Config()封装成一个函数,唤醒后直接调用。

2.5 停止模式与待机模式的区别

很多初学者分不清停止模式和待机模式。我画了一张对比表,你一看就明白:

特性 停止模式 待机模式
CPU供电 保留 切断
SRAM内容 保留 丢失
寄存器内容 保留 丢失
唤醒源 EXTI、RTC、USB等 WKUP引脚、RTC、NRST
唤醒时间 几微秒 几百微秒
典型功耗 几十微安 几微安
适用场景 需要快速唤醒、保持状态 超低功耗、允许冷启动

我个人习惯:如果项目需要保持传感器校准数据或通信状态,用停止模式;如果只是定时上报数据,待机模式更省电。

2.6 停止模式知识体系

为了让你更直观地理解停止模式的配置脉络,我画了一张流程图:

STM32停止模式配置流程 ① 使能PWR时钟 ② 配置PWR_CR(LPDS/PDDS) ③ 清除WUF唤醒标志 ④ 配置EXTI/GPIO唤醒源 ⑤ 执行WFI/WFE指令 ⚠ 注意 唤醒后系统时钟 默认使用HSI 需重新配置PLL 💡 建议 进入前关闭不用的 外设时钟降低漏电

2.7 常见问题与避坑指南

配置停止模式时,有几个问题我几乎每次培训都会被问到。这里统一回答:

  • 问:为什么我配置了停止模式,但芯片功耗还是很高?
    答:检查GPIO引脚状态。未使用的GPIO如果配置成浮空输入,会有漏电流。我习惯把所有不用的GPIO配置成模拟模式(MODER=11),这样最省电。
  • 问:唤醒后外设不能正常工作,怎么办?
    答:大概率是时钟问题。停止模式唤醒后,系统时钟自动切换到HSI。如果你的外设依赖PLL输出的高频时钟,需要重新初始化RCC。我建议在唤醒中断服务函数里直接调用SystemClock_Config()。
  • 问:停止模式和睡眠模式有什么区别?
    答:睡眠模式只停了CPU时钟,外设时钟还在跑;停止模式连外设时钟也停了。睡眠模式功耗在毫安级,停止模式在微安级。如果你需要外设继续工作(比如串口接收),用睡眠模式;如果只是等外部中断,用停止模式。

我的调试技巧:第一次调试停止模式时,别急着测功耗。先用一个LED指示进入和唤醒状态——进入前点亮LED,唤醒后熄灭。这样你能直观看到芯片是否真的进入了停止模式,以及唤醒是否正常。等逻辑验证通过后,再拿万用表或功耗分析仪测电流。