4. 低功耗模式基础(二):停止模式

停止模式,说白了就是让内核睡大觉,但保留SRAM和寄存器里的东西。跟睡眠模式不一样,停止模式下CPU时钟和大部分外设时钟都停了,但SRAM和寄存器内容不掉。我刚开始用STM32L4的时候,总觉得停止模式就是“深度睡眠”,其实没那么简单——它分了三个等级:Stop0、Stop1、Stop2。

4.1 Stop0 / Stop1 / Stop2 到底差在哪?

这三个模式的核心区别,在于唤醒速度和功耗之间的取舍。你想想看,唤醒越快,意味着内部稳压器必须保持更高的“待命”状态,自然就更耗电。

特性 Stop0 Stop1 Stop2
稳压器模式 MR(主稳压器) LPR(低功耗稳压器) LPR(低功耗稳压器)
唤醒时间 约 5 µs 约 10 µs 约 15 µs
典型功耗 ~ 100 µA ~ 10 µA ~ 3 µA
SRAM保持 全部保持 全部保持 可配置(部分掉电)
适用场景 需要快速响应 平衡模式 超低功耗待机

我个人习惯是:如果项目对响应速度要求高,比如按键唤醒后要立刻显示,我会选Stop0。如果是电池供电的传感器节点,几秒才上报一次数据,那Stop2是首选。

4.2 SRAM保持策略——别让你的数据“蒸发”

进入停止模式前,你得想清楚:哪些数据必须活着?哪些可以丢?

STM32L4的SRAM分成了多个区域。在Stop2模式下,你可以通过配置SRAM2的保持位,让关键数据留在SRAM2里,而SRAM1可以完全掉电。我在项目中遇到过一个问题:一个温湿度采集节点,每次唤醒后都要重新初始化传感器,但校准参数存在SRAM1里,结果每次唤醒都丢了——后来我把校准参数挪到SRAM2,问题就解决了。

关键配置:

  • PWR_CR1 寄存器中的 MRUDS 和 LPUDS 位控制稳压器模式
  • PWR_CR2 寄存器中的 SRAM2PDS 位控制SRAM2是否掉电
  • 使用 HAL_PWR_EnterSTOPMode() 时传入对应参数
// 进入 Stop2 模式,保持 SRAM2
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

// 或者更精细的控制——手动配置
PWR->CR1 |= PWR_CR1_LPUDS;   // 低功耗稳压器
PWR->CR1 |= PWR_CR1_FPDS;    // 闪存掉电
PWR->CR2 |= PWR_CR2_SRAM2PDS; // SRAM2 保持,SRAM1 可掉电

注意:如果你在Stop2模式下把SRAM2也掉电了,那唤醒后所有数据都会丢失。我曾经调试一个设备,发现每次唤醒后系统时间都归零了,查了半天才发现是SRAM2配置错了——嗯,从那以后我每次都会检查SRAM保持配置。

4.3 RTC唤醒——最常用的定时唤醒方式

RTC(实时时钟)在停止模式下依然可以运行。它靠的是LSE(32.768 kHz)或LSI(约32 kHz)时钟,功耗极低。你可以设置RTC闹钟或周期性唤醒定时器,让系统在指定时间醒来。

我个人习惯用RTC的周期性唤醒定时器(WUT),因为它不需要设置绝对时间,只需要配置唤醒间隔。比如一个温度采集节点,每10秒采集一次,用WUT就非常方便。

// 配置 RTC 周期性唤醒,每 10 秒唤醒一次
RTC_WakeUpCmd(DISABLE);

RTC_SetWakeUpCounter(10 * 32768 / 16); // 假设 LSE = 32.768 kHz,WUT 时钟分频 16
RTC_WakeUpCmd(ENABLE);

// 进入停止模式,等待 RTC 唤醒
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

小技巧:RTC唤醒中断的优先级建议设得比普通外设中断低一些,避免在唤醒瞬间被其他中断“抢跑”,导致系统状态混乱。

4.4 外部中断唤醒——让外部事件叫醒系统

除了定时唤醒,外部中断也是常用的唤醒方式。STM32L4的所有GPIO都可以配置为外部中断源。你只需要把对应的引脚配置为中断模式,然后在停止模式下,只要该引脚出现指定的电平变化,系统就会醒来。

我做过一个手持设备,用户按一下按键就唤醒屏幕。按键接在PA0上,配置为下降沿触发。进入停止模式后,按下按键,系统在几微秒内就恢复了运行。

// 配置 PA0 为外部中断,下降沿触发
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// 使能中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);

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

避坑指南:我曾经犯过一个错误——在进入停止模式前没有把外部中断的触发条件配置好,结果系统一进入停止模式就被“假唤醒”,功耗根本降不下来。后来我加了一个延时去抖,并且在中断服务函数里清除了挂起标志位,问题才解决。

4.5 实测停止模式功耗——数据说话

理论归理论,实际测一下才知道差距。我用STM32L476RG Nucleo板,配合一个精密电阻(10Ω)串联在电源路径上,用示波器测电压降,换算成电流。下面是我实测的数据:

模式 理论值 实测值 备注
Stop0 ~ 100 µA 112 µA 所有外设时钟关闭
Stop1 ~ 10 µA 9.8 µA RTC 运行,LSE 开启
Stop2 ~ 3 µA 2.7 µA SRAM2 保持,SRAM1 掉电

你会发现,实测值和理论值基本吻合。但要注意,如果板子上有额外的上拉电阻、LED指示灯或者调试接口,功耗会明显偏高。我建议你在做低功耗设计时,先把所有不用的外设都关掉,包括GPIO的上拉/下拉。

实测要点:

  • 使用精密电阻(1%精度)串联在电源路径上
  • 示波器设置为DC耦合,垂直分辨率调到mV级
  • 测量前先让系统稳定运行几秒钟,避免瞬态电流干扰
  • 多次测量取平均值,减少误差

4.6 停止模式的核心逻辑

下面这张图帮你理清停止模式的选择逻辑:

停止模式选择逻辑 进入停止模式 需要快速唤醒? Stop0(MR模式) 需要超低功耗? Stop2(LPR + SRAM2保持) Stop1(LPR模式) 说明: • Stop0:唤醒最快(~5 µs),但功耗最高(~100 µA),适合需要快速响应的场景。 • Stop1:平衡模式,唤醒时间约10 µs,功耗约10 µA,适合大多数应用。 • Stop2:超低功耗(~3 µA),唤醒最慢(~15 µs),适合电池供电的间歇性工作设备。

选择停止模式时,我建议你先评估两个指标:唤醒时间和平均功耗。如果系统大部分时间都在睡觉,偶尔醒来干点活,那Stop2是最佳选择。如果系统需要频繁响应外部事件,比如按键、通信中断,那Stop0或Stop1更合适。

个人经验:我做过一个智能门锁项目,平时用Stop2模式,功耗只有2.8 µA。用户刷卡时,通过外部中断唤醒,系统在15 µs内完成身份验证并开锁。如果当时选了Stop0,功耗会高出几十倍,电池寿命从两年缩水到几个月。

好了,停止模式的内容就这些。记住:选对模式,配好SRAM保持策略,再结合RTC或外部中断唤醒,你的低功耗设计就成功了一大半。