4. 工作模式详解(上):运行模式、休眠模式与深度休眠模式
各位同学,今天我们来聊聊 MCUCMS32 的三种核心工作模式。说实话,低功耗设计里最基础也最关键的就是搞懂这几种模式。我见过不少工程师,代码写得飞起,结果一测功耗直接傻眼——就是因为没把模式切换玩明白。
咱们先看一张总览图,把三种模式的关系理清楚。
4.1 运行模式下的功耗优化
很多人觉得运行模式就是让 CPU 全速跑,没什么好优化的。其实不然。运行模式下的功耗优化,说白了就是「别让 CPU 做无用功」。
我个人习惯把运行模式优化分成三个层面:
- 时钟频率动态调整:不是所有任务都需要 48MHz。比如轮询按键,8MHz 绰绰有余。我建议根据任务紧急程度动态切换系统时钟。
- 外设按需使能:用不到的 SPI、I2C、UART 模块,记得关掉时钟。我在项目中遇到过,一个没用的 UART 模块开着,白白多吃了 2mA 电流。
- 等待指令替代忙等:别用
while(flag==0);这种傻等。用WFI或WFE指令,让 CPU 在等待时进入低功耗状态。
运行模式功耗优化清单
| 优化项 | 典型功耗节省 | 实现方式 |
|---|---|---|
| 降低主频 | 0.5~3 mA | 修改 PLL 分频系数 |
| 关闭未用外设时钟 | 0.1~2 mA | RCC 外设时钟使能寄存器 |
| 使用 WFI/WFE 等待 | 0.3~1 mA | WFI 指令替代忙等 |
| 优化代码执行路径 | 0.2~0.8 mA | 减少循环次数,使用查表法 |
举个例子。我之前做一款手持设备,主频跑 48MHz,电池续航只有 6 小时。后来我把主频降到 16MHz,配合外设时钟管理,续航直接翻到 14 小时。你想想看,只是改了几行代码,效果立竿见影。
4.2 休眠模式的进入与退出
休眠模式,也叫 Sleep Mode。这个模式下 CPU 时钟停了,但 SRAM 和寄存器内容都保留,外设时钟可以继续跑。
进入休眠模式很简单,就两步:
// 步骤1:设置 SLEEPDEEP 位为 0
SCB->SCR &= ~(1 << 2);
// 步骤2:执行 WFI 或 WFE 指令
__WFI();
嗯,这里要注意。WFI 和 WFE 的区别很多人搞混。WFI 是等到中断来了才唤醒,WFE 是只要有事件就唤醒。我个人习惯用 WFI,因为唤醒时机更可控。
退出休眠模式的方式有几种:
- 任何中断:只要中断使能了,就能唤醒 CPU
- 事件:通过事件线唤醒,适合外设间协同
- 复位:最粗暴的方式,一般不推荐
小技巧:如果你想让某个外设中断唤醒,但又不希望它执行完整的中断服务函数,可以在中断入口判断一下,如果是唤醒中断,直接清标志位返回。这样可以减少不必要的处理时间。
我曾经在一个项目中,用定时器中断每 10ms 唤醒一次 CPU,执行完 ADC 采样后继续休眠。这样平均电流从 5mA 降到了 0.8mA。说白了,休眠模式就是「短平快」——进去快,出来也快。
4.3 深度休眠模式的配置
深度休眠模式,也叫 Deep Sleep Mode。这个模式更狠——CPU 停了,大部分外设也停了,只有少数几个模块还能工作,比如 RTC、LPTIM、LPUART。
进入深度休眠模式的配置稍微复杂一点:
// 步骤1:设置 SLEEPDEEP 位为 1
SCB->SCR |= (1 << 2);
// 步骤2:选择电压调节器模式(可选)
// 0 = 正常模式,1 = 低功耗模式
PWR->CR |= (1 << 1); // 低功耗模式
// 步骤3:配置唤醒源
// 使能 RTC 唤醒
RTC->CR |= (1 << 10); // WUTE 位
// 步骤4:执行 WFI
__WFI();
避坑指南:我曾经在深度休眠模式下踩过一个坑——唤醒后系统时钟没有自动恢复。原因是进入深度休眠前,我把系统时钟切换到了内部低速振荡器,但唤醒后没有切回来。解决办法是在唤醒后的第一件事就是重新配置系统时钟。
深度休眠模式的唤醒源有限,常见的有:
| 唤醒源 | 特点 | 典型应用 |
|---|---|---|
| RTC 闹钟 | 定时唤醒,精度高 | 周期性数据采集 |
| 外部中断 | 响应快,引脚触发 | 按键唤醒、传感器触发 |
| LPUART | 低功耗串口唤醒 | 无线模块数据接收 |
| 比较器 | 模拟信号触发 | 电池电压监测唤醒 |
三种模式的功耗对比,我整理了一张表:
| 模式 | 典型电流 | 唤醒时间 | 保留内容 |
|---|---|---|---|
| 运行模式 | 5~50 mA | — | 全部 |
| 休眠模式 | 0.5~5 mA | 几微秒 | SRAM + 寄存器 |
| 深度休眠模式 | 1~50 μA | 几十微秒 | SRAM(可选保留) |
你看,深度休眠模式功耗最低,但唤醒时间也最长。所以实际项目中,我一般这样用:
- 需要频繁响应的任务(比如按键扫描)→ 用休眠模式
- 需要长时间待机的任务(比如传感器每隔 1 分钟采集一次)→ 用深度休眠模式
- 需要快速处理的任务(比如通信协议栈)→ 用运行模式,但做好时钟和外设管理
核心原则:在满足实时性要求的前提下,尽可能选择功耗更低的模式。不要为了省电而牺牲功能,也不要为了性能而浪费电。