STM32F4 低功耗 STOP 模式下的 LPTIM 微秒级定时唤醒与串口数据保全实战
👁 1 阅读 · 2026-08-27 · 嵌入式
在电池供电的物联网设备中,STOP 模式是平衡功耗与响应速度的关键。但传统 RTC 唤醒精度不足,且唤醒后串口数据易丢失。本文深入剖析 STM32F4 的 LPTIM(低功耗定时器)在 STOP 模式下的微秒级定时唤醒机制,结合中断与 DMA 双缓冲策略,实现串口数据零丢失。通过原理讲解、寄存器配置、完整代码示例及调试陷阱,助你打造低功耗、高可靠性的嵌入式系统。
# 引言:低功耗与数据完整性的博弈
在嵌入式开发中,STOP 模式能大幅降低功耗(典型电流降至 µA 级),但唤醒延迟和通信中断常令人头疼。STM32F4 的 LPTIM 支持在 STOP 模式下运行,可提供微秒级定时中断,配合精心设计的串口接收策略,能完美解决“省电”与“不丢数据”的矛盾。
# 原理剖析:LPTIM 如何在 STOP 下“活”着
## 1. LPTIM 的时钟源与工作条件
- LPTIM 在 STOP 模式下依赖 **LSI(约 32 kHz)** 或 **LSE(32.768 kHz)** 时钟,这些低速时钟在 STOP 时保持运行。
- 通过配置 `RCC->CSR` 使能 LSI,并设置 `RCC->APB1ENR` 中的 LPTIM1 时钟使能。
- LPTIM 的计数频率 = 时钟源 / 预分频值(1~128),可实现从 30 µs 到数秒的定时范围。
## 2. 唤醒机制
- LPTIM 可配置为 **比较匹配中断**,当计数值达到预设的 ARR(自动重载值)时,产生事件并唤醒 CPU。
- 关键:在进入 STOP 前,需将 LPTIM 的中断使能(`LPTIM_IER_ARRMIE`),并确保 NVIC 中对应中断优先级高于临界区屏蔽级别。
## 3. 串口数据保全策略
- 使用 **DMA 接收 + 环形缓冲区**:在 STOP 前,配置 UART 的 DMA 接收模式,数据直接写入内存,不依赖 CPU。
- 唤醒后,CPU 从环形缓冲区读取数据,避免因 STOP 期间 CPU 暂停导致的溢出丢失。
# 配置步骤:从零到实战
## 步骤 1:时钟与 GPIO 初始化
```c
void SystemClock_Config_STOP(void) {
// 使能 LSI
RCC->CSR |= RCC_CSR_LSION;
while (!(RCC->CSR & RCC_CSR_LSIRDY));
// 配置 LPTIM 时钟(LSI 作为时钟源)
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN;
RCC->DCKCFGR2 |= RCC_DCKCFGR2_LPTIM1SEL_0; // LSI 选择
}
```
## 步骤 2:LPTIM 配置(定时 100 µs)
```c
void LPTIM_Init(uint32_t us) {
// 预分频:LSI 32kHz,分频 32 -> 1kHz,即 1ms 计数周期
LPTIM1->CFGR = LPTIM_CFGR_PRESC_2; // 分频 32
// 计算 ARR 值:100us = 0.1ms -> 计数 100 次
uint32_t arr = (us * 32) / 1000; // 粗略计算,实际需校准
LPTIM1->ARR = arr - 1;
// 使能比较匹配中断
LPTIM1->IER |= LPTIM_IER_ARRMIE;
NVIC_EnableIRQ(LPTIM1_IRQn);
NVIC_SetPriority(LPTIM1_IRQn, 2); // 高于临界区屏蔽级别
}
```
## 步骤 3:串口 DMA 接收配置
```c
#define RX_BUF_SIZE 256
uint8_t rx_buf[RX_BUF_SIZE];
volatile uint16_t rx_index = 0;
void UART_DMA_Init(void) {
// 使能 DMA1 时钟
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
// 配置 DMA 流(以 USART2_RX 为例)
DMA1_Stream5->CR = 0; // 先复位
DMA1_Stream5->PAR = (uint32_t)&USART2->RDR;
DMA1_Stream5->M0AR = (uint32_t)rx_buf;
DMA1_Stream5->NDTR = RX_BUF_SIZE;
DMA1_Stream5->CR = DMA_SxCR_CHSEL_1 | // 通道4(USART2_RX)
DMA_SxCR_PL_1 | // 高优先级
DMA_SxCR_MSIZE_0 | // 8位
DMA_SxCR_PSIZE_0 |
DMA_SxCR_MINC | // 内存递增
DMA_SxCR_CIRC; // 循环模式
// 使能 USART2 的 DMA 接收
USART2->CR3 |= USART_CR3_DMAR;
USART2->CR1 |= USART_CR1_RE | USART_CR1_UE;
DMA1_Stream5->CR |= DMA_SxCR_EN;
}
```
## 步骤 4:进入 STOP 模式
```c
void EnterStopMode(void) {
// 确保 DMA 已接收数据,但 CPU 暂停
__disable_irq(); // 临界区,防止中断干扰
// 设置 SLEEPDEEP 位
PWR->CR |= PWR_CR_PDDS; // 选择 STOP 模式
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
// 执行 WFI 指令
__WFI();
// 唤醒后恢复
__enable_irq();
// 重新配置系统时钟(因为 STOP 会关闭 HSI/HSE)
SystemClock_Config();
}
```
## 步骤 5:LPTIM 中断处理(唤醒后执行)
```c
void LPTIM1_IRQHandler(void) {
if (LPTIM1->ISR & LPTIM_ISR_ARRM) {
LPTIM1->ICR |= LPTIM_ICR_ARRMCF; // 清除标志
// 从环形缓冲区读取数据(示例:简单打印)
while (rx_index != (RX_BUF_SIZE - DMA1_Stream5->NDTR)) {
uint8_t data = rx_buf[rx_index++];
// 处理数据,如通过另一串口发送或存储
if (rx_index >= RX_BUF_SIZE) rx_index = 0;
}
}
}
```
# 完整代码示例(整合)
```c
// main.c 核心部分
int main(void) {
HAL_Init();
SystemClock_Config(); // 正常模式时钟
GPIO_Init(); // 配置 LED 等
UART_DMA_Init();
LPTIM_Init(100); // 100 µs 定时
while (1) {
// 主循环处理其他任务
// 当需要进入低功耗时:
EnterStopMode();
// 唤醒后继续
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(100);
}
}
```
# 注意事项与调试陷阱
- **时钟校准**:LSI 精度较差(±5%),若需精确微秒级,建议使用 LSE 或增加校准逻辑。
- **中断优先级**:LPTIM 中断优先级必须高于 `__disable_irq()` 的屏蔽级别(如高于 `BASEPRI`),否则无法唤醒。
- **DMA 缓冲区大小**:确保环形缓冲区足够大,避免在 STOP 期间溢出。
- **唤醒后时钟恢复**:STOP 模式会关闭 HSI/HSE,唤醒后必须重新配置系统时钟,否则外设工作异常。
- **调试技巧**:使用逻辑分析仪观察 UART 波形,确认数据无丢失;在 LPTIM 中断中置 GPIO 翻转,测量实际唤醒时间。
# 总结
通过 LPTIM 与 DMA 的巧妙结合,STM32F4 在 STOP 模式下实现了微秒级定时唤醒,同时保证了串口数据完整性。此方案适用于需要周期性采集且对功耗敏感的 IoT 节点。掌握这一技术,你的嵌入式设计将更上一层楼。