STM32 UART + DMA + 空闲中断使用中的帧错误(FE)问题及解决方案

STM32 UART + DMA + IDLE中断使用中的帧错误(FE)问题及解决方案

在我调试STM32H7串口空闲中断DMA接受时遇到了一个bug,这个现象发生在系统刚上电时,有个串口由于帧错误FE挂起了中断,之后在HAL_UART_IRQHandler这个全局中断处理函数结束后,所有的中断使能标志位都被清除了,经过反复调试发现以下问题:

  1. 上电初始化后,串口的 帧错误标志(FE) 会被意外触发,导致系统进入中断。
  2. 在中断处理中,由于 EIE(错误中断使能位) 被清除,导致帧错误的处理没有进入预期的 HAL_UART_ErrorCallback 函数。
  3. DMA 被停止时,空闲中断功能(IDLEIE)也被清除,后续数据接收无法正常触发空闲中断。

这是一个隐藏较深的问题,目前尚无直接的参考案例。

问题分析

通过对 HAL 库源码和中断流程的分析,发现问题的根源如下:

  1. 帧错误(FE)触发中断:

    • 帧错误通常发生在上电阶段,可能是由于串口初始化前的干扰或接收了无效数据。

    • 帧错误会触发中断,但由于 HAL 中的错误处理逻辑,UART_EndRxTransfer 会清除 EIEIDLEIE,导致后续的错误无法触发中断。

      HAL_UART_IRQHandler中的UART_EndRxTransfer函数导致所有中断使能标志位被清除

      image-20241125165406636

      进入这个函数的原因是因为我是用DMA来传输数据,如果这时候发生了任何错误,都会进入这个函数,看看这个函数原型

      /**
        * @brief  End ongoing Rx transfer on UART peripheral (following error detection or Reception completion).
        * @param  huart UART handle.
        * @retval None
        */
      static void UART_EndRxTransfer(UART_HandleTypeDef *huart)
      {
             
        /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */
        ATOMIC_CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE));
        ATOMIC_CLEAR_BIT(huart->Instance->CR3, (USART_CR3_EIE 
使用STM32HAL库时,可以通过以下步骤实现串口DMA空闲中断: 1. 配置串口DMA传输,使其使用循环模式和使用空闲中断。 ```c /* Configure the DMA handler for Transmission process */ hdma_tx.Instance = USARTx_TX_DMA_STREAM; hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL; hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx.Init.Mode = DMA_NORMAL; hdma_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx.Init.MemBurst = DMA_MBURST_INC4; hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_tx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx); /* Configure the DMA handler for reception process */ hdma_rx.Instance = USARTx_RX_DMA_STREAM; hdma_rx.Init.Channel = USARTx_RX_DMA_CHANNEL; hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_rx.Init.Mode = DMA_CIRCULAR; hdma_rx.Init.Priority = DMA_PRIORITY_HIGH; hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_rx.Init.MemBurst = DMA_MBURST_INC4; hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_rx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmarx, hdma_rx); /* Configure the DMA handler callbacks */ HAL_DMA_RegisterCallback(&hdma_tx, HAL_DMA_XFER_CPLT_CB_ID, DMA_TxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_CPLT_CB_ID, DMA_RxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_ERROR_CB_ID, DMA_RxErrorCallback); /* Configure DMA transfer interrupts */ __HAL_DMA_ENABLE_IT(&hdma_rx, DMA_IT_TC); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_HT); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_FE); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_TE); ``` 2. 实现DMA传输完成回调函数。 ```c void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { /* Transmission complete callback */ } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Reception complete callback */ } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { /* UART error callback */ } ``` 3. 实现DMA空闲中断回调函数。 ```c void HAL_UART_IDLE_IRQHandler(UART_HandleTypeDef *huart) { /* Disable DMA transfer on reception */ hdma_rx.Instance->CR &= ~DMA_SxCR_EN; /* Clear IDLE flag */ __HAL_UART_CLEAR_IDLEFLAG(huart); /* Call user callback */ HAL_UART_RxCpltCallback(huart); /* Enable DMA transfer on reception */ hdma_rx.Instance->CR |= DMA_SxCR_EN; } ``` 4. 配置空闲中断使能。 ```c /* Enable the UART Idle Interrupt */ __HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE); ``` 通过以上步骤,就可以实现串口DMA空闲中断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值