资讯动态

工控人必知:字节与16进制实战解析与避坑指南

发布时间:2026/10/21 1:04:04 来源:尧图企业网站定制
1. 工控人绕不开的那道坎字节与16进制干工控这行的不管是搞PLC编程、做上位机开发还是调变频器、读仪表数据你迟早会撞上同一个东西——字节与16进制。这东西说难不难说简单也真不简单。我见过太多新手梯形图写得挺溜一碰到通信报文里那串01 03 00 00 00 02 C4 0B就懵了不知道哪个是地址、哪个是数据、哪个是校验。也见过干了三四年的老手在调试现场因为一个字节序搞反了对着设备折腾了大半天。这篇文章就是想把“字节与16进制”这件事从工控实际应用的角度彻底讲透。不是那种教科书式的进制转换教程而是结合我在现场踩过的坑、调过的设备、写过的代码告诉你这东西在工控里到底怎么用、哪里容易出错、怎么快速排查。不管你是刚入行的新手还是想补补基础的老师傅应该都能从里面找到对自己有用的东西。先把这个话题的边界划清楚工控领域里字节和16进制几乎无处不在。PLC的寄存器地址、通信协议的数据帧、传感器的原始读数、变频器的参数设置、触摸屏的变量地址……全都跟这两个概念绑在一起。你不需要成为计算机科学家但你必须做到看到一串16进制数就能条件反射地知道它代表什么、怎么拆、怎么拼、怎么转。这是工控人的基本功绕不过去的。2. 为什么工控领域偏偏钟爱16进制2.1 从PLC寄存器说起16进制是硬件的“母语”搞过PLC的人都知道三菱的D寄存器、西门子的DB块、欧姆龙的DM区地址通常都是用十进制表示的比如D100、DB1.DBW20。但你一旦打开通信手册或者用调试工具抓报文看到的全是16进制。为什么因为底层硬件的寻址和存储天然就是按位和字节组织的而16进制跟二进制之间的转换关系极其简单——1位16进制数恰好对应4位二进制数2位16进制数恰好对应1个字节8位。这种对齐关系让16进制成为描述二进制数据最自然的方式。举个例子一个字节的取值范围是0到255无符号用十进制写是255用16进制写是FF用二进制写是11111111。在工控通信中数据是按字节打包传输的每个字节的内容用16进制表示最紧凑、最不容易看错。你试试看一串二进制11000100 00001011和对应的16进制C4 0B哪个更容易读显然是后者。2.2 一个字节为什么是8位历史与工程的共同选择很多人知道一个字节等于8位但没想过为什么。这不是随便定的。早期计算机体系结构中字节的位数并不统一有6位、7位、9位的。最终8位成为标准有几个关键原因一是8位刚好能表示256个值足够覆盖ASCII字符集128个加扩展字符二是8是2的幂跟16进制、二进制之间的转换非常规整三是硬件设计上8位数据总线的成本与效率达到了最佳平衡。在工控领域这个8位的约定直接决定了你处理数据的方式。比如一个16位的模拟量输入在PLC里占一个寄存器16位但在通信报文里必须拆成两个字节来传输。高8位和低8位怎么排、先发哪个后发哪个这就是所谓的字节序问题后面会详细讲。你如果不理解“一个字节8位”这个基础后面所有关于数据拆分和拼接的操作都会一头雾水。2.3 16进制与二进制的换算工控人的肌肉记忆16进制和二进制之间的转换是工控人必须练到条件反射的基本功。规则很简单每一位16进制数对应4位二进制数。具体对应关系如下16进制二进制十进制000000100011200102300113401004501015601106701117810008910019A101010B101111C110012D110113E111014F111115这张表建议你打印出来贴在工位上用不了一周就能背下来。背下来之后你看到A3就能立刻反应出是10100011看到5C就知道是01011100。这个技能在调试通信的时候特别有用因为很多时候你需要判断某个字节的某一位是0还是1比如判断变频器的状态字里第3位是不是故障位这时候直接看16进制就能快速定位。3. 字节操作的核心技能拆分、拼接与字节序3.1 16位数据拆成两个字节高字节与低字节工控通信中最常见的操作之一就是把一个16位的寄存器值拆成两个字节发出去或者把收到的两个字节拼成一个16位值。比如PLC里一个温度值存在D100里数值是0x0A2B即十进制的2603假设分辨率是0.1度那就是260.3度。要通过串口发给上位机就得拆成高字节0A和低字节2B。拆分的方法很简单高字节等于原值除以256取整或者右移8位低字节等于原值对256取余或者与0xFF做按位与。用代码表示就是uint16_t value 0x0A2B; uint8_t high_byte (value 8) 0xFF; // 0x0A uint8_t low_byte value 0xFF; // 0x2B拼接则是反过来uint8_t high_byte 0x0A; uint8_t low_byte 0x2B; uint16_t value (high_byte 8) | low_byte; // 0x0A2B这两个操作看起来简单但实际项目中出问题往往就出在这里。我见过一个案例某工程师在拼接的时候忘了把高字节左移8位直接high_byte | low_byte结果得到的是0x2B数据完全不对。这种错误编译不会报错运行也不崩溃就是数据莫名其妙不对排查起来很费时间。3.2 大端与小端一个让无数工控人栽跟头的问题字节序Endianness是工控通信中最容易出问题的地方之一。简单说大端模式Big-Endian是高字节在前、低字节在后小端模式Little-Endian是低字节在前、高字节在后。同一个16位值0x0A2B大端发送是0A 2B小端发送是2B 0A。不同厂商、不同协议对字节序的规定不一样。三菱PLC的MC协议通常是大端西门子的S7协议也是大端但有些Modbus设备默认是小端还有些设备可以通过参数配置切换。更麻烦的是32位数据比如浮点数的字节序排列有四种可能大端、小端、大端字节交换、小端字节交换。我见过一个项目PLC发出去的浮点数在上位机显示一直是乱码查了两天才发现是字节序没对上。处理字节序问题的经验法则先查手册再抓报文最后写代码验证。不要凭经验猜不同品牌甚至同品牌不同型号都可能不一样。如果手册写得不清楚就用调试工具抓一段已知数据的报文对照实际值反推字节序。比如你让PLC发一个已知的浮点数1.0抓到的报文是3F 80 00 00那说明是大端如果是00 00 80 3F那就是小端。3.3 32位数据的处理浮点数与双字32位数据在工控里也很常见最典型的就是浮点数REAL类型和双字DWORD。一个32位浮点数占4个字节在通信中需要拆成4个字节传输。拆分和拼接的逻辑跟16位类似只是多了一层。以浮点数1.0为例它的IEEE 754标准表示是0x3F800000。拆成4个字节大端就是3F 80 00 00。如果设备是小端发送顺序就是00 00 80 3F。在PLC编程中很多平台提供了现成的转换指令比如西门子的SWAP指令可以交换高低字节三菱的SWAP指令也有类似功能。但如果你是在上位机用C#或Python写通信程序就得自己处理。Python中处理32位浮点数的字节序import struct # 浮点数转字节大端 value 1.0 bytes_big struct.pack(f, value) # b\x3f\x80\x00\x00 # 浮点数转字节小端 bytes_little struct.pack(f, value) # b\x00\x00\x80\x3f # 字节转浮点数大端 result struct.unpack(f, b\x3f\x80\x00\x00)[0] # 1.0C#中可以用BitConverter类但要注意BitConverter默认使用系统字节序在x86架构上是小端。如果需要大端得手动反转数组。4. 通信报文实战从一串16进制数读出设备状态4.1 Modbus RTU报文拆解每个字节都有意义拿最常见的Modbus RTU协议举例。假设主站发送读保持寄存器的请求从站地址是0x01起始地址0x0000读2个寄存器。报文如下01 03 00 00 00 02 C4 0B逐字节拆解字节位置值含义第1字节01从站地址第2字节03功能码读保持寄存器第3-4字节00 00起始地址大端第5-6字节00 02寄存器数量大端第7-8字节C4 0BCRC校验低字节在前从站回复的报文可能是01 03 04 0A 2B 00 64 XX XX拆解字节位置值含义第1字节01从站地址第2字节03功能码第3字节04数据字节数2个寄存器4字节第4-5字节0A 2B第1个寄存器值大端第6-7字节00 64第2个寄存器值大端第8-9字节XX XXCRC校验你看如果你不懂16进制和字节的概念这串数据对你来说就是天书。但一旦掌握了拆解方法每个字节代表什么、数据怎么解析一目了然。这就是为什么我说这是工控人的基本功。4.2 CRC校验的计算手算一遍就懂了Modbus RTU的CRC校验是很多新手的噩梦。校验值是两个字节低字节在前、高字节在后。计算方法是初始化CRC为0xFFFF对每个字节与CRC异或然后右移8次每次如果最低位是1就异或多项式0xA001。以报文01 03 00 00 00 02为例手算过程如下这里只展示逻辑实际计算建议用代码def modbus_crc(data): crc 0xFFFF for byte in data: crc ^ byte for _ in range(8): if crc 0x0001: crc (crc 1) ^ 0xA001 else: crc 1 return crc data bytes([0x01, 0x03, 0x00, 0x00, 0x00, 0x02]) crc modbus_crc(data) print(fCRC: {crc:04X}) # 输出 0x0BC4 # 发送时低字节在前C4 0B算出来是0x0BC4发送时低字节在前就是C4 0B跟报文对上了。我建议每个工控人都至少手算一次CRC不是为了在实际项目中手算而是为了理解校验的原理。理解了之后你调试通信的时候看到CRC错误就知道是数据在传输过程中被干扰了还是自己拼报文的时候算错了。4.3 从报文到实际值解析传感器数据假设你有一个温度传感器通过Modbus RTU输出温度值寄存器地址是0x0000数据类型是16位有符号整数单位是0.1度。你读到的寄存器值是0x00FA怎么算实际温度0x00FA转十进制是250乘以0.1就是25.0度。如果读到的值是0xFF38这是一个负数有符号整数转十进制是-200实际温度就是-20.0度。这里就涉及到有符号数和无符号数的区别。在工控中很多传感器数据是有符号的你需要根据手册确定数据类型然后在代码里正确解析。Python中解析有符号16位整数import struct raw b\xff\x38 value struct.unpack(h, raw)[0] # -200 temperature value * 0.1 # -20.0如果搞错了符号类型把有符号当无符号解析0xFF38会变成65336乘以0.1就是6533.6度显然不对。这种错误在实际项目中很常见尤其是新手容易忽略数据类型。5. 工控现场常见问题与排查技巧5.1 数据对不上先查字节序再查数据类型现场调试最常遇到的问题就是“数据对不上”。上位机显示的值跟PLC里的值不一样或者跟实际物理量对不上。排查顺序应该是先确认字节序再确认数据类型最后确认缩放系数。字节序问题前面讲过了这里补充一个快速判断的方法如果读到的值跟预期值刚好是256倍的关系比如预期25但读到6400那很可能是高低字节反了。因为25是0x0019高低字节互换后变成0x1900也就是6400。这个规律在调试的时候特别管用一眼就能看出是字节序问题。数据类型问题也很常见。比如一个16位寄存器你按无符号解析是0到65535按有符号解析是-32768到32767。如果传感器输出的是有符号值你按无符号解析负数就会变成很大的正数。判断方法如果读到的值在32768到65535之间而实际物理量应该是负数那基本可以确定是符号类型搞错了。5.2 通信超时与校验错误从物理层到应用层逐层排查通信超时和CRC校验错误是工控通信中的高频问题。排查思路应该从物理层开始逐层往上排查层级检查内容常见问题物理层接线、终端电阻、屏蔽接地线接反、终端电阻未接、干扰大链路层波特率、数据位、停止位、校验位参数不匹配应用层从站地址、功能码、寄存器地址地址错误、功能码不支持数据层字节序、数据类型、缩放系数解析方式错误我遇到过的案例某现场Modbus通信时好时坏CRC错误率很高。查了参数、换了线缆都没用。最后发现是变频器干扰把通信线跟动力线捆在一起走了。分开走线之后问题解决。这个案例说明很多通信问题不是软件问题而是物理层的问题。排查的时候不要一上来就怀疑代码先看看硬件环境。5.3 浮点数解析乱码IEEE 754格式的坑浮点数解析乱码是另一个高频问题。根本原因通常是字节序不对或者把浮点数当整数解析了。IEEE 754单精度浮点数占4个字节格式是1位符号位、8位指数位、23位尾数位。如果你把这4个字节当两个16位整数读得到的值跟实际浮点数完全对不上。判断方法如果读到的4个字节转换成浮点数后是一个极大或极小的值比如1.5e38或1.2e-40或者是一个完全不合理的数据那基本可以确定是字节序问题。尝试把4个字节的顺序反转再解析一次往往就能得到正确的值。注意有些设备的浮点数格式不是标准IEEE 754而是厂商自定义的。这种情况必须查手册不能靠猜。5.4 常见问题速查表现象可能原因排查方法读到的值是预期值的256倍高低字节反了交换高低字节重新解析负数变成大正数有符号当无符号解析改用有符号类型解析浮点数显示为乱码字节序错误或类型错误尝试4种字节序排列CRC校验一直失败报文拼接错误或干扰检查报文内容检查接线通信时好时坏物理层干扰或参数不稳检查屏蔽接地分开走线读到的值跳变严重未做滤波或采样周期太短增加滤波或延长采样周期6. 工具与技巧让字节操作更高效6.1 常用调试工具推荐工控调试离不开好用的工具。串口调试助手是必备的推荐用支持16进制收发和CRC自动计算的版本。网络调试助手用于调试TCP/UDP通信。Modbus Poll和Modbus Slave是调试Modbus协议的利器可以模拟主站和从站。PLC编程软件自带的在线监控功能也很重要可以实时查看寄存器值。我个人习惯用Python写一些小脚本来辅助调试。比如批量解析报文、自动计算CRC、模拟设备响应等。Python的struct模块和binascii模块处理字节数据非常方便几行代码就能完成复杂的解析任务。6.2 手算与心算技巧虽然实际项目中都用工具计算但一些快速心算技巧能帮你在现场快速判断问题。比如16进制转十进制记住0x64100、0xFF255、0x100256、0x3E81000这几个关键值其他的可以估算。判断字节序如果读到的值跟预期值刚好是256倍关系就是高低字节反了。判断符号如果读到的值大于32767而实际物理量应该是负数就是符号类型问题。CRC快速验证如果CRC错误先检查报文长度和内容再检查接线和干扰。这些技巧看起来简单但在现场调试的时候能帮你快速缩小问题范围节省大量时间。6.3 代码模板字节操作常用函数这里整理几个工控开发中常用的字节操作函数用Python和C#各写一份可以直接拿去用。Python版本import struct def bytes_to_uint16(high, low): 两个字节拼成无符号16位整数大端 return (high 8) | low def bytes_to_int16(high, low): 两个字节拼成有符号16位整数大端 value (high 8) | low if value 32767: value - 65536 return value def uint16_to_bytes(value): 无符号16位整数拆成两个字节大端 return [(value 8) 0xFF, value 0xFF] def bytes_to_float32(data): 4个字节转浮点数大端 return struct.unpack(f, bytes(data))[0] def float32_to_bytes(value): 浮点数转4个字节大端 return list(struct.pack(f, value))C#版本public static ushort BytesToUInt16(byte high, byte low) { return (ushort)((high 8) | low); } public static short BytesToInt16(byte high, byte low) { return (short)((high 8) | low); } public static byte[] UInt16ToBytes(ushort value) { return new byte[] { (byte)(value 8), (byte)(value 0xFF) }; } public static float BytesToFloat32(byte[] data) { if (BitConverter.IsLittleEndian) Array.Reverse(data); return BitConverter.ToSingle(data, 0); }这些函数覆盖了工控开发中最常见的字节操作场景。建议你根据自己的项目需求整理一套自己的工具库用的时候直接调用避免每次重复写。7. 从字节到系统工控数据处理的完整链路7.1 数据采集层从物理信号到数字量工控系统的数据处理链路起点是物理信号。传感器把温度、压力、流量等物理量转换成电信号4-20mA、0-10V等然后经过AD转换变成数字量。这个数字量通常是一个16位的整数存在PLC的模拟量输入寄存器里。比如西门子的S7-1200模拟量输入通道的值范围是0到27648对应4-20mA的电流信号。这个27648是怎么来的它是西门子定的一个标准对应满量程的20mA。0对应4mA27648对应20mA。如果你要算实际物理量比如一个压力变送器的量程是0-10MPa输出4-20mA那么实际压力 (寄存器值 / 27648) × 10MPa这个计算过程就涉及到整数到浮点数的转换以及缩放系数的应用。如果你不懂字节和数据类型这一步就会出错。7.2 数据处理层缩放、滤波与报警采集到的原始数据需要经过处理才能使用。常见的处理包括缩放把原始值转换成实际物理量、滤波去除噪声、报警判断超限报警。这些处理在PLC里通常用梯形图或SCL语言实现在上位机里用C#或Python实现。滤波算法有很多种最简单的是算术平均滤波取最近N次采样的平均值。还有中值滤波、一阶滞后滤波等。选择哪种滤波算法取决于信号的噪声特性和响应速度要求。比如温度信号变化慢可以用较强的滤波压力信号变化快滤波太强会导致响应滞后。报警判断通常是比较操作但要注意数据类型的匹配。比如你把一个16位有符号整数跟一个浮点数比较PLC可能会自动转换类型但转换过程中可能丢失精度。建议在比较之前统一数据类型避免隐式转换带来的问题。7.3 数据通信层打包、发送、接收、解析通信层是字节操作最集中的地方。发送数据时需要把各种数据类型整数、浮点数、布尔量打包成字节流接收数据时需要把字节流解析成对应的数据类型。这个过程涉及到前面讲的所有知识点字节拆分、字节序、数据类型、校验计算。一个完整的通信流程通常是组包按协议格式拼接字节→ 计算校验 → 发送 → 等待响应 → 接收 → 校验 → 解析 → 处理。每一步都可能出问题所以调试的时候要逐段验证。我的习惯是先用调试工具手动发一帧报文确认设备能正常响应然后再写代码。这样可以把协议问题和代码问题分开排查。7.4 数据展示层从字节到人机界面最后一步是把数据显示给人看。触摸屏、上位机软件、Web页面都是数据展示层。这一层的关键是格式化和单位转换。比如温度值25.3度在界面上显示为“25.3 ℃”保留一位小数。压力值0.65MPa显示为“0.65 MPa”保留两位小数。格式化看起来简单但实际项目中经常出问题。比如浮点数精度丢失导致显示为“25.300000000000001”或者单位转换错误导致显示值差1000倍。这些问题往往是因为在数据处理层没有统一单位到了展示层才做转换容易出错。建议在数据处理层就把单位统一好展示层只负责格式化。8. 我踩过的那些坑与经验总结8.1 一个字节序问题折腾了一整天早期做项目的时候用某品牌的PLC跟一个第三方仪表通信。仪表手册上写的是Modbus RTU寄存器地址、数据类型都写得很清楚。我按手册写了代码读上来的数据就是不对。温度值应该是25度左右读上来是6400。我一开始怀疑是缩放系数错了试了好几种系数都不对。后来突然想到可能是字节序问题把高低字节交换了一下25度就出来了。这个经历让我记住了一个规律读到的值是预期值的256倍就是高低字节反了。因为25是0x0019交换后是0x1900正好是6400。这个规律后来帮我快速定位了好几次类似的问题。8.2 CRC校验错误的排查思路还有一次现场调试Modbus通信CRC错误率很高大概每10帧就有3-4帧错误。我先检查了报文内容确认CRC计算没问题。然后检查了波特率、数据位、停止位也都对。最后把注意力转到物理层发现通信线跟变频器的动力线捆在一起走了。把通信线分开走线并加了屏蔽接地之后CRC错误率降到了几乎为零。这个案例说明通信问题不一定是软件问题。排查的时候要有层次感从物理层到应用层逐层排查不要一上来就怀疑代码。另外现场环境对通信质量的影响很大布线、接地、屏蔽这些细节不能忽视。8.3 浮点数解析的四种字节序排列32位浮点数的字节序排列有四种可能大端ABCD、小端DCBA、大端字节交换BADC、小端字节交换CDAB。我遇到过一种设备用的是CDAB排列手册上还没写清楚。当时试了前三种都不对试到第四种才成功。我的经验是遇到浮点数解析不对先把四种排列都试一遍。如果四种都不对那可能是数据类型不是浮点数或者数据本身有问题。另外有些设备支持通过参数设置字节序如果手册写了可以配置优先在设备端配置好比在代码里处理更可靠。8.4 给新手的几点建议如果你刚入行面对字节和16进制觉得头大我的建议是多动手多抓报文多写代码验证。看十遍书不如自己抓一次报文分析一遍。找一个Modbus设备或者用软件模拟手动发几帧报文看看回复是什么逐字节分析。分析多了自然就有感觉了。另外养成整理工具库的习惯。把常用的字节操作函数、CRC计算函数、报文解析函数整理成自己的库用的时候直接调用。这样不仅能提高效率还能减少重复劳动带来的错误。最后遇到问题不要慌。字节和16进制的问题归根结底就是那么几类字节序、数据类型、缩放系数、校验计算。按照这个顺序排查大部分问题都能解决。现场调试的时候耐心和细心比技术更重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑