3、通讯延迟拆解:从物理层到应用层的延迟构成

做工业通讯这么多年,我见过太多人一上来就问「Modbus 到底能跑多快?」。说实话,这个问题本身就不太对。你想想看,通讯延迟不是一块铁板,它是层层叠加出来的。今天我就带你把每一层扒开看看。

3.1 物理层延迟:信号在线上跑需要时间

物理层延迟,说白了就是电信号在导线里跑的时间。这个延迟由两个因素决定:线缆长度和信号传播速度。

电信号在铜缆中的传播速度大约是光速的 60%~70%。算下来,每 100 米线缆大概会产生 0.5~0.6 微秒的延迟。嗯,这个数字在短距离通讯里几乎可以忽略。但如果你在工厂里拉了几百米线,那就得算一算了。

我在项目中遇到过一个案例:一条产线用了将近 500 米的 RS-485 总线,结果轮询周期死活压不进 100 毫秒。后来一算,光物理层往返延迟就占了将近 5 微秒。虽然看起来不多,但乘以几百个从站,累积效应就出来了。

物理层延迟计算公式:

单程延迟 = 线缆长度 / (光速 × 传输速度因子)
例如:500米 / (3×10⁸ m/s × 0.65) ≈ 2.56 μs

我的建议:布线时尽量走星型拓扑,别搞长距离菊花链。每多 100 米线,你的轮询周期就得多留 1 微秒的余量。

3.2 数据链路层延迟:帧的打包与拆解

这一层是很多人容易忽略的。数据链路层负责把你要发的数据打包成帧,加上起始位、地址、校验码这些东西。Modbus RTU 的帧结构很简单:地址 1 字节 + 功能码 1 字节 + 数据 N 字节 + CRC 2 字节。

但这里有个坑:帧间隔时间。Modbus RTU 规定,两个帧之间必须有至少 3.5 个字符时间的静默期。这个时间怎么算?

波特率 9600 bps 时:
1 个字符 = 1 起始位 + 8 数据位 + 1 停止位 = 10 位
1 个字符时间 = 10 / 9600 ≈ 1.04 ms
3.5 个字符时间 ≈ 3.64 ms

你看,光这个帧间隔就占了将近 4 毫秒。如果你用 9600 波特率,一个轮询周期里光间隔时间就能吃掉几十毫秒。

我曾经踩过的坑:有次调试一个系统,从站老是丢包。查了半天,发现是主站发的帧间隔太短,从站来不及处理。后来把帧间隔从 3.5 字符改成 5 字符,问题就解决了。嗯,有些从站芯片对时序要求比较苛刻。

3.3 协议层延迟:Modbus 的请求-应答机制

Modbus 是典型的请求-应答模式。主站发一个请求,从站收到后处理,再发回响应。这个过程中,协议层延迟包括:

  • 请求帧发送时间:取决于数据长度和波特率
  • 从站处理时间:从站 CPU 解析指令、读取数据、组装响应的时间
  • 响应帧发送时间:同样取决于数据长度和波特率
  • 帧间隔时间:请求和响应之间、响应和下一个请求之间

我习惯用一个简单的公式来估算单次通讯的总延迟:

总延迟 = 请求帧发送时间 + 从站处理时间 + 响应帧发送时间 + 2 × 帧间隔时间

举个例子,9600 波特率下读 10 个寄存器:

环节 时间计算 耗时
请求帧(8 字节) 8 × 10 / 9600 8.33 ms
从站处理 典型值 5~20 ms
响应帧(8+10×2=28 字节) 28 × 10 / 9600 29.17 ms
帧间隔 ×2 3.64 ms × 2 7.28 ms
总计 49.78~64.78 ms

看到没?一次读写操作就要 50~65 毫秒。如果你有 20 个从站,轮询一圈就是 1~1.3 秒。这还只是理论值,实际中还要加上操作系统调度延迟、驱动层缓冲延迟等。

3.4 应用层延迟:你的程序在磨蹭什么?

应用层延迟是最容易被忽视的。说白了,就是你的上位机程序在发请求之前、收到响应之后,干了多少「多余」的事。

我见过最夸张的例子:一个工程师在 PLC 里用梯形图写 Modbus 通讯,每个读写指令之间插了 50 毫秒的定时器。问他为什么,他说「怕通讯太快不稳定」。结果整个系统轮询周期直接飙到 5 秒以上。

应用层常见的延迟来源:

  • 数据格式转换(比如把 Modbus 的 16 位整数转成浮点数)
  • 日志记录(每次通讯都写一次数据库)
  • 界面刷新(收到数据后立即更新几十个控件)
  • 冗余的延时等待(很多人习惯加个 Sleep(10))

我个人习惯是在应用层做两件事:一是把数据转换和日志记录放到后台线程,别阻塞通讯主循环;二是用时间戳记录每次通讯的实际耗时,这样能快速定位到底是哪一层在拖后腿。

3.5 各层延迟的占比分析

咱们来做个总结。在一个典型的 9600 波特率、100 米线缆、读 10 个寄存器的场景下,各层延迟的占比大概是这样的:

延迟层级 典型耗时 占比 优化空间
物理层 ~3 μs 几乎为 0 布线优化
数据链路层 ~7 ms(帧间隔) 10%~15% 提高波特率
协议层 ~50 ms(收发+处理) 75%~85% 批量读写、提高波特率
应用层 0~50 ms 0~40% 代码优化、异步处理

我的经验:协议层永远是瓶颈。别在物理层和应用层上花太多精力优化,先把波特率从 9600 提到 115200,延迟能直接降到原来的 1/12。这是性价比最高的优化手段,没有之一。

好了,这一章我们把通讯延迟从物理层到应用层拆了个遍。下一章我会讲怎么针对这些延迟做具体的周期优化。记住一句话:先测量,再优化。别凭感觉改参数,拿示波器或者抓包工具看看实际波形,比什么都管用。