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

文章描述了一个在STM32中使用DMA进行UART接收时遇到的问题,当配置为LIN模式并启用空闲中断后,中断回调函数的Size参数在特定情况下出现错误。解决方案是禁用DMA的半满中断,只保留全满中断和串口空闲中断,从而确保数据正确接收且Size值正确。

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得到的值也是正确的

### STM32G474 UART DMA 中断配置及问题解决 #### 配置UARTDMA中断 对于STM32G474系列微控制器,在使用UART接口配合DMA传输时,确保正确初始化并配置相关外设至关重要。具体来说: - **硬件资源分配**:通过STM32CubeMX工具完成基本的引脚功能定义以及USART/DMA模块的选择与参数设定[^3]。 - **软件层面处理**: - 初始化UART实例`HardwareUSART1`,设置波特率、字长等通信属性。 - 启用DMA流用于自动取来自UART数据缓冲区。 - 设置接收模式为IDLE线检测触发方式,以便能够响应串口空闲状态变化事件。 ```c // 清除发送完成标志位 __HAL_UART_CLEAR_FLAG(&HardwareUSART1, UART_CLEAR_TCF); // 使能USART1接收数据时产生RXNE中断 __HAL_UART_ENABLE_IT(&HardwareUSART1,UART_IT_RXNE); ``` 上述代码片段展示了如何清除特定条件下的错误标记,并重新激活接收新字符的能力[^2]。 当遇到因为空闲时间过短而导致无法正常捕获全部消息的情况时,可以考虑调整超时阈值或是优化ISR内部逻辑来提高系统的鲁棒性。 针对可能存在的DMA缓存不足引发的问题,建议适当增加缓冲区大小至合理范围(如8KB),同时密切监控实际占用情况以防溢出风险。如果增大缓冲区后仍存在问题,则需深入排查其他潜在因素,比如是否存在外部干扰源影响信号质量等[^1]。 为了更好地管理异步I/O操作期间可能出现的各种异常状况,可以在IRQ Handler内加入更细致的状态判断机制,如下所示: ```c void USART1_IRQHandler(void) { /* 用户自定义部分 */ if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=RESET)){ __HAL_UART_CLEAR_IDLEFLAG(&huart1); HAL_UART_DMAStop(&huart1); uint32_t remainingBytes=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx); size_t receivedLength=USART1_RX_BUF_SIZE-remainingBytes; // 更新全局变量反映当前已成功获取到的有效载荷量级 USART1_RECV_FLAG=(receivedLength>0)?1:0; // 继续监听后续到来的新一轮输入序列 HAL_UART_Receive_DMA(&huart1,(uint8_t*)USART1_RX_BUF,USART1_RX_BUF_SIZE); } } ``` 此段程序实现了对idle line event作出快速反应的功能,即一旦发现线路处于静默期就立即终止现有DMA会话并将最新一批有效负载提取出来保存备用,随后再次启动新一轮监听周期等待下一次有效的起始边界到达。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值