最近在做某产线监控项目时,碰到了“LabVIEW 上位机要直接读三菱 FX 系列 PLC 数据”的需求。设备侧用的是 FX3U 加一块以太网扩展模块,上位机是 LabVIEW,现场没有中间网关,也没有现成 OPC 服务器,所有通讯都得靠上位机自己把 TCP 链路拉起来。当时第一反应是想直接走 Modbus TCP,结果翻了一圈手册才发现:FX3U 的以太网模块并不支持标准的 Modbus TCP 服务端,真正能稳走 TCP 的原生协议是三菱的 MC 协议,也就是 SLMP。这篇文章就把我用 LabVIEW 实现三菱 FX 系列以太网 MC 协议通讯的完整过程拆开讲一遍,包括 3E 帧格式、报文构造、LabVIEW 程序设计、现场调试踩坑,以及批量写入、远程启停等扩展指令的写法,给同样要做 PLC 数据对接的朋友做个参考。
1. 为什么绕不开 MC 协议:FX 系列以太网通讯的选型思路
1.1 FX 系列 PLC 接入上位机的几种常见方式
做工业上位机开发的人都知道,PLC 和上位机通讯的路子大致分三类:一是通过 OPC 服务器中转,二是走 PLC 厂商私有协议,三是把 PLC 当普通 TCP/串口设备直接读写。
对于三菱 FX 系列,第一种方式最省心,比如用某个品牌的 OPC 网关软件,配置好变量表后,LabVIEW 只需要作为一个 OPC 客户端去订阅数据。但问题也很现实:OPC 授权要钱,配置繁琐,现场如果频繁改点位,每次都要重新导变量表,调试效率不高。第二种方式在我看来才是最可控的,也就是直接用三菱自己的 MC 协议通过以太网通讯。第三种最直接,但 Modbus TCP 在 FX 系列上的支持非常有限,FX3U 的以太网模块根本没有原生的 Modbus TCP 服务端功能,FX5U 倒是能支持一部分,但很多功能仍然是 MC 协议更顺手。所以最后我选择了 MC 协议这条路线。
还有一个关键点:LabVIEW 自带的 TCP 函数非常成熟,TCP Open Connection、TCP Write、TCP Read 这套函数已经足够完成底层通讯。MC 协议本质上就是“请求帧 + 响应帧”的结构化数据,不需要额外驱动,也不需要 DLL,只要自己拼接字节、解析字节就行。对一个 LabVIEW 开发者来说,这反而比装一堆中间件更透明。
1.2 MC 协议是什么,它在三菱 PLC 生态里的位置
MC 协议全称 MELSEC Communication Protocol,是三菱 PLC 家族通用的通讯协议。注意,它不只是 FX 系列在用,Q 系列、L 系列、iQ-R 系列、FX5U 等都能用,只是不同系列支持的软元件范围和帧格式略有差异。以太网环境下最常用的是 QnA 兼容 3E 帧,而 FX3U 配合 FX3U-ENET 或 FX3U-ENET-ADP 这类模块时,走的也正是 3E 帧。
3E 帧在行业内已经不算小众知识了,很多做三菱数据采集的工程师都在用。我第一次接触时也觉得报文挺复杂,但真正把每个字节拆解以后会发现,它无非就是“帧头 + 访问目的地 + 长度 + 命令 + 数据”,比串口那套 4C 帧还要简单,甚至没有校验码,因为 TCP 本身已经保证了传输层的可靠性。相比 Q 系列,FX 系列的区别主要在软元件地址的映射规则上,比如 D 寄存器、M 继电器的地址编码,这一点后面我会重点展开。
1.3 什么情况下必须用 MC 协议
如果你的设备是 Q 系列且用了标准以太网模块,那 3E 帧几乎是唯一选择。FX5U 内置以太网口则比较灵活,它支持 SLMP 和 Modbus TCP 两种方式,但如果你要读 FX 系列特有的一些软元件状态、要远程 RUN/STOP,或者要批量读特殊继电器,Modbus TCP 根本提供不了这些服务,最后还是得回到 MC 协议。
我在项目里的总体结论是:如果上位机是 LabVIEW,通讯对象是三菱 FX 系列,且没有现成网关中转,那就直接上 MC 协议。虽然首帧组帧要自己写,但整体代码结构可以做成通用模块,以后换 PLC 型号只需要改软元件地址映射表。
2. 3E 帧逐字节拆解:一帧请求和一帧响应到底长什么样
2.1 二进制 3E 帧的骨架
MC 协议 3E 帧分为 ASCII 帧和二进制帧两种形式。ASCII 帧肉眼可读,但字节数几乎是二进制帧的两倍,解析也费劲。我在实际项目里全部用二进制帧,程序里用十六进制字符串或字节数组组装,调试时也能抓包比对。
二进制 3E 帧的请求帧骨架如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| 帧头 | 2 字节 | 固定为 D0 D0 |
| 网络号 | 1 字节 | 一般设 00 |
| PC 号 | 1 字节 | 一般设 FF,表示直接指定目标 PLC |
| 请求目标模块 IO 编号 | 2 字节 | CPU 模块一般用 03 FF |
| 请求目标模块站号 | 1 字节 | 一般设 00 |
| 请求数据长度 | 2 字节 | 小端模式,指监视定时器到数据部分的字节数 |
| 监视定时器 | 4 字节 | 发送后等待响应的时间,单位默认 250ms,0x10 大概 4 秒 |
| 命令 | 2 字节 | 例如 04 01 是批量读取(字单位) |
| 子命令 | 2 字节 | 一般 00 00 |
| 数据 | 可变 | 取决于具体命令 |
请求数据长度这个字段很多人一开始容易算错。规则是:从监视定时器开始,一直到数据部分结束的总字节数。以批量读取为例,监视定时器 4 字节 + 命令 2 字节 + 子命令 2 字节 + 数据 7 字节,算下来正好是 15 字节,也就是 0x0F,小端存放就是 0F 00。
2.2 批量读取请求帧:从 D0 开始读 10 个字
我项目里使用频率最高的就是批量读取,比如读取 D0 到 D9,共 10 个字。对应的二进制请求帧如下,十六进制逐字节写出来就是:
D0 D0 00 FF FF 03 00 0F 00 10 00 00 00 01 04 00 00 00 00 00 00 A8 0A 00
我来一行行拆:
- D0 D0:帧头
- 00:网络号,单机连接直接给 00
- FF:PC 号,给 FF 表示不需要路由表
- FF 03:IO 编号,指 CPU 模块
- 00:站号
- 0F 00:请求数据长度,0x000F = 15,小端序
- 10 00 00 00:监视定时器,0x00000010 = 16 × 250ms = 4 秒
- 01 04:命令,0x0401,表示批量读取,字单位。注意小端,低字节 01 在前
- 00 00:子命令
- 00 00 00 00:起始软元件编号,这里是 D0,所以 4 字节都是 00
- A8:软元件代码,0xA8 对应 D 寄存器
- 0A 00:读取点数,0x000A = 10 点
把这 24 个字节发给 PLC,PLC 就会把 D0 到 D9 的值返回来。注意这里的命令字段是小端存放,所以你会看到 01 04 而不是 04 01。很多人第一次看别人抓包报文时觉得“怎么命令反了”,其实就是小端序。
2.3 响应帧结构与结束代码
正常响应帧的结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| 帧头 | 2 字节 | D0 D0 |
| 访问目的地 | 5 字节 | 网络号、PC 号、IO 编号、站号 |
| 响应数据长度 | 2 字节 | 小端,指结束代码到数据部分的字节数 |
| 结束代码 | 2 字节 | 0000 表示正常 |
| 读取的数据 | 可变 | 每个字 2 字节,小端 |
对于上面的请求,响应数据长度 = 结束代码 2 字节 + 10 个字 × 2 字节 = 22 字节,也就是 0x0016,小端为 16 00。数据部分则是 D0 到 D9 各 2 字节,低字节在前。
比如 PLC 返回的响应帧可以是:
D0 D0 00 FF FF 03 00 16 00 00 00 64 00 00 00 0A 00 ……
这里结束代码是 00 00,说明正常,紧接着的 64 00 就是十六进制 0x0064,换算成十进制是 100,表示 D0 的当前值是 100。后面的字节就依次对应 D1、D2,直到 D9。
如果结束代码不是 0,例如常见的 0x1080 表示软元件地址范围非法,0x1000 表示目标站不可达,那就说明请求帧有问题或者 PLC 侧配置不对。我在排查地址写错的情况时,几乎都是通过结束代码快速定位的。
2.4 软元件代码与地址换算规则
MC 协议里访问哪个软元件,靠“软元件代码 + 起始软元件编号”两个字段决定。下面是我项目里真正用到的几个常用代码:
| 软元件 | 代码 | 说明 |
|---|---|---|
| X | 9C | 输入继电器,八进制编号 |
| Y | 9D | 输出继电器,八进制编号 |
| M | 90 | 内部继电器,十进制编号 |
| L | 92 | 锁存继电器 |
| D | A8 | 数据寄存器,字单位 |
| R | AF | 文件寄存器 |
| W | B5 | 链接寄存器 |
地址换算最需要注意的是 X/Y。FX 系列的 I/O 编号是八进制,不是十进制,但 MC 协议里的“软元件编号”又按特定规则映射。比如 X0 对应地址 0x0000,X7 对应 0x0007,X10 并不是十进制 10 而是八进制的 10,也就是十进制 8,对应的地址是 0x0008。这个换算关系极其容易错,我在调试时专门写了一张对照表,宁可在程序里查表换算,也不敢直接用“字符串转数字”的简单方式。
M 继电器相对友好,基本就是十进制编号直接转十六进制,比如 M100 就是 0x0064,M8000 对应的就是 0x1F40。D 寄存器也简单,D100 就是 0x0064。但我建议在代码里统一封装一个“软元件名转地址”的子函数,后续加点位时只改参数,不用动报文逻辑。
3. LabVIEW 端实现:连接、组帧、收发与解析
3.1 程序总架构与前面板设计
LabVIEW 里的整体思路其实很朴素:建立 TCP 连接,进主循环,循环里按顺序“组帧 → 发送 → 读响应 → 解析 → 更新显示”,然后延时,再做下一轮。这个结构对轮询式数据采集非常合适,和传统 PLC 扫描的工作方式也匹配。
前面板我建议至少放这些控件:
- IP 地址输入框、端口号输入框
- 连接按钮、断开按钮
- 软元件类型下拉框,比如 D、M、X、Y
- 起始地址输入框、读取点数输入框
- 数据表格或数值数组显示区
- 连接状态指示灯、错误信息提示区
- 轮询周期输入框,默认 100ms
程序框图大致是:布尔按钮触发 TCP Open Connection,获得 TCP 连接引用后进入 While 循环。在循环里先构造请求帧,用 TCP Write 发送,然后用 TCP Read 按响应的长度读取,解析出来更新数据表格。循环外放一个 TCP Close Connection 和错误处理。
3.2 帧构造的两种办法
LabVIEW 不像 C 语言那样直接操作字节数组,但组帧的方式其实很多。我自己常用的是“十六进制字符串 → 字节数组转换”的方式:先在字符串常量或格式化字符串里拼出完整的十六进制文本,比如“D0D000FFFF03000F00100000000104000000000000A80A00”,再用“Hex String To String”函数转成字节流,最后交给 TCP Write。这种方式的好处是报文内容肉眼可见,调试时能直接跟抓包工具比对。
另一种更严谨的方法是用字节数组常量配合“Build Array”函数,把头部、软元件代码、地址、点数一个个拼接起来。这种方式适合要动态计算长度和地址的场景。地址和点数需要转成小端序时,可以用“Number To U16/U32”函数再配合字节交换,或者手动左移右移都能实现。我更推荐第二种,因为程序逻辑清晰,也不容易出现十六进制字符串因为漏了一个空格导致组帧错误的情况。
如果你图省事,跟我一样先拼字符串再转,一定记得小端序的字节顺序。比如 D123 的地址 0x007B,在字符串里要写成“7B00”,不是“007B”。这点写错了,报文发出去 PLC 会直接给你回一个地址范围错误。
3.3 发送与读取时序:TCP 是流不是包
TCP 通讯里最容易踩的坑就是“粘包”和“半包”。PLC 返回的响应帧虽然是一个数据包,但 TCP 作为流协议,LabVIEW 的 TCP Read 一次不一定能读全,比如定义了读 24 字节,结果一次只返回了 7 字节,或者上次响应还没读完,下次读又把旧数据读进来了。
我的处理方式是把读取拆成固定头部和可变数据两部分:
- 先读 7 字节,包含帧头和访问目的地
- 再读 2 字节,得到响应数据长度
- 再读响应数据长度指定的字节数,包含结束代码和数据部分
- 检查结束代码,再解析数据
这样每一步读多少字节都是明确的,不会因为一次 recv 数量不对而出错。在 LabVIEW 里可以用循环 + TCP Read 实现“读满指定字节再退出”的子函数,这个子函数基本是所有 MC 通讯程序的公共基础。
发送端则简单很多,TCP Write 一次性把整个帧写出去就行。需要注意的是不要每轮都重新 Open Connection,我见过有人把 TCP 连接放在循环里面,每读一次数据就断开重连一次,现场通讯速度被拖得很惨。连接应该只在初始化时建一次,异常断开了才重连。
3.4 解析规则:大小端转换与有符号数处理
响应帧里每个字是 2 字节,小端存放。比如收到的数据是 64 00,在 LabVIEW 里把这两字节还原成数值,要按“低字节 + 高字节 × 256”来算,也就是 0x0064 = 100。如果你直接用“String To Word”函数去解析,LabVIEW 默认是常规的大端方式,算出来的结果就会变成 0x6400 = 25600,数字完全对不上。
我一般先取出数据部分的字节数组,每两个字节合成一个 U16,再做一次字节逆序。LabVIEW 里可以用“Swap Bytes”函数,也可以先转成 U16 数组,再对每个元素做字节交换。更省事的是在读取配置时就把 PLC 侧数据格式约定为无符号整数,然后在 LabVIEW 端按 U16 显示。
如果碰到 32 位数据或者浮点数,比如某个工艺参数用 D100、D101 两个寄存器组成一个 32 位浮点,还得额外处理高字与低字的顺序。三菱 PLC 保存浮点数时一般是低地址放低 16 位,高地址放高 16 位。用 LabVIEW 读取时需要把两个 U16 按正确顺序拼成 U32,再用“数制转换”恢复成单精度浮点。这一块最容易出现“数值很大但不合理”的情况,基本就是高低字顺序反了。
4. 现场最容易踩的坑:连接失败、数据错位与粘包
4.1 PLC 参数和端口不一致导致的连接失败
第一次在现场联调时,我最先遇到的就是 TCP 都连不上。排查半天发现并非 LabVIEW 程序问题,而是 PLC 侧的以太网端口没有设置成“MC 协议可连接”。FX3U 的以太网模块在第一次使用时默认的端口和协议可能不是 MC 协议,需要在三菱的组态软件里把通讯协议设为“MC 协议(3E 帧)”,并固定好端口号。
很多模块默认端口各不相同,我习惯的做法是手动指定一个固定端口,比如 6000,然后在 PLC 参数和 LabVIEW 前面板里都填这个端口。如果 PLC 侧已经把端口改成了别的值,上位机还用默认端口去连,TCP 握手都完不成。还有一个细节:PLC 的 IP 地址要保证和上位机在同一网段,如果现场网络有 VLAN 或者防火墙,LabVIEW 发出去的 SYN 包都到不了 PLC。
有个技巧是我后来沉淀下来的:连接之前先用 Windows 自带的 ping 命令确认 IP 通不通,再用一些 TCP 调试助手手动连一次 PLC 的 MC 端口,看看能不能建立连接。如果调试助手都连不上,那就不是 LabVIEW 程序的问题,先把网络和 PLC 参数解决好再说。
4.2 地址换算和字节序的双重错位
数据读出来了,但数值离谱,比如 D10 读出来是 25600 而不是 100,这里通常就是字节序问题。前面已经说了,MC 协议返回的数据是低字节在前,LabVIEW 解析时必须交换字节顺序。这个问题在我带过的好几个项目里都出现过,几乎每个第一次写 LabVIEW 通讯程序的人都会在这里栽一次。
另一种情况是软元件地址写错。比如想读 M100,结果报文里写成了 0x0100 还是 0x0064?MC 协议的 M 继电器地址并不是简单“编号直接写进去”这么毫无争议,它与 PLC 型号和软元件区段都有关系。我在 FX3U 上验证过,M100 确实对应 0x0064。但到了 FX5U 上,如果打开 MC 协议应用层的访问范围限制,地址可能有偏移。遇到地址问题,我的建议是不要靠猜,先抓包看 PLC 返回的结束代码,然后查手册里的软元件地址编码表。
4.3 一次收不全,响应帧被切断
读取大块数据时最容易遇到“半包”问题。比如一次读 100 个 D 寄存器,响应数据长度是 2 + 200 = 202 字节,加上 9 字节头部,总共 211 字节。如果上位机只读一次,TCP Read 可能只收到一部分,比如 100 字节,那解析出来的数据就残缺了。
我的处理方式上面已经提到,就是严格按“先读固定头,再按长度读剩余”的方式。在 LabVIEW 里写一个“TCP Read N Bytes”子 VI,循环调用 TCP Read,直到累计读取的字节数达到 N 才返回。这个子 VI 是所有 MC 通讯程序的关键基础设施,因为不管是读 20 字节还是读 200 字节,流程都是统一的。
还有另一个极端是“粘包”,如果上一轮的响应数据没有读完,下一轮的读取会把旧数据读进来。这个问题在轮询程序中特别隐蔽,因为看起来数据是连续的,但仔细比对会发现数值不对位。解决思路也很简单:每一轮读完响应后,确保没有残留数据,或者每轮都按响应长度精确读满,不盲目多读。
4.4 批量读取的长度和软元件单位
MC 协议中,批量读取命令分“字单位”和“位单位”。读取 D 寄存器时用 0x0401 字单位,读取 M、X、Y 这类位软元件时可以用 0x0402 位单位。我一开始图省事,想用字单位去读 M 继电器,结果发现点位错位非常严重。
原因是位软元件按字单位读取时,每个“字”对应的是 16 个位点,MC 协议的地址是按“字地址”递增的,不是按“位编号”递增的。比如 M0 到 M15 可以打包读成一个字,M16 到 M31 是下一个字。虽然这样读的效率更高,但解析时你得把 16 个位拆开,还得知道哪一位对应哪个 M 编号。对于 LabVIEW 来说,程序里加一个“位拆包”子 VI 也不难,但第一次做的人很容易搞混。
我个人在项目里的经验是:如果只是监控几十个 M 点,直接用位单位逐点批量读,省心;如果点位很多,比如上百个 M 点,再考虑字单位合并读取,但前提是按 16 位对齐做拆分。两者各有优劣,取决于你的点位布局是否规整。
5. 从读扩展到写:批量写入、远程启停与轮询节奏
5.1 批量写入帧结构
只读不写的数据采集程序毕竟少见,很多现场需求是“不但要把数据读上来,还要把参数写下去”,比如设定温度、下发运行速度。MC 协议批量写入命令是 0x1401,字单位。请求帧和读取帧的差异主要在数据部分。
写入 D0 到 D9 的请求帧格式如下:帧头、访问目的地、请求长度、监视定时器、命令 14 01、子命令 00 00,然后依次放起始地址、软元件代码、写入点数,最后是每个要写入的数值,每个值 2 字节小端。注意请求数据长度字段这时候要大一些,因为多了具体写入的数据。
我实际用的写入 D 寄存器示例,请求帧十六进制可以是:
D0 D0 00 FF FF 03 00 1D 00 10 00 00 00 01 14 00 00 00 00 00 00 A8 02 00 64 00 00 00
这个帧的含义是往 D0 写入 0x0064(100),往 D1 写入 0x0000(0),写入点数为 2。请求长度为监视定时器 4 + 命令 2 + 子命令 2 + 数据部分(地址 4 + 代码 1 + 点数 2 + 实际数据 4) = 29 字节,即 0x001D。
写操作比读操作要谨慎。现场调试时我一般先在界面上加一个“写允许”开关,默认关闭,只有确认要下发参数时才打开。这个开关从软件层面避免了误写导致设备异常动作。
5.2 远程 RUN/STOP 的实现思路
MC 协议支持远程 RUN 和远程 STOP,命令分别是 0x1001 和 0x1002。这两个命令在某些场景下非常实用,比如产线交接时需要远程让 PLC 进入停止状态,或者远程启动程序运行。
远程 RUN 的请求帧结构相对简单,命令 0x1001,子命令一般 00 00,数据部分是指定 RUN 模式。需要注意的是远程 RUN 命令通常会要求 PLC 处于允许远程操作的配置状态,如果 PLC 参数里禁用了远程 RUN,即使报文正确,PLC 也会返回错误代码。我一个朋友就遇到过类似情况,他觉得上位机发命令没反应,最后查出来是 PLC 侧把远程 RUN 功能关掉了。
在 LabVIEW 里实现远程启停特别适合用按钮控件触发,按下按钮后发一帧命令,不需要像数据采集那样一直轮询。发送完判断一下结束代码,然后更新状态显示就行。
5.3 轮询频率和通信负载的平衡
轮询采集时有一个很现实的问题:读得越频繁,数据越实时,但 PLC 的负担也越大。FX3U 处理 MC 请求的速度并不算快,一个请求从接收到响应通常要几毫秒到十几毫秒,如果上位机用 10ms 的周期去轮询,很可能出现请求排队、响应超时。
我的经验是:普通数据采集轮询周期取 100ms 完全够用,如果监控画面要求较高,可以缩短到 50ms。对于需要快速响应的点,比如报警信号,可以单独用位单位快速读取,而不是每次都把上千个 D 寄存器全读上来。还有一个优化手段是“分组轮询”,把不同的软元件拆成几个请求,循环里错开时间发送,避免同一时刻发送大量请求造成 PLC 通信拥堵。
LabVIEW 的延时函数是现成的,但要注意延时位置,应该放在一轮“请求-响应-解析”完成之后。如果放在发送之前,发送频率会被延时节拍影响,读上来的数据实时性就不均匀。
6. 调试与交付:我在这套程序里沉淀的几个习惯
6.1 抓包与比对工具的选择
MC 协议通讯调试不比写代码轻松,报文错了,光靠肉眼看十六进制字符串容易眼花。我调试时习惯用网络抓包工具,把 LabVIEW 与 PLC 之间的 TCP 数据完整抓下来,再逐个字节比对帧格式。
抓包看报文有个明显的好处:能直接看到请求帧和响应帧的实际字节,跟自己在程序里组装的帧做对比,很容易发现是地址算错、长度计算错还是字节序写反。我的习惯是先抓一次正常运行的报文作为参照,再对比异常报文,这样定位问题的速度快得多。
如果没有抓包工具,也可以用一些小型的 TCP 调试助手,手动发送一帧读取请求,看看 PLC 返回什么。如果返回的结束代码是地址错误,那就能确定问题在组帧的地址段;如果返回正确数据,那问题大概率出在 LabVIEW 程序的解析部分。
6.2 自动重连与状态指示
工业现场最麻烦的并不是正常通讯时的数据处理,而是网络瞬断后的恢复。PLC 偶尔断电重启、网线插头松动、交换机重新上电,这些都会导致 TCP 连接断开。如果 LabVIEW 程序没有重连机制,整条产线监控就直接瘫痪。
我在程序里加了重连状态机:连接成功后进入轮询循环;如果某次读取发生超时或连接错误,先关闭当前 TCP 连接,然后每隔 1 秒重试连接,重连次数超过了再加大间隔。前面板上的状态指示灯也做了三级显示:灰色表示未连接、绿色表示连接正常、红色表示通讯异常。
特别是在 PLC 断电重启的场景中,如果上位机一直用旧连接写数据,写入命令会全部超时,界面上又看不到明确提示,现场操作员很容易误以为设备没响应。有了状态指示,人机交互就直观多了。
6.3 后续扩展:从 FX 到其他三菱系列的平滑过渡
这套程序我并没有只做成“只针对 FX3U 的专属代码”,而是把组帧、解析、地址换算封装成了几个独立模块。软元件代码、起始地址、命令类型全部作为参数传入,后面如果要接 Q 系列或者 L 系列,只需要调整软元件地址映射表和 IO 编号,核心逻辑完全不用动。
FX 系列和 Q 系列在 3E 帧上的主要差异就是“请求目标模块 IO 编号”和软元件地址的分配规则。Q 系列里 D 寄存器的地址范围比 FX 大得多,M 软元件的地址分段也不太一样。但只要通讯底层是同一套 3E 帧,LabVIEW 程序的整体架构完全可以跨系列复用。
我在实际项目里就是这么做的:一套 LabVIEW 上位机程序,接了一条产线的 FX3U,后来又接了另一条产线的 Q 系列 PLC,改动的工作量只集中在配置文件里的软元件表,并没有重新写通讯底层。
最后再分享一个我个人很喜欢用的调试习惯:第一次联调时不要急着把程序模块做得很大很完整,而是先用一个最简单的 VI,只读 D0 到 D9 这 10 个字,把“连接、组帧、发送、读取、解析、显示”这条小链路完整跑通。这些小链路虽然看起来不起眼,但它是整个通讯程序的地基,地基稳了,后面扩展批量写入、远程启停、自动重连都会很顺手。另外,调试阶段记得把每一帧发送报文和接收报文都打印在界面上,留着这个“日志开关”,以后现场出了问题,你能省下大量靠猜的时间。