UART在低功耗模式下的行为
MCU低功耗模式分类
| 模式 | 时钟状态 | UART行为 | 唤醒能力 |
|---|---|---|---|
| Sleep | CPU停止,外设运行 | 正常收发 | 任意中断 |
| Stop | 高速时钟关闭 | 停止工作 | 特定引脚/RTC |
| Standby | 仅低速时钟 | 完全关闭 | WKUP引脚 |
| Shutdown | 全部关闭 | 无功能 | 复位/外部事件 |
功耗对比
c
// STM32L4系列实测数据(3.3V)
// Run模式:12 mA
// Sleep模式:1.5 mA
// Stop2模式:1.2 μA(UART唤醒使能)
// Standby模式:0.4 μA
IDLE中断检测(帧结束)
原理
IDLE检测:总线空闲一个字符时间(约10-11位)后触发中断
c
void uart_idle_init(void) {
// 使能IDLE中断
UART->CR1 |= USART_CR1_IDLEIE;
// 清除IDLE标志(读SR+读DR)
volatile uint32_t dummy;
dummy = UART->SR;
dummy = UART->DR;
}
// IDLE中断处理
void UART_IRQHandler(void) {
if (UART->SR & USART_SR_IDLE) {
// 清除IDLE标志
volatile uint32_t dummy = UART->SR;
dummy = UART->DR;
// 处理完整帧数据
uint16_t len = DMA_BUFFER_SIZE - DMA_CH->CNDTR;
process_frame(rx_buffer, len);
// 重启DMA
DMA_CH->CCR &= ~DMA_CCR_EN;
DMA_CH->CNDTR = DMA_BUFFER_SIZE;
DMA_CH->CCR |= DMA_CCR_EN;
}
}
应用场景:
- 变长协议包接收(ModBus、自定义协议)
- GPS NMEA语句分隔
- AT命令结束检测
RX引脚唤醒
配置方法
c
// STM32 LPUART配置(低功耗UART)
void lpuart_wakeup_init(void) {
// 1. 配置LPUART时钟源(LSE/HSI)
RCC->CCIPR |= RCC_CCIPR_LPUART1SEL_0; // LSE 32.768kHz
// 2. 使能LPUART
LPUART->CR1 = USART_CR1_UE | USART_CR1_RE;
// 3. 配置唤醒方法
LPUART->CR3 |= USART_CR3_WUS_1; // Start bit唤醒
// 选项:
// - USART_CR3_WUS_1: Start bit
// - USART_CR3_WUS_0: Address match
// - 0x3: RXNE标志
// 4. 使能唤醒中断
LPUART->CR3 |= USART_CR3_WUFIE;
LPUART->CR1 |= USART_CR1_UESM; // UART进入Stop模式
// 5. 配置NVIC
NVIC_EnableIRQ(LPUART1_IRQn);
}
// 进入Stop模式
void enter_stop_mode(void) {
// 配置唤醒源
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
// 进入Stop2模式
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
// 唤醒后从这里继续执行
SystemClock_Config(); // 恢复时钟
}
// 唤醒中断处理
void LPUART1_IRQHandler(void) {
if (LPUART->ISR & USART_ISR_WUF) {
LPUART->ICR = USART_ICR_WUCF; // 清除唤醒标志
// 处理唤醒后的数据接收
wakeup_callback();
}
}
LIN Break唤醒
原理
LIN Break:至少13位低电平(约1.5-2个字符时间)
正常数据: START 01010101 STOP START ...
LIN Break: ___________________________ START ...
|<------ ≥13 bit ------->|
实现代码
c
// 发送LIN Break
void uart_send_lin_break(void) {
UART->CR1 |= USART_CR1_SBK; // 发送Break
while (!(UART->SR & USART_SR_TC));
}
// 检测LIN Break
void uart_lin_break_detect_init(void) {
UART->CR2 |= USART_CR2_LBDIE; // 使能LIN Break检测中断
UART->CR2 |= USART_CR2_LINEN; // 使能LIN模式
}
void UART_IRQHandler(void) {
if (UART->SR & USART_SR_LBD) {
UART->SR &= ~USART_SR_LBD; // 清除标志
// 唤醒MCU
__WFI(); // 退出低功耗
lin_break_callback();
}
}
应用:
- LIN总线从节点唤醒
- 长时间空闲后的重同步
9-bit地址匹配唤醒
工作原理
多机通信:第9位为地址标识位
- 第9位=1:地址字节
- 第9位=0:数据字节
从机仅响应匹配地址的数据包。
c
#define NODE_ADDRESS 0x15
void uart_address_match_init(void) {
// 配置9位数据模式
UART->CR1 |= USART_CR1_M0; // 9-bit模式
// 配置地址
UART->CR2 &= ~USART_CR2_ADD;
UART->CR2 |= (NODE_ADDRESS << USART_CR2_ADD_Pos);
// 使能地址匹配唤醒
UART->CR3 |= USART_CR3_WUS_0; // Address match
UART->CR1 |= USART_CR1_MME; // Mute mode enable
// 进入静默模式(仅监听地址)
UART->RQR |= USART_RQR_MMRQ;
}
// 发送地址+数据
void uart_send_addressed_data(uint8_t addr, const uint8_t *data, uint16_t len) {
// 发送地址(第9位=1)
UART->TDR = (1 << 8) | addr;
while (!(UART->ISR & USART_ISR_TC));
// 发送数据(第9位=0)
for (uint16_t i = 0; i < len; i++) {
UART->TDR = data[i];
while (!(UART->ISR & USART_ISR_TC));
}
}
// 地址匹配中断
void UART_IRQHandler(void) {
if (UART->ISR & USART_ISR_CMF) { // Character match
UART->ICR = USART_ICR_CMCF;
// 退出静默模式,接收后续数据
UART->RQR |= USART_RQR_MMRQ;
address_matched = true;
}
}
唤醒延迟与时钟恢复
时序分析
Wake Event 时钟恢复 UART就绪 开始接收
| | | |
|<-- 5μs -->|<-- 2μs --->|<-- 1μs -->|
| |
|<---------- 总延迟 ≈ 8μs ---------->|
时钟恢复策略
c
// 快速时钟恢复配置
void fast_wakeup_config(void) {
// 1. 使能HSI作为唤醒时钟源(更快)
RCC->CR |= RCC_CR_HSION;
while (!(RCC->CR & RCC_CR_HSIRDY));
// 2. 配置Flash预取(减少延迟)
FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
// 3. LPUART使用低速时钟(无需等待HSI)
RCC->CCIPR |= RCC_CCIPR_LPUART1SEL_LSE;
}
// 测量唤醒延迟
void measure_wakeup_latency(void) {
GPIO_SetBits(TEST_PIN);
__WFI();
GPIO_ResetBits(TEST_PIN); // 用示波器测量脉宽
}
低功耗模式下的波特率
时钟源选择
c
// STM32 LPUART波特率计算
// BR = (256 * f_clk) / baudrate
// LSE时钟(32.768kHz)支持的波特率
LSE_32768Hz → 9600 bps (误差0.16%)
LSE_32768Hz → 4800 bps (误差0.16%)
LSE_32768Hz → 2400 bps (误差0.16%)
// 配置示例
#define LSE_FREQ 32768
#define BAUDRATE 9600
void lpuart_set_baudrate(void) {
uint32_t brr = (256 * LSE_FREQ) / BAUDRATE;
LPUART->BRR = brr; // 0x354 (9600 bps)
}
限制:
- LSE时钟下波特率≤9600 bps
- 高波特率需要HSI/HSE(功耗增加)
实际案例:GPS模块间歇接收
需求:
- GPS模块每5秒发送一次定位数据(NMEA语句)
- 其余时间MCU进入Stop模式
- 功耗<10 μA
方案设计:
c
#define GPS_BAUDRATE 9600
#define GPS_RX_BUF_SIZE 256
typedef struct {
uint8_t rx_buffer[GPS_RX_BUF_SIZE];
uint16_t rx_len;
bool data_ready;
} gps_context_t;
gps_context_t gps;
// GPS初始化
void gps_low_power_init(void) {
// 1. 配置LPUART(LSE时钟)
lpuart_wakeup_init();
lpuart_set_baudrate();
// 2. 配置DMA+IDLE中断
dma_circular_init(gps.rx_buffer, GPS_RX_BUF_SIZE);
uart_idle_init();
// 3. 配置RTC唤醒(5秒周期)
rtc_wakeup_config(5000);
}
// 主循环
void gps_task(void) {
while (1) {
// 使能GPS电源
GPS_PWR_ON();
HAL_Delay(100); // 等待GPS启动
// 等待数据接收(最多2秒)
uint32_t timeout = HAL_GetTick() + 2000;
gps.data_ready = false;
while (!gps.data_ready && HAL_GetTick() < timeout) {
enter_stop_mode(); // 被UART唤醒
}
// 处理GPS数据
if (gps.data_ready) {
parse_nmea(gps.rx_buffer, gps.rx_len);
}
// 关闭GPS
GPS_PWR_OFF();
// 进入Stop模式(等待RTC唤醒)
enter_stop_mode();
}
}
// IDLE中断处理
void LPUART_IRQHandler(void) {
if (LPUART->ISR & USART_ISR_IDLE) {
LPUART->ICR = USART_ICR_IDLECF;
// 获取接收长度
gps.rx_len = GPS_RX_BUF_SIZE - DMA_CH->CNDTR;
gps.data_ready = true;
}
}
功耗测试:
- 工作模式(接收GPS):12 mA × 2s = 24 mA·s
- Stop模式:5 μA × 3s = 15 μA·s
- 平均功耗:(24000 + 15) / 5 ≈ 4.8 mA
- 使用2000mAh电池:续航约17天
优化技巧总结
- 选择合适的低功耗模式:Stop2平衡功耗和唤醒速度
- 使用LPUART:支持LSE时钟,超低功耗
- DMA+IDLE中断:无需CPU参与,减少唤醒次数
- 预分配缓冲区:避免动态内存分配
- 关闭不必要外设:降低整体功耗
延伸阅读:\[09-FIFO管理与流控]、\[11-调试串口最佳实践]
上一篇 :\[09-FIFO管理与流控]
下一篇:\[11-调试串口最佳实践]
✨ 感谢你的阅读 ✨
技术之路漫漫,分享是为了更好地交流。
如果本文的内容对你有启发,希望能得到你的支持:
👍 点赞 | ⭐ 收藏 | 👀 关注
如果你在调试过程中遇到了其他问题,
欢迎在 评论区 💬 留言,我们一起探讨。
关注我,一起交流底层开发的那些事儿!🚀