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写多个寄存器。到时候你会发现,写操作比读操作要小心得多,因为写错了可是会出事故的。