一、原理讲解

在STM32中,使用DMA进行串口接收可以大幅降低CPU负载。HAL库提供了HAL_UARTEx_ReceiveToIdle_DMA函数,支持空闲中断(IDLE)和DMA半满/全满中断。

  • 半满中断(HT):当DMA接收缓冲区填充到一半时触发,适合处理大数据流的分段处理。
  • 空闲中断(IDLE):当串口总线空闲(一个字节时间无新数据)时触发,标志一帧数据结束。
  • 协同工作:半满中断用于及时处理前半段数据,空闲中断用于处理剩余数据并重置接收,两者结合可避免数据覆盖和丢包。

常见丢包原因:

  • 中断优先级配置不当,导致IDLE中断被延迟响应。
  • DMA缓冲区太小或处理不及时,新数据覆盖旧数据。
  • 未及时重启DMA接收,导致后续数据丢失。
  • 半满中断与空闲中断处理逻辑冲突(如重复重启DMA)。

二、配置步骤

  1. CubeMX配置:
    • 启用USART1,模式为Asynchronous。
    • 在DMA Settings中添加USART1_RX,模式为Circular(循环模式),数据宽度Byte。
    • 使能USART1全局中断和DMA通道中断。
  2. 代码初始化:
    • 调用HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, BUF_SIZE)启动接收。
    • 默认会启用半满中断,如需禁用可调用__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT)。
  3. 中断处理:
    • 重写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计数器排查。
  • 避免在回调中长时间阻塞:数据处理应尽快完成或转移至任务中。

通过合理配置半满与空闲中断,并注意上述要点,可显著提升串口接收的稳定性和效率。