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

STM32 UART空闲中断DMA接收仅获首字节问题求助

STM32F407ZGT6 UART6空闲中断+DMA接收SBUS异常排查与修复

问题描述

使用STM32F407ZGT6的UART6通过空闲中断+DMA模式接收SBUS信号时出现异常,KEIL调试发现:

  • DMA仅完成一次接收,首次全速运行时仅收到首字节;
  • 持续运行时接收缓冲区数据不更新,仅在暂停程序时能看到完整25字节数据。
    相同配置在UART4搭配串口助手测试时无异常。

代码问题分析

  1. 空闲中断标志清除不彻底:HAL库的__HAL_UART_CLEAR_IDLEFLAG宏无法保证在所有场景下正确清除标志,必须手动执行读SR+读DR的操作才能彻底清除。
  2. DMA模式选择错误:配置为DMA循环模式(DMA_CIRCULAR),但空闲中断逻辑是手动停止并重启DMA,两者逻辑冲突,导致传输异常。
  3. 硬编码串口句柄:中断处理函数中直接使用全局huart6而非函数参数huart,破坏代码通用性且可能导致逻辑错误。
  4. 无效的长度计算:计算接收长度后立刻赋值为0,完全丢失了帧长度信息,无法正确处理SBUS帧。
  5. GPIO配置不合理:RX引脚未配置上拉,SBUS信号低电平有效,无信号时引脚浮空可能导致误触发;无需配置TX引脚为复用推挽(SBUS仅接收)。

修复方案

  1. 手动清除空闲中断标志:在中断中先读取USART_SR寄存器,再读取USART_DR寄存器,彻底清除IDLE标志。
  2. 修改DMA模式为正常模式:SBUS为固定25字节帧,使用DMA_NORMAL模式配合空闲中断重启DMA,更符合帧接收逻辑。
  3. 使用函数参数的串口句柄:所有操作基于传入的huart参数,避免硬编码。
  4. 恢复长度计算逻辑:删除data_length = 0;的错误代码,保留长度计算用于后续SBUS解析。
  5. 修正GPIO配置:RX引脚配置为上拉,TX引脚若未使用可配置为浮空或禁用。

修正后代码示例

main.c

.....
/* USER CODE BEGIN PV */
uint8_t receive_buff[25]; 
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART6_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart6, (uint8_t*)receive_buff, 25);
  __HAL_UART_ENABLE_IT(&huart6, UART_IT_IDLE);
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN artWHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
.....

uart.c

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */
extern uint8_t receive_buff[25]; 
/* USER CODE END 0 */

UART_HandleTypeDef huart6;
DMA_HandleTypeDef hdma_usart6_rx;

/* USART6 init function */

void MX_USART6_UART_Init(void)
{

  /* USER CODE BEGIN USART6_Init 0 */

  /* USER CODE END USART6_Init 0 */

  /* USER CODE BEGIN USART6_Init 1 */

  /* USER CODE END USART6_Init 1 */
  huart6.Instance = USART6;
  huart6.Init.BaudRate = 100000;
  huart6.Init.WordLength = UART_WORDLENGTH_8B;
  huart6.Init.StopBits = UART_STOPBITS_2;
  huart6.Init.Parity = UART_PARITY_EVEN;
  huart6.Init.Mode = UART_MODE_TX_RX;
  huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart6.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart6) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART6_Init 2 */
  //HAL_UART_Receive_DMA(&huart6, (uint8_t*)receive_buff, 25);
  /* USER CODE END USART6_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART6)
  {
  /* USER CODE BEGIN USART6_MspInit 0 */

  /* USER CODE END USART6_MspInit 0 */
    /* USART6 clock enable */
    __HAL_RCC_USART6_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**USART6 GPIO Configuration
    PC6     ------> USART6_TX(若未使用可注释或配置为浮空)
    PC7     ------> USART6_RX
    */
    // TX引脚配置(若无需发送可禁用)
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
    // RX引脚配置为上拉输入
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* USART6 DMA Init */
    /* USART6_RX Init */
    hdma_usart6_rx.Instance = DMA2_Stream1;
    hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5;
    hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart6_rx.Init.Mode = DMA_NORMAL; // 修改为正常模式
    hdma_usart6_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart6_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart6_rx);

    /* USART6 interrupt Init */
    HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART6_IRQn);
  /* USER CODE BEGIN USART6_MspInit 1 */

  /* USER CODE END USART6_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART6)
  {
  /* USER CODE BEGIN USART6_MspDeInit 0 */

  /* USER CODE END USART6_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART6_CLK_DISABLE();

    /**USART6 GPIO Configuration
    PC6     ------> USART6_TX
    PC7     ------> USART6_RX
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_6|GPIO_PIN_7);

    /* USART6 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);

    /* USART6 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART6_IRQn);
  /* USER CODE BEGIN USART6_MspDeInit 1 */

  /* USER CODE END USART6_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

stm32f4xx_it.c

.....

void USART6_IRQHandler(void)
{
  /* USER CODE BEGIN USART6_IRQn 0 */
  /* USER CODE END USART6_IRQn 0 */
  HAL_UART_IRQHandler(&huart6);
  /* USER CODE BEGIN USART6_IRQn 1 */
   USER_UART_IRQHandler(&huart6); 
  /* USER CODE END USART6_IRQn 1 */
}

/* USER CODE BEGIN 1 */
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    HAL_UART_DMAStop(huart); // 使用传入的句柄
    
    uint8_t data_length  = 25 - __HAL_DMA_GET_COUNTER(huart->hdmarx); // 正确计算长度
    // 此处可添加SBUS数据解析逻辑,比如处理receive_buff中的25字节数据
    
    HAL_UART_Receive_DMA(huart, (uint8_t*)receive_buff, 25); // 重启DMA接收
}

void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART6) // 正确判断串口实例
    {
        if(RESET != __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
        {
            // 手动清除IDLE标志:先读SR,再读DR
            volatile uint32_t temp;
            temp = huart->Instance->SR;
            temp = huart->Instance->DR;
            
            USAR_UART_IDLECallback(huart);
        }
    }
}

/* USER CODE END 1 */
......

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:44:57