第4章:MCU低功耗模式详解:Sleep、Stop、Standby模式切换与唤醒源配置

各位做智能电表的朋友,咱们今天聊聊MCU的三种低功耗模式。说实话,这玩意儿我刚开始接触的时候也犯过迷糊——Sleep、Stop、Standby,名字看着差不多,实际用起来天差地别。

我记得有一次做一款单相电表,客户要求电池供电下能撑5年。我一开始图省事,全程用Sleep模式,结果功耗死活降不下来。后来仔细一查手册,才发现Stop模式才是我的菜。嗯,这里面的门道,我今天一次性给你讲透。

4.1 三种模式的本质区别

说白了,这三种模式就是MCU在「省电」和「响应速度」之间做的三个不同取舍。你想想看,就像你晚上睡觉——

  • Sleep模式:相当于「眯一会儿」,CPU停了,但外设还在跑。唤醒最快,但省电最少。
  • Stop模式:相当于「深度睡眠」,CPU和外设都停了,只有SRAM和寄存器保持。唤醒稍慢,但省电效果明显。
  • Standby模式:相当于「关机」,几乎全部断电,只有RTC和几个唤醒引脚在工作。唤醒最慢,但功耗最低。

我个人的习惯是:需要频繁唤醒做数据采集的,用Sleep;需要长时间待机、偶尔唤醒上报的,用Stop;电池供电、几个月才唤醒一次的,用Standby。

模式 CPU状态 外设状态 SRAM保持 典型电流 唤醒时间
Sleep 停止 运行 ~1mA ~1μs
Stop 停止 停止 ~10μA ~10μs
Standby 断电 断电 ~1μA ~100μs

4.2 Sleep模式:最常用的「浅睡」

Sleep模式其实最简单。CPU停了,但时钟还在跑,外设该干嘛干嘛。我在智能电表里经常用它来做「间歇性采样」——比如每10ms唤醒一次,读一下电压电流,然后继续睡。

代码实现也很直接:

// 进入Sleep模式
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

// 唤醒后自动从下一行继续执行
// 这里放你的采样代码
adc_value = HAL_ADC_GetValue(&hadc1);

这里有个坑,我曾经踩过——中断优先级。如果你在Sleep模式下,某个外设的中断优先级设置不对,可能根本唤不醒MCU。我建议你把唤醒源的中断优先级设成最高,省得折腾。

我的小技巧:在进入Sleep前,先把不用的外设时钟关了。虽然Sleep模式下外设还能跑,但你不关时钟,功耗照样高。我一般会写个函数,专门管理外设时钟的开关。

4.3 Stop模式:电表设计的「主力军」

Stop模式是我在智能电表项目里用得最多的。为什么?因为电表大部分时间都在等——等下一个采样周期,等通信指令,等按键触发。这些等待时间,用Stop模式最合适。

进入Stop模式后,所有时钟都停了,但SRAM和寄存器内容还在。这意味着你的程序状态、变量值都不会丢。唤醒后,直接从断点继续跑。

// 进入Stop模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

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

// 然后继续执行
printf("已从Stop模式唤醒\n");

注意看,唤醒后我调用了SystemClock_Config()。这是必须的!因为Stop模式下时钟停了,唤醒后时钟配置会丢失。我见过有人忘了这步,结果程序跑得乱七八糟,查了半天才发现是时钟没重新配。

警告:进入Stop模式前,一定要确保所有外设的DMA传输已经完成。否则DMA正在搬数据的时候你突然停了时钟,数据会丢,严重时可能死机。我曾经因为这个原因,在产线上废了3块板子...

4.4 Standby模式:终极省电方案

Standby模式,说白了就是「除了RTC和唤醒引脚,其他全关」。功耗能降到1μA以下,但代价是SRAM内容全丢。程序得从头开始跑,就像按了复位键一样。

什么时候用?我举个例子:电表里的电池供电备份模式。主电源断了,电池供电,这时候你只需要RTC计时,偶尔记录一下断电时间。用Standby模式,一块CR2032电池能撑好几年。

// 进入Standby模式
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnterSTANDBYMode();

// 唤醒后,程序从main函数重新开始
// 所以你需要判断一下复位原因
if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) {
    // 清除Standby标志
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    printf("从Standby模式唤醒\n");
}

这里有个关键点:唤醒后怎么知道是从Standby回来的?我一般会在main函数开头检查复位标志。如果是Standby唤醒,就跳过初始化流程,直接恢复运行。如果是上电复位,就做完整的初始化。

4.5 唤醒源配置:选对「闹钟」很重要

三种模式对应不同的唤醒源,选错了,MCU就睡死过去了。我整理了一张表,你直接照着配就行:

唤醒源 Sleep Stop Standby 典型应用
外部中断(EXTI) 按键、脉冲输入
RTC闹钟 定时采样、定时上报
USART 通信唤醒
定时器 高频采样
ADC 模拟量触发

我个人最推荐用RTC闹钟做唤醒源。为什么?因为RTC本身功耗极低(微安级),而且可以精确到秒甚至亚秒级。在智能电表里,我通常用RTC设置一个1秒的闹钟,每秒唤醒一次做数据采集,其余时间都在Stop模式里待着。

核心要点:

  • Sleep模式:适合频繁唤醒,外设需要持续工作的场景
  • Stop模式:适合中等频率唤醒,需要保持SRAM数据的场景
  • Standby模式:适合极低频率唤醒,可以接受程序重启的场景
  • 唤醒后一定要检查复位原因,尤其是Standby模式
  • RTC闹钟是最通用的唤醒源,功耗低、精度高

好了,关于MCU低功耗模式的三种切换和唤醒源配置,我就讲这么多。你想想看,其实选哪种模式,关键就看你的应用场景——要响应快就Sleep,要省电就Standby,折中就Stop。没有最好的模式,只有最合适的模式。

下一章我会讲外设的功耗管理,包括ADC、USART、SPI这些模块怎么在低功耗模式下合理使用。到时候我会分享一个我踩过的坑——USART在Stop模式下怎么唤醒MCU,那个问题折腾了我整整两天...