PLC数据类型与进制转换实战:从补码到BCD码的完全解析
在 PLC 编程中数据类型和进制转换是绕不开的基础功。很多初学者能写出点灯、启停电机这类梯形图但一接触到模拟量处理、通信报文解析、变频器给定频率、触摸屏地址映射时就会卡在“为什么 16#4000 对应 16384”“D100 里的值明明是 65535上位机却显示 -1”“BCD 指令转换出来的数为什么会多 6”这类问题上。这些现象背后本质是数据类型定义不正确、进制概念混淆、有符号与无符号处理不当。本文从 PLC 数据类型分类出发详细拆解二进制、十六进制、补码、BCD 码的对应关系并用结构化文本和梯形图两种方式完成进制转换实操最后给出可直接使用的问题排查路径。读完以后你至少能做到看到一条指令上的操作数能说出它占几个位、能表示什么范围、在通信或显示时该怎么处理。1. 先理解 PLC 数据类型解决什么问题1.1 从继电器逻辑到数据运算的转变早期的 PLC 主要做开关量控制梯形图里的触点、线圈、定时器、计数器已经能满足大多数顺序控制需求。那时候一个 X0、一个 Y0本质就是“位”的读和写没有复杂的数据运算。但现代 PLC 早已不是单纯的继电器替代品。模拟量采集、PID 调节、位置控制、视觉对位、MES 通信、变频器总线控制都要求 PLC 能存整数、实数、字符串、时间、数组。CPU 里的内存是一块连续的存储区如果没有数据类型约定CPU 就不知道一个地址里存的是“一位开关量”还是“一个 32 位浮点数”。数据类型的作用就是给内存建立解释规则长度是多少位、有没有符号、小数点在哪个位置、允许参与什么运算。工程里的很多故障根子就是数据类型选错。比如把一个 16 位无符号数当成 16 位有符号数传给上位机温度从 65535 变成 -1又比如把 REAL 当成 DINT 发送四个字节被拆成两个整数通信报文直接乱套。理解数据类型是进入 PLC 工程调试的必需课。1.2 常见 PLC 数据类型的基本分类不同品牌的数据类型名称有差异但底层分类逻辑一致。按存储长度从窄到宽PLC 常用数据类型可以分成以下几类。类型大类长度常见名称列举典型取值范围典型用途位1 bitBOOL、BITTRUE/FALSE按钮、接触器、报警标志字节8 bitBYTE、USINT、SINT0~255 或 -128~127通信数据原始字节、小的计数字16 bitWORD、UINT、INT0~65535 或 -32768~32767模拟量通道值、步进计数、寄存器双字32 bitDWORD、UDINT、DINT、REAL0~4294967295 或 -2147483648~2147483647REAL 约 ±3.4e38累计量、编码器位置、浮点运算长实数64 bitLREAL、LWORD约 ±1.8e308高精度浮点、科学计算字符串多个字节STRING、CHAR、WSTRING字符序列设备名称、报警文本需要注意一个容易混淆的点BYTE、WORD、DWORD 通常被当作“无符号”使用但很多 PLC 指令也允许把它们赋值给有符号类型。比如西门子 STEP 7 里WORD 和 INT 长度相同但语义不同。WORD 范围 16#0000 到 16#FFFFINT 范围 -32768 到 32767。如果不做转换直接传递CPU 会报警或产生隐式转换容易埋坑。1.3 各品牌 PLC 的数据类型差异对照实际编程时不同软件给出的类型名称不一样。常见对应关系如下。西门子 TIA Portal三菱 GX Works汇川 / Codesys说明BOOL位软元件 X/Y/MBOOL单个位BYTE字节连续位BYTE8 位无符号WORD / INT16 位 D 寄存器WORD / INT16 位DWORD / DINT / REAL32 位数据寄存器DnDn1DWORD / DINT / REAL32 位LREAL浮点寄存器双字LREAL64 位三菱 PLC 的 D 寄存器默认是 16 位要保存 32 位数据时需要占用两个连续 D 寄存器比如 D100 和 D101。西门子 S7-1200/1500 的变量表里直接声明 INT 或 DINT但底层存储区仍然按字节地址排列。汇川、信捷等支持 Codesys 风格指令的 PLC类型体系和西门子更接近都遵循 IEC 61131-3 标准。更重要的是不管品牌怎么命名CPU 内部都只能识别二进制。你在触摸屏上看到的十进制、十六进制、ASCII 字符都是人机界面按数据类型解释出来的结果。所以下面必须先讲清楚进制转换的计算逻辑。2. 进制转换是 PLC 数据操作的底层语言2.1 二进制、八进制、十进制、十六进制的关系PLC 底层只有两种电平高电平和低电平对应 1 和 0。一位二进制数称为 bit8 个 bit 组成一个 BYTE16 个 bit 组成一个 WORD。因为二进制写起来太长工程上常用十六进制来缩写每 4 位二进制对应 1 位十六进制。二进制、十进制、十六进制对照表是转换的基础不需要背但要会查、会快速心算。十进制二进制十六进制000000100011200102300113401004501015601106701117810008910019101010A111011B121100C131101D141110E151111F十六进制转二进制时一位十六进制直接展开成 4 位二进制。比如 16#A3 等于二进制 1010 0011。十进制转十六进制时可以用除 16 取余法也可以用计算器。但 PLC 工程中更常用的是反向验证直接把十六进制数按权展开确认它对应的十进制值。例如 16#A3 10 × 16 3 163。2.2 原码、反码、补码与负数表示PLC 的整数类型都带符号CPU 用补码表示负数。补码的规则是正数不变负数使用“原码取反加一”得到。以 16 位 INT 为例十进制 -1 的补码要这样计算原码1 的二进制是 0000 0000 0000 0001。取反1111 1111 1111 1110。加一1111 1111 1111 1111。所以 -1 在 PLC 里的十六进制表示是 16#FFFF。这也是为什么把 INT 当作 WORD 显示时-1 会变成 65535。两者存储的 bit 完全相同只是解释方式不同。同理-32768 的补码是 16#8000-2 是 16#FFFE。实际调试中如果触摸屏显示 65535而上位机协议要求有符号数就要检查数据类型的定义是否一致。很多通信模块默认按无符号处理必须在组态时指定符号属性。2.3 BCD 码PLC 里特有的十进制编码BCD 码Binary-Coded Decimal用 4 位二进制表示一位十进制数。0~9 的 BCD 码与十进制一致但 10~15 在 BCD 中非法。例如十进制 123BCD 码是 0001 0010 0011十六进制写出来是 16#123。注意 16#123 按普通十六进制计算等于 291但按 BCD 解读就是 123。拨码开关、部分温控表、某些编码器输出、旧式变频器面板设定值都使用 BCD 格式。PLC 里通常有 BCD 转换指令例如西门子的BCD_I和I_BCD三菱的BCD和BIN指令。使用 BCD 时最常见的错误是把 BCD 码当普通十六进制数直接计算。比如某温控表通过模拟量接口输出 BCD 码 16#257如果直接当成十进制 599 使用必然出错。正确做法是用 BCD 转整数指令把 16#257 还原为十进制 257。2.4 浮点数与整数的进制差异REAL32 位浮点数在 PLC 中遵循 IEEE 754 标准使用 1 位符号位、8 位指数位、23 位尾数表示。虽然它也是 32 位但不能像整数那样直接把十六进制数换算成十进制。比如 16#40A00000 对应的实数约等于 5.0但这个结果无法通过普通进制转换口算出来。实际调试中常见场景是上位机发来 4 个字节的十六进制报文里面包含浮点数。此时不能把四个字节按整数拆开而是要在 PLC 里把 DWORD 转换为 REAL。西门子提供了DWORD_TO_REAL或MEMCPYCodesys 里也有DWORD_TO_REAL。三菱则需要用DMOV指令或特殊寄存器处理。因此进制转换不仅包括整数进制还包括“同样的 bit 序列按整数解释是多少、按实数解释是多少、按 BCD 解释是多少、按 ASCII 字符解释是多少”这一层语义映射。理解了这层再去看数据转换指令就不迷茫了。3. 环境准备用模拟器跑通一个最小数据类型实验3.1 常用编程软件与模拟环境各品牌 PLC 的编程软件基本都提供仿真功能适合在学习阶段做数据实验。软件适用品牌模拟器支持语言TIA Portal西门子 S7-1200/1500PLCSIMLAD、FBD、ST、SCLGX Works2 / GX Works3三菱 FX / Q / L / iQ-R内置模拟器LAD、ST、STLCODESYS汇川、信捷、欧姆龙 NJ/NX 等CODESYS Control WinST、LD、FBD、SFCMCGS / 组态软件多品牌部分支持脚本、梯形图配合驱动如果没有实体 PLC推荐直接用 TIA Portal PLCSIM 或 CODESYS 仿真。这两个环境对数据类型的显示、强制、监控都很直观能把 bit、字节、字的对应关系看得清清楚楚。需要说明的是不同软件的模拟器对通信指令的支持有限。本文的进制转换实验只涉及计算指令不依赖硬件通信因此模拟器完全够用。3.2 设计实验项目变量表和监控方式新建项目后在 PLC 变量表里声明一组变量。以 TIA Portal 的 SCL 程序为例可以定义以下变量。VAR // 实验输入 decValue : INT : -1234; // 有符号整数 wordValue : WORD : 16#ABCD; // 无符号字 bcdValue : INT : 16#1234; // BCD 码示例 // 转换结果 wordFromInt : WORD; // 有符号转无符号 intFromWord : INT; // 无符号转有符号 binString : STRING[20]; // 二进制字符串 hexString : STRING[10]; // 十六进制字符串 bcdToDec : INT; // BCD 转十进制结果 decToBcd : INT; // 十进制转 BCD 结果 realFromDword : REAL; // 双字按浮点解释 dwordForReal : DWORD; END_VAR在监控表中添加这些变量后可以看到不同类型之间的 bit 对应关系。实验前要确认程序块使用结构化文本ST / SCL编写因为后面的转换函数用文本语言更直观。3.3 在结构化文本ST中实现进制转换ST 语言本身可以直接做赋值不同数据类型之间需要用转换指令。常见写法如下。// 1. INT 转 WORD保持 bit 不变 wordFromInt : WORD_TO_WORD(INT_TO_WORD(decValue)); // 或者直接 wordFromInt : WORD_TO_WORD(decValue); // 取决于软件是否允许隐式转换 // 2. WORD 转 INT intFromWord : INT_TO_INT(wordValue); // 3. 十进制转十六进制字符串 hexString : INT_TO_STRING(decValue); // 4. 自定义把 INT 转成二进制字符串 // 下面用循环拼接字符串演示逻辑 binString : ; FOR i : 15 TO 0 BY -1 DO IF (decValue AND INT_TO_WORD(16#0001)) 0 THEN binString : CONCAT(binString, 1); ELSE binString : CONCAT(binString, 0); END_IF; decValue : SHL(INT_TO_WORD(decValue), 1); // 此处的位移操作要小心 END_FOR;实际编写时要留意不同 PLC 的位操作指令签名有差异。TIA 中SHL和SHR作用于位序列类型三菱 ST 中也有类似指令但写法稍有区别。上面示例主要说明思路落地前要查对应指令手册。更稳妥的二进制字符串生成方式是使用无符号变量WORD逐位测试。VAR testWord : WORD; mask : WORD; idx : INT; END_VAR testWord : wordValue; binString : ; FOR idx : 15 TO 0 BY -1 DO mask : WORD#16#0001; mask : SHL(mask, idx); // 把 1 移到目标位 IF (testWord AND mask) 0 THEN binString : CONCAT(binString, 1); ELSE binString : CONCAT(binString, 0); END_IF; END_FOR;这里用WORD#16#0001构造掩码再循环移位避免对有符号数做移位导致符号位扩散。3.4 在梯形图中实现进制转换的思路梯形图里通常没有专门“转字符串”的指令但可以通过比较和传送指令完成进制转换。例如用比较指令CMP判断十进制数在哪一段然后输出对应的十六进制字符。用移位指令ROL、ROR、SHR、SHL逐位取出二进制位送给输出线圈。用MOVE指令把整数传送到字软元件然后通过触摸屏以十六进制格式显示。梯形图适合直观观察位变化但写复杂转换逻辑时不如 ST 方便。常见做法是计算用 ST 写梯形图只做调用和状态切换。下面给出一个梯形图片段思路用于把 INT 型变量DecValue的二进制位依次送到 16 个输出点Q0.0~Q0.7以西门子为例。先用MOVE将DecValue复制到TempWord。用SHR指令将TempWord右移 8 位然后取低 8 位送到 QB0这样 Q0.0~Q0.7 对应原值的高字节。再用SHR右移 0 位直接将低字节送到 QB1。如果输出点不够可以串行输出但要注意扫描周期对输出刷新时机的影响。由于梯形图逻辑较分散建议在 ST 块里完成核心转换再给梯形图留一个“数据已转换”的布尔标志。4. 实操三种进制转换方式对比4.1 方式一手算和验证用的转换表在工程现场没有电脑或模拟器时可以用下表快速查 16 位整数的几个关键值。十进制十六进制无符号解释二进制高 8 位二进制低 8 位PLC 数据类型建议016#00000000 00000000 0000INT / WORD 均可116#00010000 00000000 0001INT / WORD 均可25516#00FF0000 00001111 1111BYTE / USINT25616#01000000 00010000 0000WORD / INT3276716#7FFF0111 11111111 1111INT 最大值-116#FFFF1111 11111111 1111INT补码-3276816#80001000 00000000 0000INT 最小值手算十六进制转十进制时从右往左每位乘以 16 的幂次相加。例如 16#ABCD 10×16^3 11×16^2 12×16^1 13×16^0 43981。但要注意如果这个字按有符号 INT 解释那就是负数需要用补码换算结果是 -10923。这就是为什么同一个 16#ABCD在不同场景下显示完全不一样。4.2 方式二使用 PLC 内置转换指令几乎所有主流 PLC 都内置数据转换指令。下表整理常用指令的典型作用。品牌/平台指令或函数作用西门子 SCL/TIAINT_TO_WORD有符号整数转无符号字bit 不变西门子 SCL/TIAWORD_TO_INT无符号字转有符号整数西门子 SCL/TIABCD_I将 3 位 BCD 码转为整数西门子 SCL/TIAI_BCD将整数转为 BCD 码三菱 GXBIN K4X0 D0把位元件组合中的 BCD 值转换成二进制数三菱 GXBCD D0 K4Y0把整数转换成 BCD 输出到位元件汇川/CodesysWORD_TO_INT/INT_TO_WORD长度不变的重新解释汇川/CodesysBCD_TO_INT/INT_TO_BCDBCD 码转换例如在西门子 SCL 中写bcdToDec : BCD_I(bcdValue); // bcdValue 16#1234转换为 1234 decToBcd : I_BCD(decValue); // decValue 1234转换为 16#1234这里的关键点BCD_I会把 16#1234 解释为 1234而不是普通十六进制的 4660。使用前必须确认源操作数是否符合 BCD 格式。如果源数据里有 A、B、C、D、E、F 这六个非法 BCD 字符指令会报错或产生不可预期结果。4.3 方式三用 ST 语言写通用转换函数当内置指令不支持复杂场景时可以自己写函数。下面是一个把 16 位无符号整数转成十六进制字符串的 ST 函数示例适用于 Codesys、TIA SCL 等 IEC 语言环境。FUNCTION FnWordToHexString : STRING[10] VAR_INPUT value : WORD; END_VAR VAR temp : WORD; nibble : BYTE; hexChar : STRING[1]; idx : INT; result : STRING[10]; END_VAR result : ; temp : value; FOR idx : 1 TO 4 DO nibble : BYTE_TO_BYTE(temp AND WORD#16#000F); // 取低 4 位 nibble : SHL(nibble, 0); // 占位实际取低 4 位即可 CASE nibble OF 0: hexChar : 0; 1: hexChar : 1; 2: hexChar : 2; 3: hexChar : 3; 4: hexChar : 4; 5: hexChar : 5; 6: hexChar : 6; 7: hexChar : 7; 8: hexChar : 8; 9: hexChar : 9; 10: hexChar : A; 11: hexChar : B; 12: hexChar : C; 13: hexChar : D; 14: hexChar : E; 15: hexChar : F; END_CASE; result : CONCAT(hexChar, result); // 高位在前 temp : SHR(temp, 4); // 右移一个十六进制位 END_FOR; FnWordToHexString : result;这个函数返回的字符串是 4 位十六进制例如输入 16#ABCD输出ABCD。同理可以写二进制字符串函数。写通用函数时需要注意SHR对 WORD 操作是逻辑右移不会产生符号位问题但如果入参是 INT则SHR可能被视为算术右移导致负数高位补 1结果完全错误。所以函数入参尽量用无符号类型。4.4 运行与验证监控表里看结果运行仿真后在监控表里观察变量decValue输入 -1234 时wordFromInt显示 16#FB2E。wordValue输入 16#ABCD 时intFromWord显示 -21555。bcdValue输入 16#1234 时bcdToDec输出 1234。binString显示为包含 0/1 的字符串。验证时重点不是看程序没报错而是确认解释逻辑。变量输入预期输出说明decValue-123416#FB2E有符号转无符号wordValue16#ABCD-21555无符号转有符号bcdValue16#12341234BCD 码转十进制decToBcd123416#1234十进制转 BCDrealFromDword16#40A000005.0浮点解释如果在仿真中看到某个结果不符合预期优先检查变量类型是否写错再检查转换指令是否符合品牌语法。5. 数据类型相关常见错误与排查链路5.1 数据类型长度不匹配导致数据截断现象32 位编码器位置值传到 16 位寄存器后数值变得很小或来回跳变COMMUNICATION 报文解析出的数据位数不够。原因编码器计数超过 32767 或 65535但中间变量用的是 WORD/INT高位被截断。检查方式查中间变量声明长度。用监控表看原始双字数据例如 MD0 或 D100D101。对比实际物理量是否超出 16 位范围。解决方案数据源声明为 DINT/UDINT/REAL。通信映射时32 位数据要占用连续两个 16 位寄存器注意高字和低字的排列顺序。不要在传送指令中把 DWORD 强制 MOVE 到 WORD。预防建议设计变量表时先按物理量范围估算位宽再选类型。安全起见模拟量累计、编码器位置优先使用 32 位类型。5.2 有符号数和无符号数混用导致负数显示异常现象触摸屏显示 65535实际应该显示 -1三菱 D 寄存器里存了负数但上位机按无符号读取。原因PLC 和上位机对同一 16 位数据的符号解释不一致。检查方式在 PLC 监控表里同时以十进制和十六进制查看该变量。用WORD_TO_INT和INT_TO_WORD分别验证。检查触摸屏通道的数据类型设置确认是 INT 还是 WORD。解决方案明确通信协议约定所有整数都按 INT 或有符号 DINT 传输。如果是 Modbus 等无符号原生协议PLC 侧发送前把 INT 转为 WORD接收后把 WORD 转回 INT。触摸屏上统一设置对应格式。预防建议写一份数据类型映射表规定哪个地址是 WORD、哪个是 INT不让程序里混着写。5.3 十六进制与 BCD 混淆导致显示乱码或数值错乱现象拨码开关读上来的值总是比实际值大或者出现 10、11 等不该出现的数字温控表设定值变成两位数跳变。原因PLC 把 BCD 码当成普通二进制数进行算术运算或者把普通十六进制数用 BCD 指令转换。检查方式在监控表里查看原始值按十六进制观察是否为 0~9 组合。比如拨码设定 123原始值十六进制应为 16#123而不是 16#7B。分别用 BCD 转换指令和普通整数运算对比结果。解决方案若原始数据是 BCD必须先执行BCD_I或BIN指令转换成整数后再运算。若是普通整数不要使用 BCD 指令。脉冲输出、编码器位置等数据通常用普通二进制输出不需要 BCD 转换。预防建议阅读硬件手册时特别注意“输出码制”这一章确认是 BCD 还是二进制是格雷码还是普通二进制。这类参数在设备选型阶段就要确定。5.4 通信报文长度和字节序不正确现象用 Modbus 读上来的 32 位浮点数数值错误但在 PLC 监控里看到的数据是对的同一个报文在国产触摸屏上显示正常换西门子屏就乱。原因字节顺序大端/小端不匹配。西门子 PLC 和很多 TCP/IP 协议默认大端部分国产仪表、单片机设备使用小端。检查方式用串口工具或通信监视抓取原始报文确认发送字节顺序。在 PLC 端把接收到的四个字节分别显示对比浮点数十六进制表示顺序。使用WORD字节调换指令或SWAP指令试验。解决方案在 PLC 程序中加入字节交换逻辑常见做法是把 DWORD 拆成 BYTE 数组再重组。使用通信协议自带的字节序配置项。如果上位机可以配置优先在组态软件里设置字节序。预防建议所有自定义协议文档中必须写明整型、浮点型、字符串的字节序并在调试初期用固定值测试例如发送 1.0观察十六进制是否为 3F800000 的正确顺序。5.5 排查链路从现象到根因的顺序遇到数据类型相关现象时推荐按以下顺序排查。确认输入信号或通信报文原始值用监控表看原始十六进制先不看十进制。确认 PLC 变量类型位、字节、字、双字有符号还是无符号。确认是否涉及 BCD 或特殊编码看硬件手册看指令说明。确认通信缓冲区字节顺序是否颠倒寄存器地址是否错位。确认上位机或触摸屏数据类型INT/WORD/REAL 是否和 PLC 侧一致。确认转换指令执行条件是否被扫描到执行频率是否导致旧值覆盖。这条链路可以解决 80% 的数据显示错误。不要在第一步就怀疑硬件损坏大多数问题都是解释规则不一致。6. 最佳实践与工程建议6.1 建立 PLC 数据类型命名规范项目规模变大后变量命名直接影响排错效率。推荐在变量名里带上类型前缀例如bStartBtn布尔量按钮。wAlarmCodeWORD 报警代码。iCountINT 计数器。diPosDINT 位置值。rActualTempREAL 实际温度。dwStatusWordDWORD 状态字。这样在看到iCount时就能推断它是 16 位整数看到rActualTemp就知道是浮点。用变量表里的注释写清物理量和单位比事后回忆更可靠。6.2 通信数据一律显式转换同一份数据在不同层级之间传递时不要依赖隐式转换。Modbus 等通信协议本质上只传字节PLC 侧要明确寄存器中的 WORD 数据被当成 INT 还是 UINT。32 位数据对应两个连续寄存器先传高字节还是低字节。浮点数是否需要拆成两个 WORD 传输。推荐在通信映射函数中写显式转换。// 示例把 REAL 转成两个 WORD用于 Modbus 保持寄存器 // realValue 拆分 dwTemp : REAL_TO_DWORD(realValue); wHigh : WORD_TO_WORD(SHR(dwTemp, 16)); wLow : WORD_TO_WORD(dwTemp AND 16#FFFF);接收侧反推dwTemp : SHL(WORD_TO_DWORD(wHigh), 16) OR WORD_TO_DWORD(wLow); realValue : DWORD_TO_REAL(dwTemp);这样可以避免编译器隐式转换带来的警告也让后续维护人员一眼看出高低字节逻辑。6.3 保留进制转换工具函数到公共程序块把十六进制字符串转整数、二进制字符串转 WORD、BCD 转 INT 等常用函数放到公共程序块Library。以后新项目只需要复制不需要重新调试。注意每个品牌的库函数语法可能不同但逻辑相同迁移时主要改类型转换指令名称。6.4 学习路径建议从数据类型启动学习是正确方向但不要只停留在语法。建议按以下顺序继续深入。先在模拟器里完成“整数转二进制字符串”和“BCD 转十进制”两个练习。再用 Modbus 仿真工具把一个 REAL 变量通过两个寄存器发送给上位机软件验证字节序。接着做模拟量采集把 0~27648 的通道值线性转换成 0~100 摄氏度的实数并理解量程换算。最后尝试编写一个简单的通信解析块接收 10 个字节包含状态字、整型速度、浮点温度用 ST 语言解析。每一步都要求自己在监控表里同时看十六进制和十进制直到形成条件反射看到 16#FFFF 能立刻想到可能代表 -1看到 16#1234 能想到它既可能是普通数 4660也可能是 BCD 数 1234需要结合上下文判断。PLC 数据类型与进制转换并不难难的是把“存储 bit”和“业务含义”解耦。真正理解了类型是对内存的解释方式无论是西门子、三菱、汇川还是国产 PLC遇到新平台时只需要查一下指令名称就能快速迁移知识。这也是这篇实操内容希望帮你达到的效果。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →