4. USART 唤醒源:配置串口在停止模式下接收唤醒
低功耗系统里,串口唤醒是个很实用的功能。设备睡下去了,但还得随时准备接收上位机的指令。这时候如果每次都用外部中断去唤醒,布线麻烦不说,协议层还得额外处理。STM32L4 的 USART 支持在停止模式下直接检测起始位唤醒,说白了就是串口线上一个下降沿就能把芯片叫醒。
我个人习惯把这种唤醒方式叫做「静默监听」。MCU 睡大觉,串口模块还在偷偷盯着 RX 线。一旦检测到有效起始位,硬件自动拉高时钟,把系统从 Stop 模式拉回来,然后继续接收剩下的数据字节。整个过程对用户代码是透明的。
4.1 停止模式下的串口行为
先搞清楚一件事:STM32L4 的 Stop 模式分两种——Stop 0 和 Stop 1。Stop 0 保留 SRAM 和寄存器,唤醒更快;Stop 1 更省电,但唤醒稍慢。USART 在两种模式下都能工作,但时钟源必须选对。
USART 在停止模式下需要依赖 LSE 或 HSI16 作为时钟源。为什么?因为主时钟 PLL 和 HSI/HSE 在 Stop 模式下都关了。我刚开始做这个功能时踩过一个坑——用默认的 SYSCLK 去配 USART,进 Stop 后串口直接罢工,数据发不出去也收不到。后来查手册才发现,停止模式下 USART 必须切换到 LSE 或 HSI16。
| 时钟源 | 频率 | 适用场景 | 唤醒时间 |
|---|---|---|---|
| LSE | 32.768 kHz | 超低功耗,波特率 ≤ 9600 | 稍慢 |
| HSI16 | 16 MHz | 高波特率,功耗略高 | 快 |
4.2 唤醒源配置步骤
配置 USART 唤醒其实就三步。别被手册里那一堆寄存器吓到,核心逻辑很清晰。
- 使能 USART 的停止模式时钟 —— 在进入 Stop 前,调用
HAL_UARTEx_StopModeClockConfig()或直接操作UART_CR3_UCESM位。 - 选择唤醒条件 —— 设置
UART_CR3_WUS位域,可选起始位唤醒、数据匹配唤醒或空闲总线唤醒。最常用的是起始位唤醒。 - 使能唤醒功能 —— 置位
UART_CR3_WUFIE中断使能位,然后调用HAL_UARTEx_EnableStopMode()。
代码实现也不复杂。下面是我在一个 LoRa 网关项目里用过的配置片段:
/* 配置 USART2 停止模式唤醒 */
UART_StopModeConfigTypeDef stopModeCfg;
stopModeCfg.ClockSource = UART_STOPMODE_CLOCKSOURCE_LSE; /* 用 LSE 省电 */
stopModeCfg.WakeUpSource = UART_STOPMODE_WAKEUPSOURCE_STARTBIT; /* 起始位唤醒 */
stopModeCfg.EnableLPUART = DISABLE; /* 这里用普通 USART,不是 LPUART */
HAL_UARTEx_StopModeConfig(&huart2, &stopModeCfg);
HAL_UARTEx_EnableStopMode(&huart2);
/* 使能唤醒中断 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_WUF);
HAL_UART_Receive_IT() 启动接收中断。唤醒中断和接收中断是两回事。唤醒中断只负责把系统叫醒,数据接收由后续的接收中断或 DMA 完成。
4.3 唤醒后的数据接收处理
系统被唤醒后,USART 会继续接收当前帧的剩余字节。但有个细节:唤醒中断触发时,第一个字节可能已经接收了一部分。这时候 RXNE 标志位可能还没置起来。
我建议的处理流程是这样的:
- 在
HAL_UARTEx_WakeupCallback()中,先清除 WUF 标志,然后启动一次单字节接收中断。 - 收到第一个字节后,再根据协议决定是否启动 DMA 或继续中断接收。
- 如果数据量不大,直接用中断接收完整个报文。
我曾经在一个项目中偷懒,直接在唤醒回调里调用 HAL_UART_Receive_DMA(),结果发现 DMA 启动时第一个字节已经丢了。原因是唤醒到 DMA 启动之间有几十微秒的延迟,串口数据已经流过去了。后来改成先收一个字节再切 DMA,问题解决。
4.4 知识结构图:USART 唤醒流程
下面这张图把整个唤醒流程串起来了。从系统进入 Stop 模式开始,到串口检测起始位、触发唤醒、恢复时钟、接收数据,最后回到低功耗状态。
4.5 实测中的避坑指南
配置看起来简单,实际调试时问题不少。我列几个自己踩过的坑:
- 唤醒后 RXNE 不置位 —— 检查波特率是否匹配。如果唤醒时钟源是 LSE,波特率不能超过 9600。我试过用 LSE 跑 115200,结果唤醒后收到的全是乱码。
- 频繁误唤醒 —— 如果 RX 线上有噪声,起始位检测可能被误触发。可以在 RX 引脚上加一个 10kΩ 上拉电阻,或者使能 USART 的噪声滤波功能。
- 唤醒后系统卡死 —— 检查中断优先级。WUF 中断优先级不能高于 SysTick,否则在唤醒过程中可能造成调度混乱。
4.6 功耗与性能的权衡
最后聊点实际的。USART 唤醒虽然方便,但不是免费的。即使芯片在 Stop 模式,USART 模块本身还在耗电。LSE 作为时钟源时,USART 的额外功耗大约 1-2 µA;如果用 HSI16,这个数字会升到 8-10 µA。
如果你的系统对功耗极其敏感,比如纽扣电池供电、要求待机电流低于 2 µA,那 USART 唤醒可能就不太合适了。这时候可以考虑 LPUART——低功耗串口,用 LSE 时钟时额外功耗只有 0.5 µA 左右。不过 LPUART 的波特率上限只有 9600,而且 FIFO 深度也浅一些。
嗯,USART 唤醒这部分内容就到这里。配置不复杂,但细节不少。记住时钟源、唤醒条件、中断处理这三个关键点,基本不会出大问题。