4、功能码详解(上):01读线圈、02读离散输入、03读保持寄存器、04读输入寄存器

各位同行,咱们今天聊聊Modbus最基础、也最常用的四个功能码。说实话,搞楼宇自控这么多年,我打交道最多的就是这四位“老兄”。你想想看,现场那些传感器、执行器,说白了就是靠它们来“对话”的。

我个人习惯把这四个功能码分成两组:位操作字操作。01和02是读“位”的,03和04是读“字”的。先搞清楚这个分类,后面就好办了。

4.1 功能码01:读线圈(Read Coils)

功能码01,用来读取从设备的线圈状态。线圈是什么?就是开关量输出。在楼宇里,最常见的线圈就是继电器、接触器、电磁阀这些。

报文格式

请求报文(主站→从站):

从站地址 功能码 起始地址高字节 起始地址低字节 数量高字节 数量低字节 CRC校验
01        01      00            00            00          0A          CRC

响应报文(从站→主站):

从站地址 功能码 字节数 数据1 数据2 ... 数据N CRC校验
01        01      02     CD    6B          CRC

实际应用场景

我在项目中遇到过这样一个情况:一个空调机组的送风机启停状态,就是通过线圈来反馈的。主站每隔1秒发一次01功能码,读取风机接触器的辅助触点状态。

关键点:线圈地址从0开始编号。比如你图纸上标的“DO-1”,在Modbus里可能就是地址0。这个坑我踩过,图纸和程序对不上,查了半天才发现是地址偏移的问题。

我的小技巧:读线圈时,一次不要读太多。我一般控制在200个以内。为什么?因为响应报文太长,容易出错。你想想看,一个报文里塞几百个位,万一中间丢一个,整个包就废了。

4.2 功能码02:读离散输入(Read Discrete Inputs)

功能码02,读取离散输入状态。离散输入就是开关量输入,比如按钮、限位开关、水流开关这些。它和线圈最大的区别是:只读不写

报文格式

请求报文:

从站地址 功能码 起始地址高字节 起始地址低字节 数量高字节 数量低字节 CRC校验
01        02      00            00            00          05          CRC

响应报文:

从站地址 功能码 字节数 数据1 CRC校验
01        02      01     0A    CRC

实际应用场景

我记得有个项目,冷机群控系统里,每台冷机的“手自动状态”就是通过离散输入上报的。主站用02功能码轮询,判断冷机是在本地控制还是远程控制。

注意:离散输入和线圈虽然都是位,但千万别搞混。我曾经见过有人用01功能码去读离散输入,结果读回来的数据全是乱的。为什么?因为01是读线圈的,02才是读离散输入的。协议规定死了,不能混用。

4.3 功能码03:读保持寄存器(Read Holding Registers)

功能码03,读取保持寄存器。这是Modbus里最常用的功能码,没有之一。保持寄存器是16位的,可读可写。在楼宇自控里,温度、湿度、压力、流量这些模拟量,基本都是通过保持寄存器来传输的。

报文格式

请求报文:

从站地址 功能码 起始地址高字节 起始地址低字节 数量高字节 数量低字节 CRC校验
01        03      00            01            00          02          CRC

响应报文:

从站地址 功能码 字节数 数据1高 数据1低 数据2高 数据2低 CRC校验
01        03      04     00      64      00      C8      CRC

实际应用场景

举个具体的例子。一个风管温度传感器,量程0~50℃,输出4~20mA,经过AI模块转换成数字量。这个数字量就存在保持寄存器里。主站用03功能码读回来,再根据公式换算成实际温度。

避坑指南:我曾经遇到过一个问题:读回来的温度值忽高忽低。排查了半天,发现是寄存器地址搞错了。图纸上写的是“40001”,但实际Modbus地址是0。记住:PLC地址和Modbus地址差1。40001对应Modbus地址0,40002对应地址1,以此类推。

我的习惯:读保持寄存器时,一次读多个连续的寄存器。比如读一个温度传感器,我通常会把温度、湿度、压力一起读回来。这样效率高,一个报文搞定三个数据。但注意,一次最多读125个寄存器(250字节),别超了。

4.4 功能码04:读输入寄存器(Read Input Registers)

功能码04,读取输入寄存器。输入寄存器和保持寄存器很像,都是16位的。唯一的区别是:输入寄存器只读不写。它通常用来读取传感器采集的原始数据。

报文格式

请求报文:

从站地址 功能码 起始地址高字节 起始地址低字节 数量高字节 数量低字节 CRC校验
01        04      00            00            00          03          CRC

响应报文:

从站地址 功能码 字节数 数据1高 数据1低 数据2高 数据2低 数据3高 数据3低 CRC校验
01        04      06     00      01      00      02      00      03      CRC

实际应用场景

我记得有个项目,用的是进口的温湿度变送器。它的温度值放在输入寄存器里,湿度值也放在输入寄存器里。主站用04功能码读回来,然后做数据处理。

重要提醒:03和04虽然都是读寄存器,但千万别混用。我见过有人用03去读输入寄存器,结果读回来的数据全是0。为什么?因为03是读保持寄存器的,04才是读输入寄存器的。协议规定死了,不能混用。

4.5 四个功能码的对比总结

功能码 名称 操作对象 读写属性 数据宽度 典型应用
01 读线圈 开关量输出 只读 1位 继电器状态、风机启停
02 读离散输入 开关量输入 只读 1位 按钮状态、限位开关
03 读保持寄存器 模拟量输出 可读可写 16位 温度、压力、设定值
04 读输入寄存器 模拟量输入 只读 16位 传感器原始数据

嗯,这四个功能码,说白了就是Modbus通信的“四把钥匙”。搞懂了它们,你就掌握了Modbus读操作的精髓。下一章咱们聊写操作——05写单线圈、06写单寄存器、15写多线圈、16写多寄存器。到时候你会发现,写操作比读操作更有意思。

一句话总结:01和02读位,03和04读字。位操作看开关,字操作看模拟量。记住这个,你就不会搞混了。