资讯动态

Modbus RTU通讯间歇性丢包排查:轮询间隔与ADPRW指令时序优化

发布时间:2026/9/24 14:29:16 来源:尧图企业网站定制
1. 通讯不稳的排查思路先别急着换线换板子干工控这行的多多少少都遇到过 Modbus RTU 通讯时好时坏的情况。设备跑着跑着数据就断了重启一下又能撑一阵子现场折腾半天换线、换模块、换电源钱花了不少问题依旧。我最近就碰到一个典型案例客户那边一台 FX3U 配 FX3U-485ADP-MB 通讯板挂了几台 E5CC 温控器走 Modbus RTU波特率 19200距离不到 30 米按道理这种配置应该稳如老狗结果现场就是时不时丢包有时候几分钟断一次有时候一两个小时才抽风一回。客户一开始怀疑是 RS485 线材质量不行换了屏蔽双绞线问题还在。又怀疑是通讯板坏了换了一块新的 FX3U-485ADP-MB还是老样子。最后甚至把 E5CC 也换了两台依然没解决。找到我的时候客户已经快崩溃了说“硬件全换了一遍总不能是 PLC 本体的问题吧”。我到了现场先没动任何硬件而是让客户把通讯程序调出来看。这一看问题基本就锁定方向了——大概率不是硬件的事而是程序里对 Modbus RTU 报文的处理有漏洞。这个判断基于一个很简单的逻辑如果硬件真有问题通讯应该是持续性地差而不是“时好时坏”。间歇性故障尤其是这种随机性的丢包十有八九跟轮询机制、超时处理、CRC 校验、寄存器地址映射这几个软件层面的东西有关。这篇文章我就把这个案例完整拆一遍从排查思路到最终定位再到具体的梯形图程序和参数配置全部摊开来讲。不管你是用三菱 FX 系列、西门子 S7-200 SMART还是汇川、信捷的 PLC只要走 Modbus RTU这套排查逻辑都能直接套用。尤其是刚入行搞 PLC 编程的朋友看完至少能少走两三个月的弯路。2. Modbus RTU 通讯的核心机制与常见误区2.1 报文结构决定了你必须关注哪些参数Modbus RTU 的报文结构其实不复杂一帧完整的报文就四段从站地址 功能码 数据区 CRC 校验。拿最常用的 03 功能码读保持寄存器举例主机发给从站的报文长这样[从站地址 1字节] [功能码 1字节] [起始寄存器地址 2字节] [寄存器数量 2字节] [CRC 2字节]从站回复的报文[从站地址 1字节] [功能码 1字节] [字节数 1字节] [数据 N字节] [CRC 2字节]看起来很简单对吧但问题往往就藏在这些细节里。比如寄存器地址很多新手会直接把手册上的“寄存器编号”填进去但 Modbus 协议里的“起始地址”是从 0 开始算的而很多设备手册上写的是从 1 开始的编号。你填 40001实际协议里要填 0你填 40002协议里要填 1。这个偏移量搞错了要么读不到数据要么读到的全是乱七八糟的值。再比如 CRC 校验Modbus RTU 用的是 CRC-16/MODBUS多项式 0xA001初始值 0xFFFF低字节在前。这个校验算法本身不难但如果你在程序里自己手写 CRC 计算很容易在字节序或者循环边界上出错。一旦 CRC 算错从站直接丢弃报文主机这边就表现为超时无响应。2.2 轮询间隔和超时时间是最容易被忽视的坑我见过太多程序轮询就是简单粗暴地一个接一个发发完立马发下一条中间不留任何间隔。这种写法在实验室里跑没问题因为设备少、响应快但在现场就很容易出问题。Modbus RTU 协议规定两帧报文之间必须留至少3.5 个字符时间的静默间隔。这个时间怎么算跟波特率有关。比如 19200 波特率一个字符是 11 位1 起始位 8 数据位 1 校验位 1 停止位那一个字符时间就是 11/19200 ≈ 0.573 毫秒3.5 个字符就是大约 2 毫秒。听起来很短对吧但如果你用 ADPRW 指令连续发PLC 的扫描周期可能都不止 2 毫秒所以这个间隔通常不是大问题。真正的问题在于超时时间设置。ADPRW 指令本身没有超时参数它的超时行为取决于 PLC 的通讯端口设置和从站的响应速度。如果从站响应慢而你的轮询又很密集就会出现“上一条还没回下一条已经发出去了”的情况导致通讯冲突。我一般建议在两条 ADPRW 指令之间加一个定时器比如 50 到 100 毫秒的延时给从站足够的响应时间也给总线一个喘息的机会。2.3 CRC 校验不是可选项是必选项有些朋友为了图省事在程序里把 CRC 校验关掉或者用固定值代替。这种做法在单设备、短距离的情况下可能侥幸能跑但只要现场环境稍微复杂一点干扰一上来没有 CRC 校验的报文就会变成“裸奔”从站收到什么就认什么数据错乱是迟早的事。CRC 校验的作用是检测报文在传输过程中是否发生了位错误。RS485 是差分信号抗干扰能力比 RS232 强但不代表不会出错。尤其是在变频器、伺服电机、接触器这些强干扰源附近通讯线如果没有做好屏蔽和接地误码率会明显上升。这时候 CRC 就是最后一道防线它能保证从站只处理正确的报文错误的直接丢弃主机超时后重发。3. 现场排查实录从现象到根因的完整过程3.1 先确认硬件连接和端口配置到现场后我第一步不是看程序而是先确认硬件层面的基本配置。这一步不能省因为如果硬件本身就有问题后面软件调得再好也是白搭。先看接线。FX3U-485ADP-MB 的接线端子是 RDA、RDB、SDA、SDB、SG 这几个。RS485 两线制的话只需要接 RDA 和 RDBSDA 和 SDB 短接或者不接。客户这边接的是两线制RDA 接 E5CC 的 ARDB 接 B-SG 接屏蔽层。这个接法没问题。再看终端电阻。RS485 总线两端需要各接一个 120 欧姆的终端电阻用来消除信号反射。客户这边总线长度不到 30 米波特率 19200按理说终端电阻不是必须的但加上也没坏处。我检查了一下两端都没接终端电阻。这个先记下来后面如果软件调完还不稳再考虑加。然后看 PLC 的通讯端口设置。FX3U-485ADP-MB 是通过 PLC 的通道 1 或者通道 2 来通讯的具体用哪个通道取决于拨码开关和程序里的配置。客户这边用的是通道 1波特率 19200数据位 8停止位 1校验位偶校验。这个配置跟 E5CC 那边的默认设置是一致的没问题。3.2 抓取通讯报文定位丢包规律硬件确认没问题后我让客户把程序下载到 PLC然后在线监控。同时用串口调试工具在总线上抓报文看看通讯到底是怎么断的。抓了一段时间后发现一个很明显的规律每次丢包都发生在读取多个连续寄存器的时候。客户程序里有一条 ADPRW 指令一次性读取 E5CC 的 3 个连续寄存器比如 0x2000、0x2001、0x2002这条指令的失败率明显高于其他只读单个寄存器的指令。这个现象很有意思。如果硬件有问题应该是所有指令都受影响而不是只影响读多个寄存器的指令。所以问题大概率出在这条指令的参数配置上。我把这条 ADPRW 指令的参数调出来看从站地址1功能码03读保持寄存器起始地址H2000寄存器数量3目标寄存器D100 开始看起来没问题对吧但仔细一想E5CC 的 Modbus 寄存器地址映射表里0x2000 开始的寄存器确实是连续的吗我翻了一下 E5CC 的通讯手册发现 0x2000 是“运行/停止状态”0x2001 是“当前温度值”0x2002 是“设定温度值”。这三个寄存器在地址上是连续的但从站内部处理这三个寄存器的读取请求时可能需要分多次访问不同的存储区导致响应时间比读单个寄存器长。而客户的程序里这条读 3 个寄存器的指令后面紧接着就是下一条 ADPRW 指令中间没有加任何延时。结果就是第一条指令还没完全处理完第二条指令已经发出去了总线冲突丢包。3.3 根因确认轮询间隔不足导致总线冲突为了验证这个判断我做了一个简单的实验在两条 ADPRW 指令之间加一个 100 毫秒的定时器延时然后重新下载程序在线监控。结果很明显加了延时之后通讯稳定性大幅提升连续跑了两个小时一次丢包都没有。客户在旁边看着说“就这么简单”我说对就这么简单但前提是你得知道问题出在哪。这个案例的根因就是轮询间隔不足。Modbus RTU 是主从架构同一时刻总线上只能有一个主机在发指令。如果主机发得太快从站还没来得及响应下一条指令就来了从站会直接忽略或者返回错误。表现出来就是间歇性丢包而且丢包的频率跟从站的响应速度直接相关。4. ADPRW 指令的正确用法与梯形图程序4.1 ADPRW 指令的参数详解三菱 FX 系列的 ADPRW 指令是专门用来做 Modbus RTU 通讯的它的参数格式如下ADPRW S1 S2 S3 S4 DS1从站地址范围 0 到 2470 是广播地址S2功能码常用的是 01、02、03、04、05、06、15、16S3起始寄存器地址注意是从 0 开始算的S4寄存器数量或者写入数据读操作时是数量写操作时是数据D目标寄存器或者源寄存器读操作时是存放读取数据的目标写操作时是要写入的数据源拿读 E5CC 当前温度值举例E5CC 的当前温度值寄存器地址是 0x2001从站地址是 1功能码 03读 1 个寄存器存到 D100ADPRW H1 H03 H2001 H1 D100这条指令的意思是向从站 1 发送功能码 03读取起始地址 0x2001 的 1 个寄存器结果存到 D100。4.2 完整的轮询梯形图程序下面是我给客户改完之后的轮询程序用梯形图表示。为了清晰我用文字描述每一步的逻辑你可以直接照着在 GX Works2 或者 GX Works3 里画。第一步初始化通讯参数在 PLC 上电的第一个扫描周期用 M8002 触发通讯端口的初始化。FX3U-485ADP-MB 的通道 1 初始化参数存在 D8400 开始的寄存器里D8400通讯格式设置波特率、数据位、停止位、校验位D8401通讯协议设置 Modbus RTU 模式D8402超时时间单位 10 毫秒具体值根据你的从站设备来定。E5CC 这边是 19200、8、1、偶校验对应的 D8400 值是 H0C81。D8401 设为 H0001表示 Modbus RTU 协议。D8402 设为 H000A表示超时 100 毫秒。第二步轮询状态机用一个步进继电器或者状态寄存器来控制轮询流程。我习惯用 M 继电器做状态标志简单直观。M100轮询启动标志M101读从站 1 当前温度M102读从站 1 设定温度M103读从站 2 当前温度M104读从站 2 设定温度M105轮询完成回到 M101每一步触发一条 ADPRW 指令指令完成后用完成标志位比如 M8029触发下一步同时启动一个 100 毫秒的定时器定时器到了之后才允许下一步执行。第三步ADPRW 指令的触发以 M101 为例LD M101 ADPRW H1 H03 H2001 H1 D100 LD M8029 AND M101 SET M102 RST M101这段逻辑的意思是当 M101 为 ON 时执行 ADPRW 指令读从站 1 的当前温度存到 D100。当指令完成M8029 为 ON且 M101 仍为 ON 时置位 M102复位 M101进入下一步。第四步加入轮询间隔在每一步的 ADPRW 指令完成后不要立即触发下一步而是启动一个定时器比如 T100设定值 K100100 毫秒。定时器到了之后再触发下一步。LD M8029 AND M101 OUT T100 K100 LD T100 SET M102 RST M101这样就能保证每条 ADPRW 指令之间有至少 100 毫秒的间隔给从站足够的响应时间。4.3 参数计算与选择依据为什么选 100 毫秒这个值不是拍脑袋定的而是根据从站的响应时间和波特率算出来的。E5CC 的通讯响应时间手册上写的是最大 50 毫秒。在 19200 波特率下一帧读 1 个寄存器的报文主机发送大约 8 个字节从站回复大约 7 个字节总共 15 个字节。每个字节 11 位总共 165 位传输时间大约是 165/19200 ≈ 8.6 毫秒。加上从站的处理时间最坏情况下一条指令的完整周期大约是 60 毫秒。所以轮询间隔设为 100 毫秒是合理的既留了足够的余量又不会让轮询周期太长。如果你挂的从站比较多比如 10 台以上那轮询周期会相应变长这时候可以考虑适当减小间隔比如 50 毫秒但前提是你要确认从站的响应时间足够快。5. 常见问题速查与避坑经验5.1 通讯不稳的常见原因排查表现象可能原因排查方法解决方案完全无响应接线错误、端口配置错误检查 A/B 线是否接反波特率是否一致调换 A/B 线统一波特率间歇性丢包轮询间隔不足、总线冲突抓报文看丢包规律增加轮询间隔加定时器延时数据错乱CRC 校验错误、寄存器地址偏移检查 CRC 算法核对寄存器地址修正 CRC 计算调整地址偏移通讯距离短终端电阻缺失、线材质量差检查终端电阻测量线材阻抗加 120 欧姆终端电阻换屏蔽双绞线干扰严重屏蔽层未接地、走线靠近干扰源检查屏蔽层接地观察走线路径屏蔽层单端接地远离变频器5.2 实操心得这些坑我替你踩过了第一个坑寄存器地址偏移。很多设备手册上写的寄存器编号是从 1 开始的比如 40001、40002但 Modbus 协议里是从 0 开始的。你填 40001实际要填 0填 40002实际要填 1。这个偏移量搞错了要么读不到数据要么读到的全是乱七八糟的值。我一般建议先在串口调试工具里手动发一条报文确认地址对了再写程序。第二个坑CRC 字节序。Modbus RTU 的 CRC 是低字节在前、高字节在后。如果你在程序里自己算 CRC一定要注意这个字节序。我见过有人算对了 CRC 值但发送的时候高字节和低字节搞反了结果从站一直不响应。第三个坑超时时间设太短。有些朋友为了追求“实时性”把超时时间设得很短比如 10 毫秒。结果从站稍微慢一点就超时主机频繁重发总线负载飙升反而更不稳定。我一般建议超时时间至少设 100 毫秒给从站足够的响应时间。第四个坑忽略 3.5 字符间隔。虽然 PLC 的扫描周期通常大于这个间隔但如果你用高速计数器或者中断来触发 ADPRW 指令就有可能违反这个规则。保险起见还是在两条指令之间加一个定时器延时。第五个坑终端电阻乱加。终端电阻不是越多越好也不是所有场合都必须加。总线长度小于 50 米、波特率低于 38400 的情况下不加终端电阻通常也能跑。但如果通讯不稳加一个 120 欧姆的终端电阻在总线两端往往能立竿见影。5.3 不同品牌 PLC 的 Modbus RTU 实现差异虽然 Modbus RTU 是标准协议但不同品牌的 PLC 在实现上还是有些差异这里简单对比一下品牌指令/功能块特点注意事项三菱 FXADPRW直接指定从站地址、功能码、寄存器地址地址从 0 开始注意偏移西门子 S7-200 SMARTMBUS_MSG需要先调用 MBUS_CTRL 初始化轮询需要自己写状态机汇川Modbus 指令库封装程度高参数配置简单注意寄存器地址映射信捷MDBUS 指令支持主从模式固件版本不同可能有差异不管用哪个品牌核心逻辑都是一样的初始化端口、轮询发送、处理响应、超时重发。把这四步做扎实了通讯稳定性就不会有大问题。6. 从根上解决程序架构的优化建议6.1 用状态机代替线性轮询很多新手写轮询程序就是一条接一条地写 ADPRW 指令中间用完成标志位串联。这种写法在从站少的时候没问题但从站一多程序就会变得又长又乱排查问题也很麻烦。我建议用状态机的方式来写。定义一个状态寄存器比如 D200用不同的值表示不同的轮询步骤D200 0空闲D200 1读从站 1 当前温度D200 2读从站 1 设定温度D200 3读从站 2 当前温度D200 4读从站 2 设定温度D200 5轮询完成回到 1然后用比较指令来判断当前状态触发对应的 ADPRW 指令。这种写法结构清晰扩展方便加一个从站只需要加一个状态值不用改太多代码。6.2 加入通讯质量统计在实际项目中我习惯加一个简单的通讯质量统计功能。比如用两个寄存器分别记录“总请求次数”和“失败次数”每次轮询都累加失败的时候额外累加失败次数。这样在 HMI 上就能直观地看到通讯质量方便提前发现隐患。具体实现每次触发 ADPRW 指令时INC 一个寄存器比如 D300如果超时或者错误INC 另一个寄存器比如 D301。然后在 HMI 上显示 D301/D300 的比值正常应该在 1% 以下。如果超过 5%就要检查通讯参数或者硬件连接了。6.3 超时重发机制的设计ADPRW 指令本身没有重发功能如果从站没响应指令会一直等待直到超时。我一般会在程序里加一个超时计数器比如用 T200 定时 200 毫秒如果 ADPRW 指令在 200 毫秒内没有完成就强制复位当前状态重新触发这条指令。重发次数不要太多一般 2 到 3 次就够了。重发太多次会拖慢整个轮询周期反而影响实时性。如果连续重发 3 次都失败那就跳过这个从站继续轮询下一个同时记录一个错误标志在 HMI 上报警。7. 写在最后的一些个人体会这个案例说到底问题并不复杂就是轮询间隔没留够。但为什么客户换了那么多硬件都没解决因为大家习惯性地把“通讯不稳”归咎于硬件觉得软件是“死”的不会变。实际上Modbus RTU 这种主从轮询的通讯方式软件层面的时序控制才是稳定性的关键。我干了这么多年工控总结下来就一句话通讯问题先看协议再看时序最后才看硬件。协议决定了你能不能通时序决定了你稳不稳硬件决定了你跑多远。三者缺一不可但排查顺序不能乱。另外别迷信“标准配置”。别人说 19200 能跑 100 米你现场可能 30 米就不稳别人说不用加终端电阻你现场可能不加就丢包。每个现场的环境都不一样干扰源、线材质量、接地情况都会影响通讯质量。遇到问题老老实实抓报文、看时序、调参数比换硬件管用得多。最后再分享一个小技巧如果你手头没有串口调试工具可以用 PLC 的在线监控功能观察 ADPRW 指令的完成标志位和错误标志位。三菱 FX 系列的 M8029 是指令完成标志M8063 是通讯错误标志。如果 M8063 频繁置位说明通讯确实有问题如果 M8029 一直不置位说明从站根本没响应。这两个标志位能帮你快速缩小排查范围。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价