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