【串口技术系列文档 10】低功耗与唤醒机制

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天

优化技巧总结

  1. 选择合适的低功耗模式:Stop2平衡功耗和唤醒速度
  2. 使用LPUART:支持LSE时钟,超低功耗
  3. DMA+IDLE中断:无需CPU参与,减少唤醒次数
  4. 预分配缓冲区:避免动态内存分配
  5. 关闭不必要外设:降低整体功耗

延伸阅读:\[09-FIFO管理与流控]、\[11-调试串口最佳实践]

上一篇 :\[09-FIFO管理与流控]

下一篇:\[11-调试串口最佳实践]


✨ 感谢你的阅读 ✨

技术之路漫漫,分享是为了更好地交流。

如果本文的内容对你有启发,希望能得到你的支持:

👍 点赞 | ⭐ 收藏 | 👀 关注

如果你在调试过程中遇到了其他问题,

欢迎在 评论区 💬 留言,我们一起探讨。

关注我,一起交流底层开发的那些事儿!🚀


相关推荐
逐光者9332 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32
yuehuaxin012 小时前
上海贝岭 BL6523GX(D78) 单相计量芯片开发笔记:引脚定义、寄存器配置与校表流程
嵌入式硬件·智能家居·适配器模式·智能硬件
Kuakewei8883 小时前
可调OVP/OCP USB限流保护芯片IC的50μs过流响应测试
嵌入式硬件
HRTOS3 小时前
HRTOS 官网内容调整记录:首页、系统架构与文档页面优化
单片机·系统架构·51单片机
雨田言炎3 小时前
STM32之SysTick--系统定时器(内容简单但重要,附件有程序源码)
stm32·单片机·嵌入式硬件
ccbw164 小时前
CH32M007K8U7:集成48V三相预驱的RISC-V电机MCU
单片机·嵌入式硬件
蓝天居士5 小时前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(5)
mcu·嵌入式
编程圈子5 小时前
I.MX6U 裸机开发 24. 电容触摸屏与 I2C 驱动
单片机·嵌入式硬件
Zwawa6 小时前
传感器模块都在交出什么:从模拟电压到数字协议
c语言·单片机·嵌入式