一、为什么需要DMA+空闲中断+缓冲区翻转?
在嵌入式开发中,串口接收不定长数据是常见需求。传统轮询或单字节中断方式占用CPU资源且效率低。DMA+空闲中断方案能实现零拷贝、低负载接收,而缓冲区翻转(双缓冲)进一步解决数据处理与接收的并发冲突,避免数据覆盖。
- DMA:自动搬运数据,释放CPU。
- 空闲中断:检测总线空闲,标志一帧数据结束。
- 缓冲区翻转:双缓冲交替使用,实现“接收-处理”流水线。
二、原理详解
2.1 空闲中断触发机制
串口空闲中断(IDLE)在检测到总线空闲(一个字节时间无新数据)时触发。结合DMA传输完成中断,可精准判断帧结束。
2.2 缓冲区翻转(双缓冲)设计
定义两个缓冲区buf0和buf1,DMA当前写入buf0,当空闲中断发生时,切换DMA目标到buf1,同时通知应用处理buf0。处理完成后,再切回buf0,形成乒乓操作。
- 优点:接收与处理并行,避免数据覆盖。
- 关键:切换DMA目标需重新配置DMA通道(HAL库中需先停止DMA再启动)。
三、CubeMX配置步骤
- 使能USART1,模式为Asynchronous。
- 开启USART1全局中断(NVIC中使能USART1中断)。
- 添加DMA通道:USART1_RX,模式Circular(循环模式),数据宽度Byte。
- 生成代码。
四、代码实现
4.1 定义缓冲区与状态
#define BUF_SIZE 128
uint8_t buf0[BUF_SIZE], buf1[BUF_SIZE];
uint8_t *active_buf = buf0; // DMA当前写入的缓冲区
uint8_t *ready_buf = NULL; // 待处理的缓冲区
volatile uint8_t data_ready = 0;
4.2 启动DMA接收
void Start_Receive(void) {
HAL_UART_Receive_DMA(&huart1, active_buf, BUF_SIZE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
}
4.3 空闲中断处理(在stm32f1xx_it.c中)
void USART1_IRQHandler(void) {
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
// 停止DMA以获取已接收长度
HAL_UART_DMAStop(&huart1);
uint16_t len = BUF_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
if (len > 0) {
ready_buf = active_buf;
// 切换缓冲区
active_buf = (active_buf == buf0) ? buf1 : buf0;
data_ready = 1;
}
// 重新启动DMA接收
HAL_UART_Receive_DMA(&huart1, active_buf, BUF_SIZE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
}
HAL_UART_IRQHandler(&huart1);
}
4.4 主循环处理数据
while (1) {
if (data_ready) {
// 处理ready_buf中的数据,长度需另行记录(见注意事项)
ProcessData(ready_buf, data_len);
data_ready = 0;
}
}
五、关键踩坑与注意事项
-
长度获取问题:上述代码中
len在切换缓冲区后丢失,需用全局变量保存每次接收的长度。建议定义volatile uint16_t recv_len;在中断中赋值。 -
DMA停止与重启的时序:
HAL_UART_DMAStop会关闭DMA和串口接收,必须重新调用HAL_UART_Receive_DMA启动,否则无法接收下一帧。 - 中断优先级:确保USART中断优先级高于DMA中断(若使用DMA中断),避免竞争。
- 缓冲区大小与溢出:若一帧数据超过BUF_SIZE,DMA循环模式会覆盖,需根据实际帧长调整或使用流控。
-
内存对齐:DMA缓冲区建议使用
__attribute__((aligned(4)))确保对齐,提升效率。 -
空闲中断标志清除:必须使用
__HAL_UART_CLEAR_IDLEFLAG清除,否则会反复进入中断。 -
HAL库版本差异:不同系列(F1/F4/H7)的DMA计数器获取方式略有不同,注意
__HAL_DMA_GET_COUNTER的宏定义。
六、总结
DMA+空闲中断+缓冲区翻转是STM32串口接收不定长数据的高效方案。掌握其原理与实现细节,能显著提升系统实时性与可靠性。重点在于正确管理缓冲区切换、长度记录及DMA重启时序,避开上述陷阱即可稳定运行。
暂无评论,来抢沙发~