第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模式的完整流程串起来了。你注意看右侧的唤醒源部分——RTC、USART、LPUART、外部中断,都可以把MCU从Stop模式里拉出来。但每个唤醒源的配置和功耗都不一样,选哪个得看你的具体需求。
总结一下:Stop模式是STM32L4低功耗设计的核心。它保留了SRAM数据,支持多种唤醒源,功耗和唤醒时间之间可以灵活取舍。用好Stop模式,你的产品续航就能上一个台阶。