一、原理讲解
在STM32中,使用DMA进行串口接收可以大幅降低CPU负载。HAL库提供了HAL_UARTEx_ReceiveToIdle_DMA函数,支持空闲中断(IDLE)和DMA半满/全满中断。
- 半满中断(HT):当DMA接收缓冲区填充到一半时触发,适合处理大数据流的分段处理。
- 空闲中断(IDLE):当串口总线空闲(一个字节时间无新数据)时触发,标志一帧数据结束。
- 协同工作:半满中断用于及时处理前半段数据,空闲中断用于处理剩余数据并重置接收,两者结合可避免数据覆盖和丢包。
常见丢包原因:
- 中断优先级配置不当,导致IDLE中断被延迟响应。
- DMA缓冲区太小或处理不及时,新数据覆盖旧数据。
- 未及时重启DMA接收,导致后续数据丢失。
- 半满中断与空闲中断处理逻辑冲突(如重复重启DMA)。
二、配置步骤
-
CubeMX配置:
- 启用USART1,模式为Asynchronous。
- 在DMA Settings中添加USART1_RX,模式为Circular(循环模式),数据宽度Byte。
- 使能USART1全局中断和DMA通道中断。
-
代码初始化:
- 调用
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, BUF_SIZE)启动接收。 - 默认会启用半满中断,如需禁用可调用
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT)。
- 调用
-
中断处理:
- 重写
HAL_UARTEx_RxEventCallback回调,根据Size参数判断是半满还是空闲事件。
- 重写
三、完整代码示例
#define RX_BUF_SIZE 128
uint8_t rx_buf[RX_BUF_SIZE];
volatile uint16_t old_pos = 0;
/* 启动接收 */
void UART_Start_Receive(void) {
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, RX_BUF_SIZE);
// 默认开启半满中断,若不需要可禁用:
// __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);
}
/* 回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart->Instance == USART1) {
// 计算新数据长度(考虑循环缓冲区)
uint16_t new_data_len;
if (Size >= old_pos) {
new_data_len = Size - old_pos;
} else {
new_data_len = RX_BUF_SIZE - old_pos + Size;
}
// 处理数据(示例:回传)
HAL_UART_Transmit(&huart1, &rx_buf[old_pos], new_data_len, 100);
old_pos = Size;
// 注意:HAL_UARTEx_ReceiveToIdle_DMA 在空闲中断后会自动重启接收,无需手动调用
}
}
/* 错误处理 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
// 发生错误时重新启动接收
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, RX_BUF_SIZE);
old_pos = 0;
}
}
四、注意事项
- 中断优先级:确保USART1中断优先级高于或等于DMA中断,避免IDLE响应延迟。
-
缓冲区管理:使用循环DMA时,
old_pos记录上次处理位置,避免重复处理。 - 半满中断处理:若数据量大,可在半满中断中及时处理前半段,减少空闲中断负担。
-
错误恢复:在
HAL_UART_ErrorCallback中重启DMA接收,防止因溢出错误导致接收停止。 - 调试技巧:使用逻辑分析仪或串口打印统计丢包次数,结合DMA计数器排查。
- 避免在回调中长时间阻塞:数据处理应尽快完成或转移至任务中。
通过合理配置半满与空闲中断,并注意上述要点,可显著提升串口接收的稳定性和效率。
暂无评论,来抢沙发~