4、功能码详解(一):01H读线圈、02H读离散输入、03H读保持寄存器、04H读输入寄存器
好,咱们今天来啃Modbus RTU里最基础、也最常用的四个功能码。说白了,这哥儿四个就是PLC和上位机之间最常用的“读”操作。我刚开始搞工业通信那会儿,就是靠这几个功能码把现场设备一个个“盘活”的。
你想想看,一个自动化系统里,你总得知道开关按没按、传感器通没通、温度是多少、压力是多少吧?这四个功能码,正好覆盖了这些需求。咱们一个一个来拆解。
4.1 01H - 读线圈(Read Coils)
这个功能码,读的是PLC或者远程IO模块里的数字量输出。也就是那些继电器、接触器、指示灯的状态。嗯,这里要注意:它读的是输出线圈,不是输入。
我记得有一次在现场调试,一个老电工非说01H能读按钮状态,结果折腾了半天。其实按钮是输入,得用02H。这个坑我踩过,你们别踩。
报文格式:
请求:01 01 00 00 00 0A 3D CC
响应:01 01 02 CD 0B B7 8C
我来拆解一下:
- 请求报文:从站地址01,功能码01,起始地址0000(从第0个线圈开始),读取10个线圈(000A),CRC校验3DCC。
- 响应报文:从站地址01,功能码01,数据长度02字节,数据CD 0B(二进制:1100 1101 0000 1011),CRC校验B78C。
这里有个细节:线圈地址是从0开始的。但在很多组态软件里,你看到的地址可能是00001这种格式。其实它对应的是协议里的地址0。我个人习惯在写程序时,直接按协议地址来,省得换算搞晕。
重点:01H读的是输出线圈,不是输入。每个线圈占1位,返回的数据按字节打包,高位在前。
我的小技巧:如果你不确定读回来的数据对不对,拿个计算器把十六进制转成二进制,对着位图看。我当年就是这么干的,虽然慢,但踏实。
4.2 02H - 读离散输入(Read Discrete Inputs)
这个功能码,读的是数字量输入。比如按钮、限位开关、光电传感器的状态。说白了,就是读“外部世界给PLC的信号”。
报文结构和01H几乎一模一样,就是功能码从01变成了02。
请求:01 02 00 00 00 0A 79 C9
响应:01 02 02 CD 0B B8 0C
你看,除了功能码和CRC,其他都一样。但千万别搞混。我见过有人把02H当01H用,结果读回来的数据全是0,还以为是设备坏了。其实是你读错了对象。
应用场景:
- 读取按钮状态(启动、停止、急停)
- 读取传感器信号(光电、接近、磁性开关)
- 读取设备状态(运行、故障、就绪)
注意:离散输入是只读的。你不能通过Modbus去写它。想控制输出?那是05H、0FH的事,后面会讲。
4.3 03H - 读保持寄存器(Read Holding Registers)
这个功能码,可以说是Modbus里最常用的一个。它读的是保持寄存器,也就是那些可以读也可以写的寄存器。比如变频器的频率设定值、PID的目标值、设备的运行参数。
为什么它最常用?因为保持寄存器既能读又能写,灵活性最高。我做过一个项目,整个上位机监控系统,90%的通信都是靠03H和06H(写单个寄存器)完成的。
报文格式:
请求:01 03 00 00 00 02 C4 0B
响应:01 03 04 00 0A 00 14 7A 5E
拆解一下:
- 请求:从站地址01,功能码03,起始地址0000,读取2个寄存器(0002),CRC校验C40B。
- 响应:从站地址01,功能码03,数据长度04字节(2个寄存器×2字节),数据00 0A(十进制10)和00 14(十进制20),CRC校验7A5E。
这里有个关键点:每个寄存器是16位的,也就是2个字节。读取N个寄存器,返回的数据长度就是N×2字节。这个千万别算错,不然解析数据会乱套。
重要:保持寄存器的地址范围通常是40001-4xxxx(PLC地址),对应协议地址0-65535。但不同厂家可能偏移不同,一定要看设备手册。
避坑指南:我曾经遇到过一台温控器,手册上写保持寄存器地址是40001,但实际协议地址是0。结果我按40001发请求,设备死活不回。后来才发现,它用的是0基址。所以,一定要确认地址偏移。
4.4 04H - 读输入寄存器(Read Input Registers)
这个功能码,读的是输入寄存器。它和03H的区别在于:输入寄存器是只读的。通常用来读取模拟量输入,比如温度、压力、流量等传感器的测量值。
你想想看,一个4-20mA的变送器,把温度信号变成电流,PLC的模拟量模块把它转成数字量。这个数字量,就存在输入寄存器里。你想读它?用04H。
请求:01 04 00 00 00 02 71 CB
响应:01 04 04 01 2C 00 FA 7B 9E
报文结构和03H完全一样,就是功能码不同。返回的数据格式也一样,每个寄存器2个字节。
应用场景:
- 读取模拟量输入(4-20mA、0-10V)
- 读取传感器原始值(AD转换结果)
- 读取设备状态字(只读的状态信息)
注意:输入寄存器是只读的。你不能通过Modbus去修改它。想写参数?请用03H对应的保持寄存器。
4.5 四个功能码对比总结
好了,咱们把这四个功能码放在一起对比一下,这样你心里就有数了。
| 功能码 | 名称 | 读写属性 | 数据单位 | 典型应用 |
|---|---|---|---|---|
| 01H | 读线圈 | 只读(输出) | 位(1 bit) | 继电器、指示灯、接触器 |
| 02H | 读离散输入 | 只读(输入) | 位(1 bit) | 按钮、限位开关、传感器 |
| 03H | 读保持寄存器 | 可读可写 | 字(16 bit) | 参数设定、运行数据 |
| 04H | 读输入寄存器 | 只读 | 字(16 bit) | 模拟量输入、传感器值 |
说白了,01H和02H是读“开关量”的,一个读输出,一个读输入。03H和04H是读“模拟量”的,一个可读可写,一个只能读。记住这个,你就不会用错了。
我的建议:刚开始学的时候,先拿一个Modbus调试工具(比如ModScan、Modbus Poll),对着一个真实的从站设备,一个一个功能码试。读回来的数据,对照手册看对不对。这样上手最快。
嗯,这四个功能码就讲到这里。下一节咱们讲写操作:05H写单个线圈、06H写单个寄存器、0FH写多个线圈、10H写多个寄存器。到时候你会发现,写操作比读操作要小心得多,因为写错了可是会出事故的。