第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 进入停止模式的完整步骤
配置流程其实就四步,但每一步都有坑。我建议你按这个顺序来:
- 配置电源调节器模式——设置PWR_CR的LPDS位,决定调节器是正常模式还是低功耗模式
- 选择唤醒源——使能EXTI中断线,配置GPIO为中断模式
- 清除唤醒标志——写PWR_CR的CWUF位,清除之前残留的WUF标志
- 执行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 停止模式知识体系
为了让你更直观地理解停止模式的配置脉络,我画了一张流程图:
2.7 常见问题与避坑指南
配置停止模式时,有几个问题我几乎每次培训都会被问到。这里统一回答:
- 问:为什么我配置了停止模式,但芯片功耗还是很高?
答:检查GPIO引脚状态。未使用的GPIO如果配置成浮空输入,会有漏电流。我习惯把所有不用的GPIO配置成模拟模式(MODER=11),这样最省电。 - 问:唤醒后外设不能正常工作,怎么办?
答:大概率是时钟问题。停止模式唤醒后,系统时钟自动切换到HSI。如果你的外设依赖PLL输出的高频时钟,需要重新初始化RCC。我建议在唤醒中断服务函数里直接调用SystemClock_Config()。 - 问:停止模式和睡眠模式有什么区别?
答:睡眠模式只停了CPU时钟,外设时钟还在跑;停止模式连外设时钟也停了。睡眠模式功耗在毫安级,停止模式在微安级。如果你需要外设继续工作(比如串口接收),用睡眠模式;如果只是等外部中断,用停止模式。
我的调试技巧:第一次调试停止模式时,别急着测功耗。先用一个LED指示进入和唤醒状态——进入前点亮LED,唤醒后熄灭。这样你能直观看到芯片是否真的进入了停止模式,以及唤醒是否正常。等逻辑验证通过后,再拿万用表或功耗分析仪测电流。