第4章:Stop模式详解——进入、退出与唤醒源实战

Stop模式,说白了就是STM32L4在低功耗设计里最常用的一个状态。它不像Sleep那么浅,也不像Standby那么深——刚好卡在中间,既能大幅省电,又能快速恢复工作。我个人做产品时,90%的低功耗场景用的都是Stop模式。

这一章,咱们就把Stop模式掰开揉碎了讲。从怎么进去、怎么出来,到SRAM怎么保数据,再到RTC、USART、LPUART这些唤醒源怎么配。最后我会带你看一段完整的RTC唤醒代码。

4.1 Stop模式的两种子模式

STM32L4的Stop模式其实分两种:Stop0 和 Stop1。它们的主要区别在于稳压器的状态。

特性 Stop0 Stop1
主稳压器(MR) 开启 关闭
低功耗稳压器(LPR) 关闭 开启
典型电流 ~100 µA ~3 µA
唤醒时间 ~5 µs ~15 µs
SRAM保持 全部 全部

你想想看,Stop1的电流只有Stop0的几十分之一,但唤醒时间多了10微秒。这个取舍,得看你项目的具体需求。我做过一个电池供电的传感器节点,每10秒采集一次数据,大部分时间都在睡——这种场景我肯定选Stop1,省电才是王道。

核心结论:需要频繁唤醒(比如毫秒级周期)用Stop0;追求极致续航用Stop1。

4.2 进入Stop模式

进入Stop模式其实就两步:配置寄存器,然后执行一条WFI或WFE指令。

关键寄存器是 PWR->CR1 里的 LPMS 位域。我习惯用HAL库,但底层原理你得懂。

// 进入Stop1模式的标准流程
void Enter_Stop1_Mode(void)
{
    // 1. 配置PWR寄存器:选择Stop1,关闭调压器
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    // 2. 执行WFI后,MCU在这里停住
    //    直到被唤醒,才会继续往下走
    
    // 3. 唤醒后第一件事:重新配置系统时钟
    SystemClock_Config();
}

这里有个坑,我踩过好几次——唤醒后必须重新配置时钟。因为Stop模式会把HSE、HSI都关掉,醒来后默认用的是MSI。如果你的外设跑在80MHz,醒来后不切时钟,直接就卡死了。

我曾经...在一个量产项目里忘了在唤醒后重配时钟,结果设备每隔几分钟就死一次。查了两天才找到原因。从那以后,我把时钟重配写成了一个强制调用的函数,每次唤醒后第一件事就是调用它。

4.3 SRAM保持策略

Stop模式下,SRAM的内容是全部保持的。这一点比Standby模式强太多——Standby会把SRAM清空,连寄存器都得重新配。

但保持归保持,功耗还是有讲究的。STM32L4的SRAM分成了多个bank,你可以通过 PWR->CR3 里的 SRAMxP 位,把不用的bank断电。

SRAM Bank 地址范围 控制位
SRAM1 0x2000 0000 - 0x2000 BFFF PWR_CR3_SRAM1P
SRAM2 0x2000 C000 - 0x2000 FFFF PWR_CR3_SRAM2P

我建议你把关键数据放在SRAM1,把临时缓冲区放在SRAM2。进入Stop前,把SRAM2断电,能省下大约20 µA的电流。别小看这20 µA,在电池供电的产品里,可能就是多撑一个月的差距。

小技巧:断电前确保SRAM2里的数据已经处理完了。如果还有DMA在往SRAM2写数据,断电会导致数据丢失。

4.4 唤醒源详解

Stop模式的唤醒源比Sleep多,比Standby少。常用的有三个:RTC、USART、LPUART。

4.4.1 RTC唤醒

RTC是Stop模式下最可靠的唤醒源。它有自己的独立时钟(LSE或LSI),不受主时钟影响。你可以设置RTC闹钟,或者用RTC的周期性唤醒定时器。

我个人偏爱用周期性唤醒——配置简单,功耗低。比如每1秒唤醒一次,检查传感器数据,然后继续睡。

4.4.2 USART唤醒

USART在Stop模式下可以保持接收功能。只要RX线上检测到起始位,就能把MCU唤醒。这个功能叫 USART Stop Mode Wake-up。

配置要点:

  • 使能USART的 WUF(Wake-up from Stop mode)功能
  • 选择唤醒条件:起始位、地址匹配、或数据帧匹配
  • 确保USART时钟在Stop模式下保持开启(通过RCC的 LPUARTxEN 位)

注意:USART唤醒只支持Stop0和Stop1,不支持Standby。而且唤醒后,接收到的第一个字节可能会丢失——因为MCU醒来需要时间。我一般会在协议里加一个前导码,用来让MCU先"热个身"。

4.4.3 LPUART唤醒

LPUART是低功耗版的USART。它的最大优势是:可以在极低时钟下工作。LPUART可以用LSE(32.768 kHz)作为时钟,波特率最低能到300 bps。

LPUART的唤醒机制和USART类似,但功耗更低。我做过一个远程传感器,用LPUART接收唤醒命令,平时电流只有1.5 µA左右。

唤醒源 典型功耗 唤醒延迟 适用场景
RTC ~0.5 µA ~5 µs 定时唤醒、周期性任务
USART ~10 µA ~10 µs 串口通信、外部触发
LPUART ~1 µA ~15 µs 低功耗串口、远程唤醒

4.5 代码实战:RTC唤醒Stop模式

下面这段代码,是我从一个量产项目里抽出来的。它实现了:进入Stop1模式,每5秒被RTC唤醒一次,唤醒后翻转一个LED,然后继续睡。

#include "stm32l4xx_hal.h"

RTC_HandleTypeDef hrtc;

void RTC_Wakeup_Config(void)
{
    __HAL_RCC_RTC_ENABLE();
    
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
    hrtc.Init.AsynchPrediv = 127;   // 异步分频
    hrtc.Init.SynchPrediv = 255;    // 同步分频
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
    hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    HAL_RTC_Init(&hrtc);
    
    // 配置周期性唤醒:每5秒
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 5000, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
}

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 唤醒后的处理
    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    RTC_Wakeup_Config();
    
    while (1)
    {
        // 进入Stop1模式
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
        
        // 唤醒后重新配置时钟
        SystemClock_Config();
        
        // 这里可以加一些唤醒后的处理
        // 比如读取传感器、发送数据等
    }
}

这段代码看起来简单,但有几个细节你得注意:

  • RTC_WAKEUPCLOCK_CK_SPRE_16BITS 用的是1Hz的时钟,所以 5000 就是5秒
  • 唤醒回调函数里不要做耗时操作,否则会影响下一次睡眠
  • 每次唤醒后必须重配时钟,这个我前面强调过了

实测数据:用这段代码跑在STM32L431上,Stop1模式下的总电流大约是2.8 µA(包括RTC和LPR)。每5秒唤醒一次,平均功耗不到3 µA。一颗200 mAh的纽扣电池,理论上能撑7年以上。

4.6 Stop模式唤醒流程图

下面这张图,把Stop模式的完整生命周期画出来了。从进入、保持、到唤醒,每一步都标清楚了。

Stop模式唤醒流程图 Stop模式唤醒流程图 正常运行 配置PWR寄存器(LPMS) 执行WFI/WFE Stop模式(SRAM保持) 唤醒源 RTC闹钟/周期性唤醒 USART/LPUART起始位 外部中断(EXTI) 唤醒(中断触发) 重配系统时钟 回到正常运行,继续执行

这张图把Stop模式的完整流程串起来了。你注意看右侧的唤醒源部分——RTC、USART、LPUART、外部中断,都可以把MCU从Stop模式里拉出来。但每个唤醒源的配置和功耗都不一样,选哪个得看你的具体需求。

总结一下:Stop模式是STM32L4低功耗设计的核心。它保留了SRAM数据,支持多种唤醒源,功耗和唤醒时间之间可以灵活取舍。用好Stop模式,你的产品续航就能上一个台阶。