PLC 通讯中数据格式转换及处理是核心环节,因 PLC 存储数据的方式(如寄存器位数、字节顺序、数据类型)与上位机(如 C#、Python)存在差异,稍不注意就会出现 “读错值” 的问题。以下从常见数据类型映射、转换规则、典型场景处理三个维度详细说明:
一、PLC 与上位机数据类型的核心差异
PLC 的寄存器通常以16 位无符号整数(UInt16) 为基础单位(如 Modbus 的保持寄存器),而上位机需要解析为实际物理量(如温度、压力、液位),核心差异体现在:
位数不同:PLC 中 16 位寄存器可能存储 8 位(字节)、16 位(整数)、32 位(浮点数、长整数)数据。
字节顺序(端序):PLC 存储多字节数据时可能采用 “大端序”(高位字节在前)或 “小端序”(低位字节在前),与上位机默认顺序可能相反。
物理量转换:寄存器的原始值(如 0-4095)需通过 “量程换算” 转换为实际物理值(如 0-10V 对应 0-50℃)。
二、常见数据类型转换规则(以 Modbus 为例)
1. 16 位整数(最基础)
PLC 存储:1 个寄存器(16 位),如西门子 S7-1200 的 "MW100"、三菱 FX5U 的 "D100"。
数据类型:有符号整数(Int16)或无符号整数(UInt16)。
转换示例(C#):
csharp
// 假设从PLC读取的寄存器值为0x03E8(1000)ushort plcValue = 1000; // 无符号16位short signedValue = (short)plcValue; // 转换为有符号(若PLC存储的是负数,需用此方式)
2. 32 位整数(长整数)
PLC 存储:连续 2 个 16 位寄存器(如 D100 和 D101),需注意字节顺序。
大端序(高位在前):D100 存高位字节,D101 存低位字节(多数 PLC 默认,如三菱、欧姆龙)。
csharp
ushort[] plcRegs = { 0x1234, 0x5678 }; // D100=0x1234,D101=0x5678byte[] bytes = new byte[4];// 大端序拼接:高位寄存器的字节在前Buffer.BlockCopy(plcRegs, 0, bytes, 0, 2); // D100的字节→bytes[0-1]Buffer.BlockCopy(plcRegs, 2, bytes, 2, 2); // D101的字节→bytes[2-3]int int32Value = BitConverter.ToInt32(bytes, 0); // 结果:0x12345678 = 305419896小端序(低位在前):D100 存低位字节,D101 存高位字节(如部分西门子 PLC),需交换寄存器顺序后再转换。
3. 32 位浮点数(最容易出错)
PLC 存储:连续 2 个 16 位寄存器,通过 IEEE 754 标准编码(如温度 25.5℃、压力 0.8MPa)。
转换关键:先按 PLC 的字节顺序拼接为 4 字节数组,再转换为 float。
示例(大端序,三菱 PLC):PLC 中 D200=0x41C8,D201=0x0000(对应浮点数 25.5)
csharp
ushort[] plcRegs = { 0x41C8, 0x0000 };byte[] bytes = new byte[4];// 大端序拼接:D200的两个字节→bytes[0-1],D201的两个字节→bytes[2-3]bytes[0] = (byte)(plcRegs[0] >> 8); // 0x41bytes[1] = (byte)(plcRegs[0] & 0xFF); // 0xC8bytes[2] = (byte)(plcRegs[1] >> 8); // 0x00bytes[3] = (byte)(plcRegs[1] & 0xFF); // 0x00float floatValue = BitConverter.ToSingle(bytes, 0); // 结果:25.5注意:若转换后数值异常(如极大 / 极小值),大概率是字节顺序搞反了,需交换两个寄存器的位置重试。
4. 字符串(ASCII/UTF-8)
PLC 存储:每个字符占 1 个字节,1 个 16 位寄存器可存 2 个字符(低 8 位和高 8 位)。
示例:PLC 寄存器 D300=0x4869(ASCII 码:'H'=0x48,'i'=0x69),转换为字符串:
csharp
ushort regValue = 0x4869;char c1 = (char)(regValue >> 8); // 高8位→'H'char c2 = (char)(regValue & 0xFF); // 低8位→'i'string str = $"{c1}{c2}"; // 结果:"Hi"
三、物理量量程换算(关键步骤)
PLC 寄存器的原始值(如模拟量输入)通常是 “归一化数值”,需转换为实际物理量:
示例:PLC 模拟量输入模块将 0-10V 信号转换为 0-4095(12 位精度),对应温度 0-100℃。若读取的寄存器值为 2048,计算实际温度:
csharp
int rawValue = 2048; // 寄存器原始值int rawMin = 0, rawMax = 4095; // 模块量程对应的原始值范围float physMin = 0, physMax = 100; // 实际物理量范围float physValue = physMin + (rawValue - rawMin) * (physMax - physMin) / (rawMax - rawMin);// 结果:2048 → 约49.96℃(接近50℃)
注意:若传感器是 4-20mA 信号,原始值范围对应 2048-4095(12 位模块),需按实际量程调整。
四、常见问题及排查
数值完全错误
排查:数据类型选错(如把浮点数当整数读)、字节顺序搞反(大端 / 小端弄混)。
解决:用 PLC 编程软件监控寄存器原始值,对比上位机读取的字节数组是否一致。
数值偏移或比例错误
排查:量程换算时原始值范围或物理量范围设置错误(如把 4-20mA 当 0-20mA 算)。
解决:查阅 PLC 模拟量模块手册,确认原始值与信号的对应关系。
负数无法正确显示
排查:16 位整数未转换为有符号类型(如 ushort 直接转 int,负数会变成大正数)。
解决:先用 (short) 强制转换为有符号类型,再处理。
五、工具推荐
调试辅助:用 Modbus Poll 读取寄存器原始值,手动计算验证转换逻辑。
类库简化:C# 可使用
BitConverter,Python 可使用struct模块处理字节转换,减少手动拼接错误。


