解决STM32使用DMA传输UART空闲中断接收数据时遇到的问题及解决方法

STM32使用DMA传输UART空闲中断中接收的数据遇到的问题以及解决方法

CubeMX配置
  • 串口配置:使用默认配置(传输数据长度为8 Bit,奇偶检验无,停止位为1 Bit, 接收和发送都使能),因为我的是LIN项目所以使用的时串口的LIN模式,一般就是异步通信

  • 打开DMA传输

  • 打开串口接收中断

  • 生成工程

  • 在mian.c中添加如下代码

    //添加方法定义
    void Util_Receive_IT(UART_HandleTypeDef *huart);
    
    //USER CODE BEGIN 4之间实现Util_Receive_IT方法
    /**
     * 重写接收中断函数
     */
    void Util_Receive_IT(UART_HandleTypeDef *huart)
    {
        if(huart == &huart2)
        {
            //开启DMA传输UART空闲中断中接收的数据,并在接收到UART空闲中断后停止传输
            //判断DMA接收是否正常启动
            if(HAL_UARTEx_ReceiveToIdle_DMA(huart, pLINRxBuff, LIN_RX_MAXSIZE) != HAL_OK)
            {
                //如果有错误的话,清空缓冲,置位一些标志
                HAL_UART_AbortReceive(huart);
                //重启DMA接收,pLINRxBuff是接收缓冲数组,接收的字节数,我这里LIN_RX_MAXSIZE是12
                HAL_UARTEx_ReceiveToIdle_DMA(huart, pLINRxBuff, LIN_RX_MAXSIZE);
            }
        }
    }
    
    //USER CODE BEGIN 2中添加如下代码
    //开启中断接收
    Util_Receive_IT(&huart2);
    
    //USER CODE BEGIN 4之间增加中断回调函数和错误回调函数的实现
    void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
    {
        //LIN协议
        if (huart == &huart2)
        {
            //具体的数据处理函数
        }
        //重启DMA接收
        Util_Receive_IT(huart);
    }
    /**
     * 重写UART错误中断处理程序,重新开启中断
     *
     * @brief UART error callback.
     * @param huart UART handle.
     * @retval None
     */
    void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
    {
        /* Prevent unused argument(s) compilation warning */
        //解决串口溢出,导致不断进入串口中断函数,使MCU过载的问题
        if(HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE)
        {
            //清除ORE标志位
            __HAL_UART_FLUSH_DRREGISTER(huart);
            Util_Receive_IT(huart);
            huart->ErrorCode = HAL_UART_ERROR_NONE;
        }
    }
    
  • 存在的问题:

  • LIN_RX_MAXSIZE是12,所以程序最多可以接收12个字节的数据,但是因为HAL_UARTEx_RxEventCallback回调函数会处理半满中断,全满中断和串口空闲中断,所以接收6个字节(包括6个字节)的数据中断回调函数中Size参数得到的值是正确的,并且保存到接收缓冲数组中的数据也是正确的。当接收超过6个字节的数据,中断回调函数中Size参数得到的值是LIN_RX_MAXSIZE一半,也就是6,只要字节数在7-12之间,参数Size的值永远是6,数据是可以正常接收的。
  • 原因可能开启了DMA通道的中断,然后HAL_UARTEx_RxEventCallback中断回调函数会处理半满中断、全满中断和串口空闲中断这三种情况,半满中断先执行导致Size会变成接收字节数的一半。
  • 解决方法
  • 取消DMA通道的中断

  • 在串口页面查看中断,可以发现DMA通道的中断已经取消了

  • 代码不需要修改

  • 存在的问题:

  • 接收1-11个字节的数据是没有问题的,只要接收12个字节的数据,在串口调试过程中,点击串口的发送按钮,需要发送两次12个字节的数据,才会进入到中断回调函数的断点中,并且中断回调函数的Size参数得到的值是不正确的,接收缓存中保存的数据也是不正确的
  • 还有一个问题,当进入中断回调函数的断点中,再次用串口调式助手发送一次数据,当前的中断回调函数执行完后,再次使用串口调试助手发送12字节的数据,中断回调函数只会接收到一个字节的数据
  • 目前没有找到原因
  • 再次开启DMA通道中断
  • 开启DMA通道中断,虽然Size会有错误,但是接收的数据是正确的,只要全满中断和串口空闲中断就可以了,不需要半满中断

  • 通过网上查阅资料,可以使用__HAL_DMA_DISABLE_IT()宏来禁用半满中断

  • stm32g0xx_hal_dma.h定义了全满中断、半满中断和错误中断

  • 修改代码

    //开启中断接收
    Util_Receive_IT(&huart2);
    //禁用DMA半满接收中断
    __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
    
    
    /**
     * 重写接收中断函数
     */
    void Util_Receive_IT(UART_HandleTypeDef *huart)
    {
        if(huart == &huart2)
        {
            //开启DMA传输UART空闲中断中接收的数据,并在接收到UART空闲中断后停止传输
            //判断DMA接收是否正常启动
            if(HAL_UARTEx_ReceiveToIdle_DMA(huart, pLINRxBuff, LIN_RX_MAXSIZE) != HAL_OK)
            {
                //如果有错误的话,清空缓冲,置位一些标志
                HAL_UART_AbortReceive(huart);
                //重启DMA接收
                HAL_UARTEx_ReceiveToIdle_DMA(huart, pLINRxBuff, LIN_RX_MAXSIZE);
            }
            //禁用DMA半满接收中断
            __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
        }
    }
    
  • 每次开启DMA接收时,都要禁用DMA半满接收中断

  • 至此,可以正常接收数据,并且中断回调函数中的Size得到的值也是正确的

  • 物联沃分享整理
    物联沃-IOTWORD物联网 » 解决STM32使用DMA传输UART空闲中断接收数据时遇到的问题及解决方法

    发表评论