Yang.Lian 发表于 2025-8-16 09:34:31

32G等 DMA 串口接收超时判断的方法

<p>我看官方给出 32G 实现 UART4 下 DMA 软件超时的方法,实际上还是用了串口中断,既然要用串口中断,直接用串口中断不就好了?MCU还是在不停被打断,意义不大。</p>
<p>最近用32G 比较多,开始的时候用RTOS 比如 迟老师的 <a href="https://www.stcaimcu.com/thread-1807-1-1.html" title="全局不关总中断的 RTOS,CosyOS-III-V1.2.0, 送 擎天柱-AI8051U转89C52核心板">CosyOS</a>,慢慢开始觉得还是裸机比较爽。</p>
<p>脱离 RTOS 作超时判断比较麻烦一点,刚好在看32G的文档的时候,注意到 DMA_URnR_DONE 和 DMA_URnR_DONEH 寄存器。</p>
<p>这两个寄存器主要是用来记录当前读取了多少,那么如果在规定时间内,检查 DMA_URnR_DONE 和 DMA_URnR_DONEH 的数量大于0而且没有变化,那就不是超时了?</p>
<p>主循环中直接</p>
<pre><code class="language-CPP">unsigned int req_length = DMA_UR4R_DONE | (DMA_UR4R_DONEH &lt;&lt; 8);

    if (req_length &gt; 0)
    {
      if (modbus_rx_last_count != req_length)
      {
            os_timer_restart_8(&amp;modbus_rx_timeout);
            modbus_rx_last_count = req_length;
      }
      else
      {
            if (os_timer_expired_8(&amp;modbus_rx_timeout))
            {
                // 收到数据了
                DMA_UR4R_CR = 0; // 停止接收
                DMA_UR4R_DONE = 0;
                DMA_UR4R_DONEH = 0;

                IO_RS485_TC = ENABLE_WRITE; // 转入发送状态

                {
                  int resp_length = modbus_response(
                        1,
                        modbus_rx_buffer,
                        req_length,
                        modbus_tx_buffer,
                        &amp;modbus_main_mapping);

                  if (resp_length &gt; 0)
                  {
                        // 需要发送
                        DMA_UR4T_AMT = (resp_length - 1);
                        DMA_UR4T_AMTH = (resp_length - 1) &gt;&gt; 8;
                        DMA_UR4T_STA = 0;
                        DMA_UR4T_CR = 0xC0;
                  }
                  else
                  {
                        // 不需要发送
                        modbus_start_recv();
                  }
                }
            }
      }
    }
}
</code></pre>
<p>其中 modbus_rx_timeout 是一个定时器,可以用 os_timer_expired_8 函数来判断是否超时,或者用 os_timer_restart_8(&amp;modbus_rx_timeout); 来重新开始计时。</p>
<p>我实验了一下,没有发现什么问题</p>
<p>我就没有想明白,为什么官方实现的方式要用 串口中断,是不是有什么隐情?还是说官方的那种方式比较稳定,而这个方式不太稳定?请指教</p>

国芯人工智能 发表于 2025-8-16 09:38:00

在使用 STC32G 系列单片机进行 UART 通信时,结合 DMA 实现高效数据接收是一种常见需求。然而,在实际应用中,如何有效判断串口接收是否超时,是开发者常遇到的问题之一。

官方文档中提供的 UART4 下 DMA 软件超时判断方法,确实依赖于串口中断机制来实现超时检测。这种设计虽然能够实现基本的超时功能,但其本质仍需依赖中断服务程序(ISR)的触发,导致 MCU 在无数据到来时仍会频繁进入中断处理流程,影响系统效率。

若仅为了实现超时判断而引入串口中断,实则并未充分发挥 DMA 的优势。DMA 的核心价值在于减少 CPU 中断频率,提高数据传输效率。因此,建议在裸机环境下,通过轮询方式结合 DMA 寄存器状态进行超时判断,以实现更高效的系统设计。

STC32G 系列提供了两个关键寄存器:DMAURnRDONE 和 DMAURnRDONEH,它们分别记录当前 DMA 接收通道中已接收的数据字节数。通过读取这两个寄存器的值,可以获取当前接收缓冲区中的数据长度。

在主循环中,可采用如下方式读取接收长度:

c
unsigned int reqlength = DMAUR4RDONE | (DMAUR4RDONEH0)
{
    if (modbusrxlastcount != reqlength)
    {
      ostimerrestart8(&modbusrxtimeout); // 重置超时计时器
      modbusrxlastcount = reqlength;
    }
    else
    {
      if (ostimerexpired8(&modbusrxtimeout))
      {
            // 超时处理
            DMAUR4RCR = 0; // 停止 DMA 接收
            // ...
      }
    }
}

此方法避免了依赖串口中断,实现了基于轮询的超时判断机制,适用于裸机环境下的高效数据接收控制。

需要注意的是,该方案依赖于定时器的精度和主循环的执行频率。在实际应用中,应合理设置超时阈值,并确保主循环能够及时响应数据变化。

综上所述,结合 DMA 寄存器状态和定时器机制,可以在不依赖串口中断的情况下实现有效的 UART 接收超时判断,提升系统性能与稳定性。

[本文内容由国芯人工智能辅助生成,仅供参考]

ercircle 发表于 2025-8-16 11:24:58

查询模式中断模式,萝卜白菜各有所爱,选择合适你项目的即可。

Yang.Lian 发表于 2025-8-16 21:27:12

经过测试发现即使没有什么负载的情况下,偶尔还是会出现丢包的现象,不清楚是什么原因

Yang.Lian 发表于 6 天前

Yang.Lian 发表于 2025-8-16 21:27
经过测试发现即使没有什么负载的情况下,偶尔还是会出现丢包的现象,不清楚是什么原因 ...

问题找到了跟一DMA点关系都没有,现在就是一个字稳稳当当当
页: [1]
查看完整版本: 32G等 DMA 串口接收超时判断的方法