RT-Thread论坛
直播中

王健

8年用户 1033经验值
私信 关注
[问答]

串口发送数据的过程中,会中间停几毫秒,为什么?

串口发送数据的过程中,会中间停几毫秒,导致PLC触发了MODBUS的T3.5,数据接收不对,
1、一开始用的是freemodbus,查看后发现是轮询发送,后来改为不用freemodbus,直接发。情况依旧
2、把其余线程都关掉,情况依旧
3、把发送改为DMA,情况依旧,
请问下能有什么办法让他中间不断
注:红框内的,应该是连续的,但是断开了,串口助手的超时时间是10ms



回帖(1)

张鹏

2025-10-13 18:10:35

我们分析一下问题:在串口发送数据的过程中,中间会停顿几毫秒,导致MODBUS的T3.5超时(T3.5是MODBUS协议中帧间超时,通常为3.5个字符时间),从而数据接收不正确。

已经尝试的方法:
1. 一开始使用freemodbus(轮询发送),问题存在;改为直接发送,问题依旧。
2. 关闭其他线程,问题依旧。
3. 改用DMA发送,问题依旧。

注意:从串口助手看到的数据,红框内应该是连续的数据,但是断开了,而串口助手的超时时间是10ms。

可能的原因:
1. 发送数据时,数据被分成多个包发送,每个包之间有时间间隔。
2. 发送过程中有中断或高优先级任务打断了发送。
3. 硬件问题或驱动问题。

虽然已经尝试了关闭其他线程和改用DMA,但问题依旧,说明可能不是由其他任务或中断(软件层面)导致的,也可能是硬件或驱动层面的问题。

建议的排查步骤:

1. **检查发送函数**:确认发送函数是否会将数据分成多个小包发送?例如,如果发送缓冲区较小,可能会分段发送。查看发送函数内部实现。

2. **检查串口配置**:确认串口的波特率、数据位、停止位等配置是否正确。特别是,确认波特率设置是否准确(可以使用示波器检查实际波特率)。

3. **检查流控**:是否启用了硬件流控(RTS/CTS)?如果启用了,可能是流控信号导致发送暂停。

4. **使用示波器或逻辑分析仪**:这是最直接的方法,通过测量串口TX引脚上的波形,可以精确看到每个字符之间的时间间隔。如果发现字符之间有较大的间隔,那么可以确定是发送端的问题。

5. **检查DMA配置**:虽然使用了DMA,但DMA传输也可能被中断(比如DMA传输完成中断处理不及时,或者DMA被其他更高优先级的外设占用)。但通常DMA是硬件完成传输,不应有太大延迟。

6. **检查中断优先级**:如果使用了中断,确保串口中断优先级足够高,不会被其他中断长时间阻塞。

7. **检查系统时钟配置**:系统时钟配置是否正确?特别是串口使用的时钟源是否稳定?

8. **降低波特率测试**:尝试降低波特率(比如从9600到1200),看是否仍然出现停顿。如果降低波特率后停顿消失,可能是系统处理能力不足或中断延迟导致。

9. **检查MODBUS协议实现**:确认T3.5超时时间设置是否正确?T3.5超时时间应该是3.5个字符传输时间。例如,在波特率为9600时,一个字符(包括起始位、8位数据、停止位,共10位)的传输时间是1/9600*10≈1.04ms,则T3.5=3.5*1.04≈3.64ms。如果串口助手设置的超时是10ms,那么超过3.64ms的间隔就会被MODBUS认为是帧结束。所以,如果发送过程中有两个字符之间的间隔大于3.64ms,就会触发T3.5超时。

10. **发送连续数据**:尝试发送一个较长的连续数据(比如100个字节),观察在发送过程中是否在某个固定位置出现间隔?如果是,可能是发送缓冲区大小限制,或者发送函数内部有分块。

11. **检查是否使用了操作系统**:如果使用了RTOS,检查任务堆栈是否足够,以及任务优先级设置是否合理。

12. **直接寄存器操作**:如果可能,尝试用最简单的轮询方式(不使用DMA,也不使用中断)发送一个数据包,看是否仍然有间隔。这样可以排除DMA和中断的影响。

根据问题描述,已经尝试了直接发送和DMA,并且关闭了其他线程,问题依旧。那么重点可以放在:

- 硬件连接问题(如线路干扰,但可能性较小)
- 串口外设配置问题(比如发送使能、时钟使能等)
- DMA配置问题(比如DMA传输模式、传输完成中断等)

一个可能的解决方向:确保在发送整个MODBUS帧的过程中,禁止任务切换(如果使用RTOS)或禁止所有中断(临界区保护),然后连续发送整个帧。这样就不会被其他中断打断。

但是,在DMA传输过程中,CPU可以执行其他任务,而DMA传输本身不应该被打断(因为是硬件传输)。所以使用DMA时出现间隔,可能是由于DMA传输被分成了多个事务(比如设置了DMA最大传输长度),或者DMA传输完成中断处理中重新配置DMA的时间过长。

具体操作建议:

1. **使用单次DMA传输整个帧**:确保DMA配置为一次性传输整个数据帧,而不是分成多个块。检查DMA的配置,比如DMA缓存区大小是否足够,是否配置了循环模式(应该禁用循环模式)。

2. **在DMA传输前关闭中断,传输完成后再打开中断**:虽然这可能会影响系统的实时性,但可以测试是否是因为中断导致DMA传输被延迟。

3. **检查DMA传输的触发方式**:是否在启动DMA后,外设(串口)就立即开始发送?还是需要等待触发条件?

4. **使用中断发送**:尝试使用中断发送(非DMA),但中断发送时,在发送整个帧的过程中,提升发送中断优先级,并关闭其他中断(或使用临界区)。

5. **使用轮询发送**:在发送整个帧的过程中,禁用中断,然后用轮询方式逐个字节发送。这样可以保证连续发送。

示例代码(轮询发送,禁用中断):
```c
void USART_SendData(USART_TypeDef* USARTx, uint8_t *pData, uint16_t len)
{
   // 进入临界区(禁止中断)
   __disable_irq();

   while (len--)
   {
     // 等待发送缓冲区为空
     while (!(USARTx->SR & USART_SR_TXE));
     USARTx->DR = *pData++;
   }

   // 等待最后一次传输完成(可选,确保最后一个字节发送完成)
   while (!(USARTx->SR & USART_SR_TC));

   // 退出临界区(使能中断)
   __enable_irq();
}
```

注意:这种方法在发送期间会阻塞系统,但可以保证发送的连续性。

如果使用HAL库,也可以考虑在发送前调用__HAL_DISABLE_INTERRUPTS,发送后再开启。

6. **检查是否有硬件复位或看门狗复位**:在发送过程中是否有复位发生?

7. **电源问题**:电压不稳定导致单片机工作异常?

8. **更换硬件**:如果有可能,更换一个串口模块或单片机测试,以排除硬件故障。

由于问题描述中已经尝试了多种方法,所以需要逐步缩小范围。建议优先使用示波器检查波形,以确定问题的确切位置。

在串口发送数据过程中出现中间停顿几毫秒的问题,导致MODBUS的T3.5超时(通常为3.5个字符传输时间),以下是可能原因和解决方案的详细分析:


根本原因分析




  1. 发送流程被打断



    • 即使关闭其他线程或使用DMA,高优先级中断(如系统滴答定时器SysTick、硬件故障中断等)仍可能抢占CPU,导致发送间隔。

    • DMA传输虽不占用CPU,但若配置为非连续模式(如每次发送后重新配置DMA),或DMA缓存区不连续,仍会产生间隔。




  2. 数据分块发送



    • 发送函数内部可能将数据分割成小包(如受限于发送缓冲区大小),包之间产生延迟。




  3. 硬件或驱动问题



    • 串口外设时钟不稳定或配置错误(如波特率精度不足)。

    • 硬件流控(RTS/CTS)意外启用,导致等待对方信号。

    • 线路干扰或硬件故障。




  4. MODBUS协议要求严格



    • T3.5超时时间极短(例如9600波特率下约3.5ms),任何微小延迟均可能触发超时。






针对性解决方案


1. 确保数据连续发送(关键步骤)




  • 禁用中断发送整个帧
    使用临界区保护,在发送过程中禁止中断:


     __disable_irq();  // 关闭所有中断
    HAL_UART_Transmit(&huart, data, len, HAL_MAX_DELAY);  // 轮询发送
    __enable_irq();   // 重新启用中断

    此方法强制CPU连续发送,避免任何任务/中断抢占。




  • 优化DMA配置



    • 设置DMA为单次传输模式(Non-Circular),一次性发送完整数据帧。

    • 确保DMA缓存区连续(如使用__attribute__((aligned(4))对齐内存)。

    • 启动DMA后等待传输完成,避免分块:
      HAL_UART_Transmit_DMA(&huart, data, len);
      while (HAL_UART_GetState(&huart) != HAL_UART_STATE_READY) {} // 等待发送完成




2. 优化系统配置



  • 提升串口中断优先级
    将UART和DMA中断优先级设置为最高(如NVIC优先级0):
     HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0); // 根据实际DMA通道调整

  • 检查波特率精度
    使用示波器测量实际波特率,调整时钟源(如更换高精度晶振)。

  • 禁用硬件流控
    确认串口初始化中未启用RTS/CTS:
     huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;


3. MODBUS协议层调整



  • 延长T3.5超时
    在PLC端(若可控)适当增加T3.5时间(如调整到15ms),但需确保符合协议标准。

  • 添加帧间填充
    在MODBUS帧之间插入延时(如HAL_Delay(2)),避免PLC误判帧结束。


4. 硬件诊断



  • 示波器检查
    测量TX引脚波形,确认:

    • 字符间隔是否均匀。

    • 停顿位置是否固定(如每32字节停顿,可能是缓冲区限制)。


  • 更换硬件环境测试
    尝试不同串口线、接口转换器或单片机型号,排除硬件故障。




验证步骤




  1. 简化测试代码


    uint8_t test_data[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B}; // 示例MODBUS帧
    __disable_irq();
    HAL_UART_Transmit(&huart, test_data, sizeof(test_data), 100); // 轮询发送
    __enable_irq();

    观察此时串口助手是否仍有间隔。




  2. 测量波形



    • 正常发送时字符间隔应为 1/(波特率) * 10(10位/字符)。例如9600波特率下,字符间隔约1.04ms。

    • 若仍存在>2ms间隔,则需检查时钟配置或硬件故障。






总结建议



  • 首选方案:使用临界区+轮询发送__disable_irq() + HAL_UART_Transmit),确保帧内绝对连续。

  • 备选方案:若需DMA,配置为最高优先级单次传输,并等待传输完成。

  • 协议层面:协调PLC适当调整T3.5超时时间(如从3.5ms改为5ms),兼容现有硬件限制。


通过上述方法,可消除发送过程中的异常间隔,解决MODBUS超时问题。如仍不生效,需结合示波器进一步诊断硬件时序。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分