STM32 UART空闲中断DMA接收仅获首字节问题求助
STM32F407ZGT6 UART6空闲中断+DMA接收SBUS异常排查与修复
问题描述
使用STM32F407ZGT6的UART6通过空闲中断+DMA模式接收SBUS信号时出现异常,KEIL调试发现:
- DMA仅完成一次接收,首次全速运行时仅收到首字节;
- 持续运行时接收缓冲区数据不更新,仅在暂停程序时能看到完整25字节数据。
相同配置在UART4搭配串口助手测试时无异常。
代码问题分析
- 空闲中断标志清除不彻底:HAL库的
__HAL_UART_CLEAR_IDLEFLAG宏无法保证在所有场景下正确清除标志,必须手动执行读SR+读DR的操作才能彻底清除。 - DMA模式选择错误:配置为DMA循环模式(
DMA_CIRCULAR),但空闲中断逻辑是手动停止并重启DMA,两者逻辑冲突,导致传输异常。 - 硬编码串口句柄:中断处理函数中直接使用全局
huart6而非函数参数huart,破坏代码通用性且可能导致逻辑错误。 - 无效的长度计算:计算接收长度后立刻赋值为0,完全丢失了帧长度信息,无法正确处理SBUS帧。
- GPIO配置不合理:RX引脚未配置上拉,SBUS信号低电平有效,无信号时引脚浮空可能导致误触发;无需配置TX引脚为复用推挽(SBUS仅接收)。
修复方案
- 手动清除空闲中断标志:在中断中先读取USART_SR寄存器,再读取USART_DR寄存器,彻底清除IDLE标志。
- 修改DMA模式为正常模式:SBUS为固定25字节帧,使用
DMA_NORMAL模式配合空闲中断重启DMA,更符合帧接收逻辑。 - 使用函数参数的串口句柄:所有操作基于传入的
huart参数,避免硬编码。 - 恢复长度计算逻辑:删除
data_length = 0;的错误代码,保留长度计算用于后续SBUS解析。 - 修正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
相关产品推荐
相关产品推荐

