一、为什么需要低功耗串口接收?
在电池供电的嵌入式设备中,MCU大部分时间应处于低功耗模式。但串口接收是异步事件,传统轮询或每字节中断会频繁唤醒CPU,增加功耗。理想方案是:无数据时MCU进入STOP模式,串口收到数据时自动唤醒,并用DMA搬运数据,空闲中断判断一帧结束。这样CPU仅在完整帧到达后处理一次,极大降低功耗。
二、原理讲解
2.1 STM32低功耗模式
- Sleep:仅关闭内核时钟,外设仍运行,唤醒快但功耗较高。
- STOP:关闭所有时钟(除LSI/LSE和唤醒源),保留SRAM和寄存器,功耗约几μA~几十μA。
- Standby:功耗最低,但SRAM丢失,唤醒相当于复位。
本方案选用STOP模式,因为需要保留串口配置和DMA状态。
2.2 串口空闲中断(IDLE)
串口在接收完一帧数据后,总线会保持空闲(高电平)一个字节时间,此时触发IDLE中断。利用它可判断“一帧结束”,无需定时器。
2.3 DMA接收
DMA将串口数据自动搬运到内存缓冲区,不占用CPU。配合IDLE中断,可在中断中计算接收长度并处理。
2.4 STOP模式唤醒源
STM32的USART支持在STOP模式下通过RX引脚边沿唤醒(需配置USART时钟源为HSI或LSE,并使能唤醒中断)。当起始位到来时,USART产生唤醒事件,MCU退出STOP,随后DMA继续搬运数据。
三、CubeMX配置步骤
以STM32L4系列为例(其他系列类似):
- 时钟配置:使能HSI16作为USART时钟源(STOP模式下HSI可保持)。
-
USART配置:
- 模式:Asynchronous
- 波特率:115200
- 开启USART全局中断(NVIC中使能USARTx中断)
- 使能DMA接收:添加USARTx_RX的DMA通道,模式为Normal(或Circular,视需求)
- NVIC:使能USARTx中断,并设置合适优先级。
- 低功耗:无需额外配置,代码中调用HAL_PWR_EnterSTOPMode。
四、完整代码示例
4.1 变量定义
#define RX_BUFFER_SIZE 128
uint8_t rx_dma_buffer[RX_BUFFER_SIZE];
volatile uint8_t rx_frame_ready = 0;
volatile uint16_t rx_frame_len = 0;
4.2 初始化
void USART_Init(void)
{
// 启动DMA接收
HAL_UART_Receive_DMA(&huart1, rx_dma_buffer, RX_BUFFER_SIZE);
// 使能空闲中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
}
4.3 空闲中断处理
在stm32l4xx_it.c的USART1_IRQHandler中:
void USART1_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
// 停止DMA以获取已接收长度
HAL_UART_DMAStop(&huart1);
rx_frame_len = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
rx_frame_ready = 1;
// 重新启动DMA接收
HAL_UART_Receive_DMA(&huart1, rx_dma_buffer, RX_BUFFER_SIZE);
}
HAL_UART_IRQHandler(&huart1);
}
4.4 主循环与STOP模式
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USART1_UART_Init();
USART_Init();
while (1)
{
if (rx_frame_ready)
{
// 处理接收到的数据
process_frame(rx_dma_buffer, rx_frame_len);
rx_frame_ready = 0;
}
else
{
// 进入STOP模式,等待串口唤醒
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后需重新配置系统时钟(STOP模式会关闭HSI以外时钟)
SystemClock_Config();
}
}
}
4.5 唤醒配置
在HAL_UART_MspInit中确保USART时钟源为HSI,并调用:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_WUF); // 使能唤醒中断
HAL_UARTEx_EnableStopMode(&huart1); // 允许STOP模式唤醒
五、注意事项
-
时钟恢复:STOP模式唤醒后,系统时钟可能切换为HSI,需重新调用
SystemClock_Config()恢复原时钟。 - DMA重启:每次空闲中断后需重新启动DMA,否则无法接收下一帧。
- 缓冲区溢出:若一帧数据超过缓冲区大小,DMA会覆盖,建议使用循环模式或加大缓冲区。
- 唤醒延迟:STOP唤醒需要几个时钟周期,波特率较高时可能丢失第一个字节,可适当降低波特率或使用硬件流控。
- 中断优先级:USART中断优先级应高于DMA,避免竞争。
- 功耗测量:实际功耗受外设漏电流影响,建议关闭未使用外设时钟。
六、总结
本文方案通过DMA+空闲中断+STOP模式,实现了低功耗串口接收。核心在于利用串口唤醒事件退出STOP,DMA自动搬运数据,空闲中断判断帧尾。代码简洁,适合电池供电的传感器节点、无线模块等场景。开发者可根据具体型号调整时钟和唤醒配置,即可获得μA级待机功耗与可靠接收。