资讯详情

LabVIEW与三菱FX系列PLC以太网MC协议通讯完整实现

📅 2026/10/21 8:24:55 | 华诺云谱 👁 阅读
LabVIEW与三菱FX系列PLC以太网MC协议通讯完整实现
最近在做某产线监控项目时碰到了“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 字节一般设 00PC 号1 字节一般设 FF表示直接指定目标 PLC请求目标模块 IO 编号2 字节CPU 模块一般用 03 FF请求目标模块站号1 字节一般设 00请求数据长度2 字节小端模式指监视定时器到数据部分的字节数监视定时器4 字节发送后等待响应的时间单位默认 250ms0x10 大概 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网络号单机连接直接给 00FFPC 号给 FF 表示不需要路由表FF 03IO 编号指 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 字节都是 00A8软元件代码0xA8 对应 D 寄存器0A 00读取点数0x000A 10 点把这 24 个字节发给 PLCPLC 就会把 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 协议里访问哪个软元件靠“软元件代码 起始软元件编号”两个字段决定。下面是我项目里真正用到的几个常用代码软元件代码说明X9C输入继电器八进制编号Y9D输出继电器八进制编号M90内部继电器十进制编号L92锁存继电器DA8数据寄存器字单位RAF文件寄存器WB5链接寄存器地址换算最需要注意的是 X/Y。FX 系列的 I/O 编号是八进制不是十进制但 MC 协议里的“软元件编号”又按特定规则映射。比如 X0 对应地址 0x0000X7 对应 0x0007X10 并不是十进制 10 而是八进制的 10也就是十进制 8对应的地址是 0x0008。这个换算关系极其容易错我在调试时专门写了一张对照表宁可在程序里查表换算也不敢直接用“字符串转数字”的简单方式。M 继电器相对友好基本就是十进制编号直接转十六进制比如 M100 就是 0x0064M8000 对应的就是 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 还是 0x0064MC 协议的 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 写入 0x0064100往 D1 写入 0x00000写入点数为 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 个字把“连接、组帧、发送、读取、解析、显示”这条小链路完整跑通。这些小链路虽然看起来不起眼但它是整个通讯程序的地基地基稳了后面扩展批量写入、远程启停、自动重连都会很顺手。另外调试阶段记得把每一帧发送报文和接收报文都打印在界面上留着这个“日志开关”以后现场出了问题你能省下大量靠猜的时间。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑