1518057960 发表于 2025-9-30 16:20:05

提问,串口DMA发送问题(已解决)

使用官方stc32g的官方例程测试,我的是STC32G864K
如图
当使用延时Delay1000ms();的时候,DMA只在上电时发送了一次
当把Delay1000ms();注释,DMA能会一直发送数据
把DMA_UR1T_CR = 0xc0;注释掉,也是只发一次数据
但加了延时后好像就进不去if((DmaTxFlag))这个条件
DmaTxFlag只在中断中置1,

那是不是 delay导致中断进不去了?还是有什么其他原因?delay是官方isp上复制的应该不会有错吧?
有没有大佬遇到过这种情况的解答一下

国芯人工智能 发表于 2025-9-30 16:23:00

你提出的问题是关于 STC32G864K 芯片在使用 串口DMA发送数据 时,加入延时函数 Delay1000ms() 后,DMA发送只执行一次,而去掉延时则能持续发送数据。你怀疑延时函数是否影响了中断的响应,从而导致 DmaTxFlag 无法置位。

下面我将从 硬件原理、DMA机制、中断响应、延时函数实现机制 等角度,结合你的代码片段,进行详细分析并给出建议。

一、问题现象总结

1. 使用 STC32G 官方例程,串口通过 DMA 发送数据。
2. 当加入 Delay1000ms(); 时,DMA 只发送一次。
3. 注释掉 Delay1000ms(); 后,DMA 可持续发送。
4. 注释掉 DMAUR1TCR = 0xc0; 后,也只发送一次。
5. DmaTxFlag 是在中断中置位的,但加了延时后无法进入 if(DmaTxFlag) 分支。

二、可能的原因分析

1. 延时函数占用 CPU 时间,阻塞中断响应

你提到的 Delay1000ms(); 是“阻塞式延时函数”,即在这1秒内,CPU 会一直执行空循环,不做其他事情。这种延时方式会 完全阻塞主程序流程,并且 可能屏蔽中断响应。
如果 Delay1000ms(); 是基于 关闭中断 的方式实现的(例如使用 EA = 0;),那么在延时期间,DMA发送完成中断将无法被响应。
即使没有关闭全局中断,如果延时函数本身是一个“死循环”,那么在延时期间,中断服务程序虽然触发了,但主程序没有机会去处理中断标志位(如 DmaTxFlag)。

2. DMA发送配置不完整或未重新使能
DMAUR1TCR = 0xc0; 是用于启动DMA传输的寄存器操作。如果每次发送后没有重新设置该寄存器,DMA可能不会再次启动。
在DMA中断中,是否对 DMA 通道进行了 重新初始化或重新使能?如果没有,DMA只能发送一次。

3. DmaTxFlag 未正确清除或未及时处理
如果 DmaTxFlag 没有在中断中正确置位,或者在主程序中未及时清除,可能导致程序逻辑判断失效。
若中断未被及时响应(如因延时函数阻塞),DmaTxFlag 的置位也会被“延迟”,主程序可能已经跳过判断。

三、建议解决方案

1. 避免使用阻塞式延时函数
尽量使用 定时器中断+标志位 实现延时,避免阻塞CPU。
或者使用 非阻塞延时函数,例如:
c
void DelayMs(uint16t ms) {
      while (ms--) {
          DelayUs(1000);
      }
}
但前提是 DelayUs() 不关闭中断。

2. 检查中断使能配置

确保以下中断使能已正确开启:

c
EA = 1;             // 全局中断使能
DMAIE = 1;         // DMA中断使能

此外,检查DMA通道的中断使能是否开启。

3. 在DMA中断中重新启动DMA传输

在DMA发送完成后,应在中断服务程序中重新配置并启动DMA:

c
void DMAISR(void) interrupt 18 {
    if (DMAISRTXDONEFLAG) {
      DmaTxFlag = 1;
      DMAUR1TCR = 0xc0;   // 重新启动DMA发送
      DMAISRTXDONECLEAR; // 清除中断标志
    }
}

注意:具体寄存器名称和位定义请参考STC32G的官方手册。

4. 优化主循环逻辑

不要在主循环中频繁调用长时间延时函数。可以使用如下方式:

c
uint32t lastTime = 0;

while (1) {
    if (DmaTxFlag) {
      DmaTxFlag = 0;
      // 重新准备数据并启动DMA
    }

    if (GetSysTime() - lastTime >= 1000) {
      lastTime = GetSysTime();
      // 执行1秒周期性操作
    }
}

其中 GetSysTime() 可以是一个基于定时器的毫秒级时间戳函数。

四、总结

| 问题点 | 原因分析 | 建议 |
|--------|----------|------|
| 加延时后DMA只发一次 | 延时函数阻塞中断响应 | 改用非阻塞延时或定时器 |
| 注释DMA启动寄存器后只发一次 | DMA未重新使能 | 中断中重新启动DMA |
| DmaTxFlag未进入判断 | 中断未响应或标志未置位 | 检查中断配置与标志处理 |
| 延时函数是否错误 | 可能关闭了中断 | 查看Delay函数实现是否影响中断 |

五、附加建议
使用调试器(如STC-ISP或Keil+STC-Link)观察中断是否真正触发。
打印调试信息:可通过串口输出调试信息,确认中断是否执行。
阅读官方手册:STC32G的DMA和串口部分,确认寄存器配置是否符合预期。

如果你能提供完整的代码片段(尤其是DMA中断服务程序和延时函数的实现),我可以进一步帮你定位具体问题。

如有需要,我也可以帮你改写为非阻塞式延时版本,或者提供基于定时器的周期性任务处理方案。

如需进一步帮助,请继续提问。

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

ercircle 发表于 2025-9-30 16:53:32

楼主打包上传下问题工程看看

1518057960 发表于 2025-9-30 17:20:30

ercircle 发表于 2025-9-30 16:53
楼主打包上传下问题工程看看

刚刚测试 使用了计数延时,也还是不行

ercircle 发表于 2025-9-30 19:09:08

1518057960 发表于 2025-9-30 17:20
刚刚测试 使用了计数延时,也还是不行


可以复现,取消共用用户中断号恢复正常,发一包255字节回一包.
感觉加上延时刚好遇到中断冲突导致的bug。




关于大于31中断号的使用可以看手册这个章节:


1518057960 发表于 2025-10-1 18:54:43

ercircle 发表于 2025-9-30 19:09
可以复现,取消共用用户中断号恢复正常,发一包255字节回一包.
感觉加上延时刚好遇到中断冲突导致的bug ...

好的谢谢,等过完假期试试
页: [1]
查看完整版本: 提问,串口DMA发送问题(已解决)