news 2026/9/13 5:54:19

C#/VB与三菱FX5U PLC通过SLMP协议实现以太网通讯交互

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#/VB与三菱FX5U PLC通过SLMP协议实现以太网通讯交互

简介:这套源码采用C#与VB.NET编写,面向三菱FX5U可编程控制器的上位机通讯交互,专为需要将个人电脑与控制器对接的开发者打造,尤其适用于自动化设备的调试与数据采集场景。方案基于TCP协议,支持整数、双整数与浮点数的读写,并提供ASCII和二进制两种报文格式,能适配不同现场设备的通讯习惯,降低集成难度,方便不同经验水平的开发人员快速上手。压缩包内共有95个文件,以VB与C#源文件、动态链接库组件、可执行程序为主,另含项目解决方案、配置文件以及接口说明文档,整个压缩包只有681KB,结构清晰,各类型文件用途明确。目前已有1544人学习下载,内容获得用户初步验证。两个工程版本均可直接复制到项目中使用,省去安装配置;借助配套的接口说明,开发者可以快速理解FX5U的通讯机制,显著缩短报文格式调试与系统联调周期。

1. 为什么 C#/VB 与 FX5U 的通讯交互先看 SLMP 而不是 Modbus

三菱 FX5U 作为当前中小型项目里出现频率极高的 PLC,上位机要跟它建立通讯交互,选协议是第一道坎。很多人一上来就问“FX5U 支持 Modbus TCP 吗”,支持,FX5U 内置以太网口确实可以配置成 Modbus TCP 从站,但实际做项目时你会发现 Modbus 能读到的数据范围受限,而且保持寄存器和线圈的地址映射要按三菱的缓冲存储区规则重新算一遍,绕了一圈还不如直接用三菱自家的 SLMP 协议(Seamless Message Protocol,即 MC 协议以太网版)。

SLMP 是 FX5U 原生支持的以太网通讯协议,不需要额外配置 Modbus 映射表,直接按软元件编号(D、M、X、Y、R、ZR 等)读写。C# 和 VB 作为 .NET 平台的两门语言,写 TcpClient 连接 PLC 的端口号 1024(FX5U 默认的 SLMP 端口),组帧发命令、收响应、解析返回数据,整个流程下来代码量不大,但细节非常多。字节序、帧头、CPU 编号、双字节和校验、批量读写的点数限制,这些才是真正决定能不能一次跑通的关键。

本文适合已经在做或者准备做上位机开发的人,尤其是 C#/VB 和 PLC 之间的通讯交互总是出现“能连上但数据不对”“读写偶尔超时”这类问题的场景。下面从协议选型开始,给出可直接套用的 C# 和 VB 源码,再展开数据转换和排错手段。

2. 通讯交互的协议与参数:FX5U 的 SLMP 帧和寻址规则

2.1 FX5U 的以太网通讯交互方式有哪些

FX5U 本体带一个以太网口,支持 SLMP、Modbus TCP、Socket 通讯和 CC-Link IE Field Basic。做上位机时,SLMP 是效率最高的选择,因为它直接面向软元件,不需要在 PLC 侧做专门的通讯程序,只要在 GX Works3 里启用“简单 CPU 通讯”功能并开放端口即可。

重要概念是 FX5U 实际上分 TCP 服务器模式和 TCP 客户端模式。上位机主动连接时,PLC 作为服务器监听 1024 端口,这是最常见的“C#/VB 主动连接”方式。反过来如果 PLC 做客户端主动上报,就用不上 SLMP 的请求-响应模型,需要走 Socket 通讯自己定报文,这个不在本文范围内。

SLMP 帧格式有两种:三菱传统的 3E 帧(二进制帧)和 4E 帧(ASCII 帧)。上位机几乎都用 3E 帧,因为长度短、解析快。需要特别注意的是,FX5U 的 3E 帧和 Q 系列 PLC 的 3E 帧在寻址上是兼容的,但请求数据长度的计算方式略有差异,这点在写代码时要注意。

2.2 SLMP 3E 帧格式逐字节拆解

3E 帧请求报文格式如下:

字段长度说明
帧头1 字节0x50
请求数据长度2 字节(低位在前)从“子帧头”开始到报文末尾的字节数
子帧头2 字节0x00 0xFF
网络编号1 字节通常 0x00
PC 编号1 字节通常 0xFF
请求目标模块 IO 编号2 字节0x03 0xFF(FX5U 固定值)
请求目标模块站号1 字节通常 0x00
请求数据长度2 字节(低位在前)从“命令”开始到末尾的字节数
命令2 字节0x01 0x04 读;0x01 0x14 写
子命令2 字节0x00 0x00(按字访问)
软元件编号3 字节见 2.3 节
软元件点数2 字节(低位在前)1~960(字访问时)
数据(写时)不定每个字 2 字节,低位在前

读命令没有“数据”字段。写命令要在这个位置把要写入的数值带上。

有一个高频踩坑点是“请求数据长度”这个字段,它包含子帧头到末尾的所有字节,但不包含帧头和它自己这 3 个字节。很多第一次写的人会把整个报文长度算进去,导致 PLC 返回错误码。另外 FX5U 的请求目标模块 IO 编号固定填 0x03 0xFF,这是 FX5U 和 Q 系列不一样的地方,Q 系列用的是 0x00 0x00。

需要注意,这个报文的帧头以0x50开头,这也是为什么网上很多文章直接用“50 00 FF FF FF 03 00”开头来识别 SLMP 报文。知道了这个规则,用 Wireshark 抓包时就能快速确认报文的正确性。

2.3 三菱 FX5U 软元件编号的 24 位编码规则

SLMP 报文中的软元件编号是 3 字节(24 位),不是简单的十进制转十六进制。它把十进制编号按位拆分,每个十进制数字占 4 位二进制,再加上软元件代码填充到高字节。

以 D100 为例,D 类软元件代码是 0xA8。100 的每个十进制位分别是 1、0、0,对应二进制 0001 0000 0000,组合成 24 位,低 16 位是 0x0100(注意这个 0x0100 不是十六进制 100,而是十进制的百位、十位、个位各自编码的结果),高字节是软元件代码 0xA8。所以报文里 3 字节为 A8 00 01,发送时按字节序 A8 00 01 排列。

这个规则对刚接触 SLMP 的人非常不友好,因为和三菱编程口协议的 BCD 编号还不太一样。FX5U 实际的 D 区范围是 D0 到 D7999 以及扩展的 D10000 以上,编号都在 65535 以内,但编码规则不变。M 软元件的代码是 0x90,X 是 0x9C,Y 是 0x9D,R 是 0xAF,ZR 是 0xB0。

实操层面,建议写一个通用的软元件编码函数,把“D100”或“M50”字符串解析成 24 位寻址字节。网上很多现成的 C# SLMP 类库就是这么实现的,比如 PduLayer 里的 PlcDevice 类,但直接用库会掩盖细节,出了问题根本不知道是寻址错还是帧格式错,所以下面第 3 章的示例代码会从底层开始写。

3. C# 实现 FX5U 通讯交互:读 D 区和写 M 区的可运行源码

3.1 先解决连接:TcpClient 连接 FX5U 的 1024 端口

连接部分用 System.Net.Sockets.TcpClient,设置 ReceiveTimeout 和 SendTimeout。FX5U 作为 TCP 服务器,上位机连上后要维持长连接,因为 SLMP 没有类似 Modbus 的“断开重连”机制,频繁断开重连会导致 PLC 侧的资源释放延迟,可能触发“设备忙碌”错误。

public class Fx5uClient : IDisposable { private TcpClient _client; private NetworkStream _stream; private readonly byte[] _buffer = new byte[4096]; private int _sequenceNumber; public bool Connect(string ip, int port = 1024) { _client = new TcpClient(); _client.ReceiveTimeout = 3000; _client.SendTimeout = 3000; _client.Connect(ip, port); _stream = _client.GetStream(); return _stream.CanRead && _stream.CanWrite; } }

这段代码里有几个参数值得说明。ReceiveTimeout 建议设 3000 毫秒,低于 1000 毫秒时 FX5U 在忙的时候容易误报超时,高于 5000 毫秒时上位机 UI 会明显卡顿。另外 Connect 方法本身没有超时控制,如果 PLC 没开机,TcpClient.Connect 会阻塞很久,建议用BeginConnect配合WaitOne实现连接超时,这块在工业现场比在办公室重要得多。

连接成功不代表通讯交互就绪。FX5U 默认最多允许 4 个 TCP 连接,如果之前有异常断开的连接没释放,新连接会失败。这时需要在 PLC 侧重启或等待超时释放,也可以在程序里主动先关闭旧的 socket。

3.2 构造 SLMP 读请求帧并解析响应

下边是读取 D 区字数据的核心方法。注意使用BinaryWriter处理字节序,SLMP 里多字节整数的字节序统一是“低位在前”。

public byte[] BuildReadFrame(string device, ushort count) { byte[] address = EncodeDevice(device); int dataLength = 12 + address.Length + 2; using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); bw.Write((byte)0x50); bw.Write((ushort)(dataLength + 2)); bw.Write((byte)0x00); bw.Write((byte)0xFF); bw.Write((byte)0x00); bw.Write((byte)0xFF); bw.Write((byte)0x03); bw.Write((byte)0xFF); bw.Write((byte)0x00); bw.Write((ushort)(dataLength)); bw.Write((ushort)0x0401); bw.Write((ushort)0x0000); bw.Write(address); bw.Write((ushort)count); return ms.ToArray(); }

代码中的bw.Write((ushort)0x0401)需要注意:0x01 0x04 是读命令,但 SLMP 报文里是先传低位,所以ushort值写作 0x0401,写到流里输出了 01 04。同理最后bw.Write((ushort)count)输出的是点数低位在前。

帧构造好后调用ReadDWords等待响应:

public int[] ReadDWords(string device, ushort count) { byte[] frame = BuildReadFrame(device, count); _stream.Write(frame, 0, frame.Length); _stream.Flush(); int header = 0; while (header < 9) { int n = _stream.Read(_buffer, header, 9 - header); if (n <= 0) throw new TimeoutException("PLC 未响应"); header += n; } ushort bodyLen = (ushort)(_buffer[3] | (_buffer[4] << 8)); int total = 9 + bodyLen; while (header < total) { int n = _stream.Read(_buffer, header, total - header); if (n <= 0) throw new TimeoutException("读取响应中断"); header += n; } int resultCode = _buffer[9] | (_buffer[10] << 8); if (resultCode != 0) throw new Exception($"PLC 返回错误码 0x{resultCode:X4}"); int[] values = new int[count]; for (int i = 0; i < count; i++) { values[i] = BitConverter.ToUInt16(_buffer, 11 + i * 2); } return values; }

这里注意响应帧的结构:前 9 个字节是帧头(1)加响应数据长度(2)加子帧头(2)加网络号加 PC 号加模块 IO(3),然后 2 字节结束代码(0x0000 表示成功),之后才是数据。代码里读取完第 9 个字节后再从_buffer[9]开始取结束码,顺序是对的。

这个方法的容错处理有个细节:_stream.Read在 TCP 下不保证一次能读满请求的字节数,所以必须用循环累计读取。很多简化版代码直接一个Read到位,局域网下碰巧能跑,但遇到 PLC 响应被拆成两个 TCP 包时就会卡死或读错位。这个问题在真实项目里几乎一定会出现,所以循环读取是必须写的。

3.3 写单个 D 字或批量写 M 区的封装

写 D 区和写 M 区在底层的帧格式差异只体现在软元件编码上。M 区的位数据在 SLMP 按字访问时,每一个 M 是一个位,但字访问模式下 M0 到 M15 组成第一个字,第 0 位对应 M0。批量写 M 区时要把布尔数组打包成 ushort 数组,每个元素 16 个位。

public void WriteDWord(string device, ushort value) { byte[] address = EncodeDevice(device); int dataLength = 12 + address.Length + 2 + 2; using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); bw.Write((byte)0x50); bw.Write((ushort)(dataLength + 2)); bw.Write((byte)0x00); bw.Write((byte)0xFF); bw.Write((byte)0x00); bw.Write((byte)0xFF); bw.Write((byte)0x03); bw.Write((byte)0xFF); bw.Write((byte)0x00); bw.Write((ushort)dataLength); bw.Write((ushort)0x1401); // 写命令 0x01 0x14 bw.Write((ushort)0x0000); bw.Write(address); bw.Write((ushort)1); bw.Write(value); _stream.Write(ms.ToArray(), 0, (int)ms.Length); _stream.Flush(); ReadResponse(); } private ushort[] PackBits(bool[] bits) { int len = (bits.Length + 15) / 16; ushort[] words = new ushort[len]; for (int i = 0; i < bits.Length; i++) if (bits[i]) words[i / 16] |= (ushort)(1 << (i % 16)); return words; }

PackBits方法处理位打包逻辑。M0 对应第 0 位,M1 对应第 1 位,以此类推。FX5U 的 M 区通过字方式访问时是 LSB 在前,写入时1 << (i % 16)就是按照这个规则排列。

批量写 M 区时,FX5U 对位软元件按字访问有点数上限,单帧最大 960 个字,也就是最多写 15360 个 M 位。超过这个值必须分帧。实际上建议控制在 512 点以内,因为超过后 PLC 的响应时间会明显变长。

3.4 循环数据采集和 UI 刷新卡顿的处理方式

通讯交互代码写好后,下一个绕不过去的问题是循环采集数据时 UI 卡顿。很多 C# 上位机在定时器里直接调用ReadDWords然后往 TextBox 上赋值,PLC 响应 50 毫秒就卡 50 毫秒,多个控件就卡成幻灯片。

常见的做法是用一个独立的后台线程循环采集,数据放到ConcurrentQueue或直接用System.Threading.Timer,UI 层用BeginInvoke批量刷新。采集频率控制在 100 毫秒以上,太快的频率意义不大,因为 FX5U 的以太网响应通常在 10 到 30 毫秒,但频繁的小报文会占满 PLC 的通讯处理时间。更好的做法是 200 毫秒批量读一次,一次读 20 个 D 字和 64 个 M 位,这样报文数量少,吞吐反而更高。

4. VB 实现 FX5U 通讯交互:字节拼接、布尔转换和字符串乱码处理

4.1 VB 版 SLMP 帧构造:用 Byte 数组代替 BinaryWriter

VB.NET 写法和 C# 在底层完全一样,只是语法差异。VB 里没有BinaryWriter写起来那么顺手,多数人直接用 Byte 数组拼接。要注意 VB 的数组下标从 0 开始,和旧 VB6 不一样。

Public Function BuildReadFrame(device As String, count As UShort) As Byte() Dim addr() As Byte = EncodeDevice(device) Dim dataLength As UShort = 12 + addr.Length + 2 Dim frame(27 + addr.Length) As Byte Dim idx As Integer = 0 frame(idx) = &H50 : idx += 1 frame(idx) = CByte(dataLength And &HFF) : idx += 1 frame(idx) = CByte((dataLength >> 8) And &HFF) : idx += 1 frame(idx) = &H0 : idx += 1 frame(idx) = &HFF : idx += 1 frame(idx) = &H0 : idx += 1 frame(idx) = &HFF : idx += 1 frame(idx) = &H3 : idx += 1 frame(idx) = &HFF : idx += 1 frame(idx) = &H0 : idx += 1 frame(idx) = CByte(dataLength And &HFF) : idx += 1 frame(idx) = CByte((dataLength >> 8) And &HFF) : idx += 1 frame(idx) = &H1 : idx += 1 frame(idx) = &H4 : idx += 1 frame(idx) = &H0 : idx += 1 frame(idx) = &H0 : idx += 1 Array.Copy(addr, 0, frame, idx, addr.Length) idx += addr.Length frame(idx) = CByte(count And &HFF) : idx += 1 frame(idx) = CByte((count >> 8) And &HFF) Return frame End Function

VB 的手工字节拼接容易出现在frame数组长度上算错的问题,建议在写完帧后加一个断言检查:Debug.Assert(idx = frame.Length)。凡是手工构造报文,都建议加这个检查,等号两端对不上立刻就暴露数组长度计算错误。

另外一个 VB 专属陷阱是Byte类型溢出。CByte((dataLength >> 8) And &HFF)这个写法不会溢出,因为先做了And &HFF。如果写CByte(dataLength >> 8)编译器会报错,因为UShortByte是收缩转换,VB 默认开启Option Strict时会拒绝编译。

4.2 从响应帧提取位状态:把字节还原成 M 区布尔值

读 M 区时 PLC 返回的数据每个 M 只占 1 位,16 个 M 位合一个字节(字访问时按字返回,但每个字含 16 个连续 M)。上位机要还原成布尔数组,核心代码是位测试。

Public Function DecodeBits(data() As Byte, count As Integer) As Boolean() Dim result(count - 1) As Boolean For i As Integer = 0 To count - 1 Dim byteIndex As Integer = i \ 8 Dim bitIndex As Integer = i Mod 8 result(i) = ((data(byteIndex) >> bitIndex) And &H1) = 1 Next Return result End Function

这里有个关键点容易搞错:SLMP 返回的 M 区数据,如果起始地址正好对齐到 16 的倍数(比如 M0、M16、M32),那么每个字从第一个字节的低位开始,位序 LSB 在前,上面的DecodeBits就是对的。但如果起始地址不是 16 的倍数,比如从 M5 开始读,返回的数据是从 M5 开始连续排列的,第一个字节位 0 是 M5,这时按上面的DecodeBits解析会整体错位。

实际通讯交互时,如果确实需要从任意 M 地址开始读,建议永远是按 16 的倍数对齐后整块读取,再在业务层做偏移。比如要读 M5 到 M10,就读取 M0 到 M15,然后取第 5 到第 10 位。这个策略可以避免所有非对齐解析的坑,代价是多读一点数据,在 FX5U 上完全可以接受。

4.3 VB 读取 D 区 32 位整数和浮点数的字节顺序调整

D 区按字读回来的是 16 位无符号整数,D 和 D+1 两个连续寄存器组成 32 位数据时,三菱的默认规则是 D 存放低 16 位,D+1 存放高 16 位(小端序)。FX5U 没有像 Q 系列那样可以全局设置字节序的选项,所以 C#/VB 上位机重组 32 位数据时必须按小端处理。

Public Function ReadDWord32(device As String, isFloat As Boolean) As Double Dim raw() As UShort = ReadDWords(device, 2) Dim bytes(3) As Byte bytes(0) = CByte(raw(0) And &HFF) bytes(1) = CByte((raw(0) >> 8) And &HFF) bytes(2) = CByte(raw(1) And &HFF) bytes(3) = CByte((raw(1) >> 8) And &HFF) If isFloat Then Return BitConverter.ToSingle(bytes, 0) Else Return BitConverter.ToInt32(bytes, 0) End If End Function

这段代码先读出两个连续字,再将这两个 16 位值按“第一个字在低地址”的规则拼成 4 字节小端,然后用 BitConverter 转换。如果 PLC 程序里用的是结构化文本的DINTREAL,GX Works3 的设置默认和这里相同,可以直接用。

需要特别小心的是,如果 PLC 程序是别人写的,他可能用了MOV指令把一个 32 位值直接搬到 D0,没有拆高低字,这时候上位机按 D0=低字、D1=高字解析出来的结果会差 65536 的倍数。排查方法很简单:给 D0 写一个已知值 65539,看上位机读出来是 65539 还是 4,后者说明高地位反了。

4.4 字符串读取:Shift-JIS 解码和末尾 00 截断

FX5U 的 D 区存字符串通常是 16 位一个字符,一个 D 字放一个 Shift-JIS 编码的字符。如果 PLC 程序里用$MOV指令写入的字符串,读取时按每个字两个字节重组,再用 Shift-JIS 编码转成 Unicode。

Public Function ReadString(device As String, wordCount As Integer) As String Dim raw() As UShort = ReadDWords(device, wordCount) Dim bytes(wordCount * 2 - 1) As Byte For i As Integer = 0 To wordCount - 1 bytes(i * 2) = CByte(raw(i) And &HFF) bytes(i * 2 + 1) = CByte((raw(i) >> 8) And &HFF) Next Dim enc As Encoding = Encoding.GetEncoding(932) Dim text As String = enc.GetString(bytes) Dim idx As Integer = text.IndexOf(ChrW(0)) If idx >= 0 Then text = text.Substring(0, idx) Return text.TrimEnd() End Function

中文字符在 Shift-JIS 里占两个字节,所以会跨两个 D 字。读取时先按字取出所有字节,再统一用编码 932 解码,不要逐字解码,否则双字节字符会被拆开变成乱码。末尾的ChrW(0)截断对应三菱字符串的终止符,PLC 里没有显式写入 0 的字符串,读出来会有尾部垃圾字符,截断是必须的。如果乱码问题依旧,考虑 PLC 用的是 UTF-8 写入:FX5U 的$MOV支持 Unicode 字符串写入,但那是另一套数据布局,不在本文的通讯交互范围内。

5. FX5U 通讯交互的排错矩阵:连接失败、错误码和 Wireshark 抓包核对

5.1 连不上 1024 端口时的排查顺序

FX5U 通讯交互最让人头疼的不是协议本身,而是“连不上”。现场最常见的现象是 C# 程序在办公室连模拟器正常,到现场连真机就超时。排查顺序一般是这样:先 ping 通 PLC 的 IP,确认网线没问题;再在 GX Works3 的“简单 CPU 通讯设置”里确认端口号是 1024,并且勾选了“允许 RUN 中写入”;最后检查 PLC 侧做的以太网配置有没有和别的功能冲突,例如以太网模块同时做了 Socket 通讯和 SLMP,端口会互相占用。

另外一个被忽略的点是,FX5U 的 SLMP 服务器只允许 4 个并发连接。如果调试时多次跑程序没正常释放 socket,占满之后新的连接会被拒。检查方法是在 Windows 命令行执行netstat -an | findstr 1024,如果看到大量ESTABLISHED或者TIME_WAIT状态的连接,说明程序没有正确释放资源。

C# 程序里必须写在finally里调Dispose或者Close,尽量不用using块包裹长连接。因为using在块结束时会释放流,但不会主动发送 RST 包,PLC 侧需要等 TCP 超时才能感知断开。

5.2 SLMP 错误码解读表和响应超时的常见原因

PLC 返回的结束代码不为 0 时,通讯交互逻辑上已经走通,只是请求本身被拒绝。下表是 FX5U 相较常用到的几个错误码:

错误码含义常见原因
0xC051点数超限一次请求超过 960 个字
0xC05C软元件地址错误软元件编号编码错误或超出范围
0xC056不支持的命令命令号写反了(读写了 0x14 0x01)
0xC059数据长度错误请求数据长度字段计算不正确
0xC0A0通讯模块繁忙PLC 正在处理大量请求,稍后重试

超时问题的定位比错误码麻烦。ReadTimeout抛出时,不要急着加大超时时间,先用 Wireshark 看请求有没有到 PLC。如果抓包只有 SYN 没有 ACK,是网络层问题;有请求帧但没响应帧,是 PLC 配置问题;有响应但程序还是超时,那是解析逻辑问题,说明响应帧读不完整或者长度判断错误。

5.3 用 Wireshark 核对双字节和校验与数据对齐

很多 SLMP 实现里会加一个双字节和校验字段(从帧头到数据末尾的累加和),三菱的 MC 协议在串口版里是必须的,以太网版 3E 帧其实可以不带,FX5U 默认不带校验也能通讯。但有些三菱兼容设备(比如部分国产 PLC 模仿 SLMP)会强制要求校验,所以稳妥的做法是自研的时候带上,免得到现场发现对方设备需要校验而源码里完全没这块逻辑。

双字节和校验的算法是从帧头字节开始,两字节一组作为一个 16 位整数累加,溢出回卷,最后取反加一。在 Wireshark 里核对时,找到报文末尾的校验字段,手工按这个规则算一遍就能确认。如果计算的校验和和报文里不一致,果断定位是组帧逻辑的问题,不要去查 PLC 配置。

数据对齐的核对比校验更常用。Socket 通讯时抓包看到的响应数据区,如果是读 D 区 10 个字,每个字 2 字节,第 0 个 D 字在数据区最开始的两字节,而且低字节在前。用 Wireshark 抓到一串0x2A 0x00 0x01 0x00这样的数据,对应 D0=42,D1=1。如果程序读出来的 D0=10752,那肯定是字节序取反了,把数据区的两字节高低位调换即可。调换位置是解析层唯一要处理的地方,不要改动通讯层的解码逻辑。

6. 通讯交互压测:用自检报文验证 FX5U 响应时间和数据正确性

写完通讯交互代码后,不建议直接接设备全流程调,先在 FX5U 的 D0 到 D9 写入已知测试值,具体操作用 GX Works3 的监视功能在线改值。注意写入时区分数据类型,D0 写成 16 位整数测试数、D2 起写 32 位浮点测试数、D4 起写字符串测试数、M0 到 M15 写入即位模式,注意 FX5U 的位元件从 M0 开始 16 个一组。

然后写一个压测小程序,循环 1000 次读 D0 到 D9 和 M0 到 M15,记录每次的耗时和错误数。正常情况下 FX5U 单次响应在 10 到 30 毫秒,1000 次全部成功,平均耗时在 15 毫秒左右。如果发现偶发超时,检查程序是不是在 UI 线程里同步调用,或者在同一连接上并发发了多个请求。SLMP 是请求-应答模型,一个连接上同时发多个请求会导致响应错位,这是“有时读对有时读错”的头号原因。

压测时额外验证一次连续地址读取。FX5U 批量读 D0 到 D19,和分 20 次单读 D0 到 D19,数据必须完全一致。批量读返回数据的第 i 个值必须和单读 Di 的值相等,IO 编号和 CPU 编号不一致时的数据也有可能在批量读时被错误排序,这类问题属于 GX Works3 侧的网络配置,不是上位机代码能绕开的。

最后说调试完收尾时的技巧:通讯交互代码里保留一个“原始报文开关”的配置项,压测时把它打开,每次收发都记录到日志文件或者输出到 Debug 窗口。等实际部署到现场出现偶发问题时,直接看这个开关打出来的原始帧,用 Hex 工具逐个字节对照第 2 章的帧格式表,80% 的问题能在五分钟内定位到是组帧的问题还是解析的问题。这个习惯比任何调试器都管用。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 5:52:50

OSG中Mipmap纹理技术的原理与优化实践

1. Mipmap纹理技术概述在三维图形渲染领域&#xff0c;纹理质量直接影响最终视觉效果。当观察者与纹理表面的距离变化时&#xff0c;传统单级纹理会导致明显的视觉瑕疵——近处出现锯齿&#xff08;Aliasing&#xff09;&#xff0c;远处产生闪烁&#xff08;Flickering&#x…

作者头像 李华
网站建设 2026/9/13 5:51:05

S7-200 PLC与组态王在自动洗车控制系统中的应用实践

前阵子朋友盘下一个小型洗车店&#xff0c;设备是二手市场淘来的“残血版”自动洗车机&#xff0c;原控制柜里的继电器东倒西歪&#xff0c;动作时序全靠时间继电器硬凑&#xff0c;三天两头卡壳。让我过去看看能不能救活&#xff0c;我一看柜子里的走线&#xff0c;头就大了—…

作者头像 李华
网站建设 2026/9/13 5:49:39

磁控U位资产管理系统:机房资产全链路智能管控实践

1. 机房资产管理的痛点&#xff1a;为什么传统U位管理越来越跟不上我做机房运维这些年&#xff0c;最怕的不是服务器宕机&#xff0c;而是年底资产盘点。几百上千个机柜&#xff0c;上万台设备&#xff0c;底账和现场普遍对不上。仓库里明明显示有空U位&#xff0c;到了现场一查…

作者头像 李华
网站建设 2026/9/13 5:49:30

双馈风机低电压穿越技术与MATLAB仿真实践

1. 双馈风机DFIG与低电压穿越技术背景双馈异步风力发电机&#xff08;Doubly-Fed Induction Generator, DFIG&#xff09;作为现代风力发电系统的核心部件&#xff0c;其独特之处在于转子绕组通过背靠背变流器与电网连接。这种结构使得DFIG能够在同步转速30%的范围内实现变速运…

作者头像 李华