You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HAL_UART_TRANSMIT_DMA触发RX回调后无法发送的问题排查

解决UART循环DMA接收后无法正常DMA发送ACK的问题

看起来你遇到的问题核心是UART发送DMA通道的资源冲突,以及初始发送请求的不合理性,咱们一步步拆解解决:

问题根源分析

  1. 初始发送抢占通道:你在main()里一开始就调用了HAL_UART_Transmit_DMA,这个发送请求会占用UART的发送DMA通道,当循环接收的回调触发时,发送通道可能还处于BUSY状态,导致新的ACK发送请求被HAL库拒绝,所以串口没有输出,状态显示忙。
  2. 循环发送模式的特性:当你把TX改成循环DMA模式时,DMA会持续重复发送缓冲区内容,这是循环模式的设计逻辑,自然会导致终端被持续数据淹没。

分步解决方案

1. 移除不必要的初始发送请求

你的需求是接收完设置字符串后才发送ACK,所以main()里的初始HAL_UART_Transmit_DMA完全多余,直接删掉:

int main(void) {
  // ... 其他初始化代码(时钟、GPIO、DMA等) ...
  MX_USART2_UART_Init();
  
  // 只启动循环DMA接收,无需初始发送
  HAL_UART_Receive_DMA(&huart2, rx_buff, INCOMING_SETTING_STRING_SIZE);
  
  while (1) {
    // ... 主循环逻辑 ...
  }
}

2. 优化接收回调函数,确保发送通道空闲

在HAL_UART_RxCpltCallback里,发送ACK前先检查发送通道的状态,避免在BUSY时发起新请求;同时添加返回值检查,方便调试:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  if (huart->Instance != USART2) return; // 只处理USART2的回调

  ProcessSerialSettings(); // 处理设置更新
  
  // 等待发送通道空闲(可添加超时逻辑避免死等)
  uint32_t timeout = HAL_GetTick() + 100; // 100ms超时
  while ((HAL_UART_GetState(&huart2) & HAL_UART_STATE_BUSY_TX) && (HAL_GetTick() < timeout)) {
    // 空等,或者添加看门狗喂狗逻辑
  }
  
  // 发送ACK并检查结果
  if (HAL_UART_Transmit_DMA(&huart2, (uint8_t *)"ACK\n", 5) != HAL_OK) {
    // 这里可以添加错误处理,比如LED闪烁、日志输出
  }
  
  memset(rx_buff, 0, sizeof(rx_buff));
  
  // 注意:循环模式DMA接收会自动重启,无需再次调用HAL_UART_Receive_DMA
}

3. 确认DMA通道配置(关键)

确保你的DMA配置符合需求:

  • RX通道:配置为DMA_CIRCULAR(循环模式),这样每次接收满缓冲区后,DMA会自动重新开始接收,无需在回调里手动重启。
  • TX通道:保持DMA_NORMAL(正常模式),发送完成后DMA自动停止,不会重复发送。

你可以在自动生成的MX_DMA_Init()函数里检查这个配置,比如:

static void MX_DMA_Init(void) {
  // ... 其他DMA配置 ...
  hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; // 接收循环模式
  hdma_usart2_tx.Init.Mode = DMA_NORMAL;   // 发送正常模式
  // ...
}

4. 可选:添加发送完成回调用于调试

如果需要确认发送是否完成,可以实现HAL_UART_TxCpltCallback:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
  if (huart->Instance == USART2) {
    // 发送完成的标记或日志,比如:
    // tx_ack_completed = 1;
  }
}

额外注意事项

  • 避免在回调里执行耗时操作:如果ProcessSerialSettings()逻辑复杂、耗时久,建议把它放到主循环里,用一个标志位(比如settings_updated_flag)触发,避免阻塞UART的中断回调。
  • 缓冲区大小匹配:确保rx_buff的实际大小等于INCOMING_SETTING_STRING_SIZE,防止DMA溢出。

内容的提问来源于stack exchange,提问作者charley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 12:12:38