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 | USART_CR3_RXFTIE)
### STM32 UART空闲中断的接收机制或实现方法 STM32UART 空闲中断是一种用于检测串口通信中数据结束的有效方式。当接收到的数据流之间存在一段空闲时间时,UART 控制器会触发空闲中断信号。这种特性非常适合于不定长度数据包的接收场景。 #### 1. **空闲中断的工作原理** 在 UART 接收过程中,如果检测到 RX 引脚上的电平保持高状态超过一个字符周期的时间,则认为发生了空闲条件,并触发 IDLE 中断[^1]。此事件通常用来标记当前一数据已经完成传输,从而允许处理器执行后续操作,比如保存已接收的数据或将缓冲区清零以便准备下一次接收。 #### 2. **配置步骤** ##### (a) 初始化 UARTDMA 为了利用空闲中断来高效管理数据接收过程,需要先初始化好相应的硬件资源——即设置好 UART 参数以及关联其工作的直接存储访问(DMA)[^4]: ```c // 配置UART参数, 如波特率、字长等. static void MX_USART2_UART_Init(void){ huart2.Instance = USART2; huart2.Init.BaudRate = 9600; // 设置波特率为9600bps huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if(HAL_UART_Init(&huart2)!= HAL_OK){ Error_Handler(); } } // 启动DMA通道并绑定至USART外设 static void MX_DMA_Init(void){ __HAL_RCC_DMA1_CLK_ENABLE(); hdma_usart2_rx.Instance = DMA1_Channel6; hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; hdma_usart2_rx.Init.Priority = DMA_PRIORITY_HIGH; if(HAL_DMA_Init(&hdma_usart2_rx)!= HAL_OK){ Error_Handler(); } __HAL_LINKDMA(&huart2,Rx,DmaHandle); } ``` ##### (b) 开启IDLE中断及其他必要选项 通过调用`__HAL_UART_ENABLE_IT()`宏指令启用特定类型的中断源,在这里我们需要关注的是IDLE类型[^3]: ```c __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); // Enable Idle Interrupt ``` 同时也要记得调整向量表里对应IRQ handler的行为逻辑: ```c void USART2_IRQHandler(void){ uint16_t temp=0; /* Enter your code here */ if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)){ __HAL_UART_CLEAR_IDLEFLAG(&huart2); // Stop current transfer and reconfigure buffer pointer etc... HAL_UART_DMAStop(&huart2); // Restart the next cycle of receiving data via DMA again after processing previous one completely. HAL_UART_Receive_DMA(&huart2,(uint8_t*)rxBuffer,BUFFER_SIZE); }else{ HAL_UART_IRQHandler(&huart2); } } ``` 注意上述代码片段中包含了对可能发生的其他种类异常情况(例如溢出错误 ORE 或者FE)进行适当处理的部分[^2]^. #### 3. **注意事项** - 当前正在运行的任务可能会因为频繁进入服务程序而受到影响性能表现;因此建议合理规划各部分功能模块之间的关系结构。 - 如果采用循环模式(Circular Mode),则无需手动干预指针位置更新动作,反之则需自行维护读写索引变量值变化轨迹以防越界风险发生。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值