Modbus全功能码详解(持续更新中)

 
前言:本文是个人基于Modbus协议英文原版说明书为基础,多方面考证理解后进行的理解性翻译。网络上现有的解释驳杂不清,多数功能码也没有详细解释。既然没有轮子,荷取就只能自己造了。说实话准备翻译校对和整理的时候根本没多想.....真干起来才发现难度比预想的要大得多。借助了gpt翻译,但gpt翻译得也不尽如人意,糟糕的中英语法逻辑转换加之gpt的胡言乱语,四处校对和钻研挺花时间,预计再过个几天就能利用空闲时间消化、整理、翻译、转述完毕吧。

0x01 读线圈状态(Read Coils)

该功能码用于从远程设备中连续读取1到2000(0xFFFF)个线圈的状态。使用方法是请求PDU指定起始地址(第一个指定线圈的地址)+线圈的数量。在PDU中,线圈从零开始寻址。因此,编号为1-16的线圈被寻址为0-15。
响应消息中的线圈被打包为N个数据字段,字段中每个位代表一个线圈,1=打开,0=关闭。如果输出数量不是8的倍数,则增加一个字节,最后一个数据字节中的剩余位将用零填充(向字节的高位填充)。

 在这里首先明确一个基本概念:字节序和位序

在前篇的数据结构中已经说明,modbus采用大端字节序,会将大数据分割成多个字节,优先传输高位。这里的顺序指的是字节发送的顺序而非位序。而在串行通信中,对于位的传输都是从低位到高位,该顺序与字节的顺序相反。,因此我们可以看到如下实例:

 如图实例,输出27-20的状态以字节值CD十六进制或二进制1100 1101表示。输出27是该字节的最高有效位(MSB),而输出20是最低有效位(LSB)。

按照惯例,字节内的位从左到右显示,最高有效位(MSB)在左侧,最低有效位(LSB)在右侧。因此,第一个字节中的输出是从左到右的“27到20”。下一个字节中的输出是从左到右的“35到28”。由于位是串行传输的,它们从最低有效位(LSB)流向最高有效位(MSB):20 . . . 27,28 . . . 35,依此类推。
在最后一个数据字节中,输出38-36的状态以字节值05十六进制或二进制0000 0101表示。输出38位于从左侧开始的第六位位置,而输出36是该字节的最低有效位(LSB)。剩下的五个高位位都是零。

0x02 读离散输入状态(Read Discrete Inputs )

0x02和0x01基本一致,唯一的区别就是离散输入是只读的。离散输入是只读的输入,线圈是可控的输出。

0x03 读保持寄存器(Read Holding Registers )

该功能码用于读取远程设备中连续一块保持寄存器的内容。和线圈类似,请求PDU=起始地址+寄存器数量。在PDU中,寄存器从零开始寻址。因此,寄存器编号为1-16的寄存器被寻址为0-15。最多允许读125个寄存器(125*8=2000)
响应消息中的寄存器数据以每个寄存器两个字节的形式打包,二进制内容在每个字节中右对齐。对于每个寄存器,第一个字节包含高位,第二个字节包含低位。

 

 实例如图,请求报文为03(功能码)+00(起始地址高位字节)+6B(起始地址低位字节)+00(寄存器地址高位字节)+03(寄存器地址低位字节)

响应报文为03(功能码响应)+06(接下来的数据大小,6个字节)+02(0x6B+1=0x6C,也就是十进制108,108号寄存器的高位)+2B(108号寄存器的低位)+00(109高位)+00(109低位)+00(110高位)+64(110低位)

由于此处寄存器都是对字节的操作,因此不需要考虑位序的问题。

0x04 读输入寄存器(Read Input Registers)

和0x03一样,区别只在于输入寄存器是只读的。

0x05 写单线圈(Write Single Coil )

简单来说就是把单个输出设置为打开或关闭状态。值为FF 00为打开状态。值为00 00则是关闭状态。其他任意值都为非法,都不改变输出状态。
依旧是请求PDU从0开始寻址,寻址方式都和之前如出一辙。正常的响应是在写入线圈状态后返回请求的回显。也就是请求啥响应啥。

 

 具体看实例就明白了了。如图,格式就是功能码+输出地址高位+低位+输出(写入)值高位+输出(写入)值低位,响应报文则和输入报文一模一样。

0x06 写单寄存器(Write Single Register)

和0x05基本上一模一样,区别只有目标对象是寄存器而已

0x07 读取异常状态(限串行线)(Read Exception Status (Serial Line only) )

有的设备自带异常状态输出,该功能码用于读取远程设备中八个异常状态输出的内容。由于这8个状态是由设备预先定义,modbus协议本身也定义了0-7为异常状态输出,因此不需要额外的地址或者输出引用。
正常的响应包含了八个异常状态输出的状态。这些输出被打包到一个数据字节中,每个输出占用一个位。最低输出引用的状态包含在字节的最低有效位中。

 如图,实例中返回6D,将其作为BCD码转化回二进制则为01101101。输出时从最高地址到最低地址的依次显示,也就是从右到左,从最高有效地址位到最低有效地址位。

0x08 通信系统诊断(限串行线)(Diagnostics (Serial Line only) )

功能码08用于测试客户端设备和服务器之间通信系统,或检查服务器内部的各种错误条件。

08功能码还需要一个大小为2Bytes的子功能码来定义要执行的测试类型。服务器会在正常响应中同时回显功能码和子功能码。某些诊断功能会导致设备的数据在正常响应的数据字段中返回。
通常情况下,向远程设备发出诊断功能不会影响远程设备中运行的用户程序。诊断功能不会访问用户逻辑单元,如离散和寄存器。某些功能可以选择性地重置远程设备中的计数器。
另外,服务器设备可以被强制进入“仅监听模式”,在该模式下,它将监视通信系统上的消息,但不对其进行响应。该模式可能会影响应用程序和设备间的数据交换。该模式的执行一般用于将有故障的设备在通信系统中移除。
对于“返回查询数据”请求的正常响应是将相同的数据回送。功能码和子功能码也会被回显。
 
 

 

 接下来列出其子功能码:

 

以下是翻译后的内容:

00 返回查询数据

请求数据字段中传递的数据将在响应中返回(回送)。整个响应消息应与请求完全相同。
子功能请求数据字段响应数据字段
0000 任意 回显

01 重新启动通信选项

初始化和重新启动远程设备的串行线端口,并清除所有通信事件计数器。如果端口当前处于仅监听模式,则不返回响应。此功能是唯一将端口从仅监听模式中退出的功能。如果端口当前不处于仅监听模式,则会在重启之前返回正常响应。
当远程设备接收到请求时,它会尝试重新启动和自检测试,成功完成测试将使端口上线。
请求数据字段为FF 00十六进制会导致端口的通信事件日志也被清除。请求数据字段为00 00将保留日志在重新启动之前的状态。
子功能请求数据字段响应数据字段
0001 00 00 00 01 00 00
0001 FF 00 00 01 FF 00

02 返回诊断寄存器

响应中返回远程设备的16位诊断寄存器的内容。

子功能请求数据字段响应数据字段
0002 00 00 诊断寄存器内容

 

03 更改ASCII输入分隔符

请求数据字段中传递的字符'CHAR'将成为未来消息的结束分隔符(替代默认的LF字符)。在ASCII消息末尾不需要换行符的情况下,此功能非常有用。
子功能请求数据字段响应数据字段
0003 CHAR 00 回显请求数据


04 强制进入仅监听模式

强制被寻址的远程设备进入仅监听模式,用于MODBUS通信。这将使其与网络上的其他设备隔离,允许它们在不受干扰的情况下继续通信。不返回响应。
当远程设备进入仅监听模式时,所有活动的通信控制都被关闭。准备就绪的看门狗定时器将被允许超时,关闭控制。在设备处于此模式时,将监视寻址到它或广播的任何MODBUS消息,但不会执行任何操作,也不会发送任何响应。
在进入该模式后,唯一会被处理的功能是重新启动通信选项功能(功能码8,子功能1)。
子功能请求数据字段响应数据字段
0004 00 00 无响应返回

10 (0A Hex) 清除计数器和诊断寄存器

目标是清除所有计数器和诊断寄存器。计数器也会在上电时被清除。
子功能请求数据字段响应数据字段
0010 00 00 回显请求数据

11 (0B Hex) 返回总线消息计数

响应数据字段返回远程设备自上次重新启动、清除计数器操作或上电以来在通信系统上检测到的消息数量。
子功能请求数据字段响应数据字段
0011 00 00 总消息计数

12 (0C Hex) 返回总线通信错误计数

响应数据字段返回远程设备自上次重新启动、清除计数器操作或上电以来遇到的CRC错误数量。
子功能请求数据字段响应数据字段
0012 00 00 CRC错误计数

13 (0D Hex) 返回总线异常错误计数

响应数据字段返回远程设备自上次重新启动、清除计数器操作或上电以来返回的MODBUS异常响应数量。
异常响应在第7节中有描述和列出。

子功能

请求数据字段

响应数据字段

0013

00 00

异常错误计数

14 (0E Hex) 返回服务器消息计数

响应数据字段返回自上次重新启动、清除计数器操作或上电以来远程设备处理的寻址到该远程设备或广播的消息数量。
子功能请求数据字段响应数据字段
0014 00 00 服务器消息计数

15 (0F Hex) 返回服务器无响应计数

响应数据字段返回自上次重新启动、清除计数器操作或上电以来寻址到远程设备但未返回任何响应(既不是正常响应也不是异常响应)的消息数量。
子功能请求数据字段响应数据字段
0015 00 00 服务器无响应计数

16 (10 Hex) 返回服务器NAK计数

响应数据字段返回自上次重新启动、清除计数器操作或上电以来寻址到远程设备并返回否定应答(NAK)异常响应的消息数量。异常响应在第7节中有描述和列出。
子功能请求数据字段响应数据字段
0016 00 00 服务器NAK计数

17 (11 Hex) 返回服务器忙计数

响应数据字段返回自上次重新启动、清除计数器操作或上电以来寻址到远程设备并返回服务器设备忙异常响应的消息数量。
子功能请求数据字段响应数据字段
0017 00 00 服务器设备忙计数

18 (12 Hex) 返回总线字符溢出计数

响应数据字段返回自上次重新启动、清除计数器操作或上电以来寻址到远程设备但由于字符溢出条件而无法处理的消息数量。字符溢出是由数据字符到达端口的速度超过存储速度,或由于硬件故障导致字符丢失引起的。
子功能请求数据字段响应数据字段
0018 00 00 服务器字符溢出计数

20 (14 Hex) 清除溢出计数器和标志

清除溢出错误计数器并重置错误标志。
子功能请求数据字段响应数据字段
0020 00 00 回显请求数据

 

实例如下:

 如图,请求报文为功能码08+子功能码高位00+子功能码低位00(子功能码0000,实现返回查询数据)+数据高位A5+数据低位37

响应报文和返回报文一致,即实现了返回查询数据。

 

0x09-0x0A无

0x0B 获取串行通信事件计数器(Get Comm Event Counter (Serial Line only) )

该功能码用于从远程设备的通信事件计数器中获取状态字和事件计数。每次成功完成消息事件(各种请求响应命令等,但不包括异常响应、轮询命令和获取事件计数器的命令),都会让设备的事件计数器在增加。通过通信系统诊断功能(功能码08)的子功能码0001(重启)或000A(清除计数器和诊断计数器)可以重置事件计数器。正常的响应包含一个两字节的状态字和一个两字节的事件计数。如果先前发出的程序命令仍在远程设备中处理中(存在繁忙状态),状态字将为全1(FF FF十六进制)。否则,状态字将为全0。

 

 在该例中,状态字为FF FF,远程设备繁忙。事件计数显示设备已经计数了264个事件(01 08十六进制)

0x0C 获取串行通信事件日志 ( Get Comm Event Log (Serial Line only) )

这个功能码用于从远程设备获取状态字、事件计数、消息计数和事件字节字段。状态字和事件计数与获取通信事件计数器功能(功能码0x0B)返回的相同。
消息计数器包含自上次重新启动、清除计数器操作或上电以来远程设备处理的消息数量。该计数与通信系统诊断诊断功能(功能码0x08)中的子功能——返回总线消息计数(功能码0x08的子功能码0x0B)返回的计数相同。
事件字节字段包含0-64个字节,每个字节对应于远程设备的一个MODBUS发送或接收操作的状态。远程设备按照时间顺序将事件记录在该字段中。字节0是最近的事件。每个新字节会将最旧的字节从字段中清除。
正常的响应包含一个大小为2Bytes的状态字字段,2Bytes的事件计数字段,2Bytes的消息计数字段,以及一个包含0-64个事件字节的字段。此外,单独有一个一个“字节计数字段”定义这四个字段中数据的总长度。

 

该例中,状态字为00 00十六进制,表示远程设备没有在处理程序功能。事件计数显示远程设备已经计数了264个事件(01 08十六进制)。消息计数显示已经处理了289个消息(01 21十六进制)。最近的通信事件显示在事件0字节中。其内容(20十六进制)表示远程设备最近进入了仅监听模式。上一个事件显示在事件1字节中。其内容(00十六进制)表示远程设备接收到了通信重启。

响应的事件字节如下:
事件字节由获取通信事件日志功能返回,可以是四种类型之一。类型由每个字节的第7位(高位)定义(可以理解为某种形式的功能码)。它可能进一步由第6位定义(可以理解成某种形式的子功能码)。下面进行解释。

设备事件:Modbus接收

存储时机:远程设备处理消息之前

补充说明:此事件由第7位设置为逻辑“1”来定义。如果相应的条件为TRUE,则其他位将设置为逻辑“1”。各位含义如下:

 

设备事件:设备MODBUS发送

存储时机:远程设备完成处理请求消息时

补充说明:如果远程设备返回正常或异常响应,或者没有响应,则会存储该事件。此事件由第7位设置为逻辑“0”,第6位设置为“1”来定义。如果相应的条件为TRUE,则其他位将设置为逻辑“1”。各位含义如下:

 

设备事件:进入仅监听模式

存储时机:远程设备进入仅监听模式。
补充说明:该事件由被定义为了十六进制的04。

设备事件:发起通信重启

存储时机:远程设备的通信端口重新启动时(通过0x08通信系统诊断诊断的子功能:0001重启通信选项)。

补充说明:该功能提供了“录错继续”或“错误终止”两种模式。如果将远程设备置于“录错继续”模式,则将事件字节添加到现有事件日志中。如果将远程设备置于“错误终止”模式,则将字节添加到日志中,并将此后的其余部分清零。该事件由被定义为了十六进制的00。

0x0D-0x0E无

0x0F 写多个线圈(Write Multiple Coils)

功能如其名,使用方法和0x01读线圈状态基本一致、这个功能码用于强制远程设备中一系列线圈中的每个线圈为打开或关闭状态。请求PDU指定要强制的线圈引用。线圈的地址从零开始。因此,编号为1的线圈被视为地址0。
请求数据字段的内容指定了所请求的打开/关闭状态。字段中的逻辑“1”表示请求相应的输出为打开状态。逻辑“0”表示请求相应的输出为关闭状态。
正常的响应返回功能码、起始地址和强制线圈的数量。

 以下是一个写入从线圈20开始的一系列10个线圈的请求示例,在阅读该例子前,请先确保已经理解了我在0x01功能码中所写的有关位序和字节序的说明。

请求数据内容为两个字节:CD 01十六进制(1100 1101 0000 0001二进制)。二进制位对应的输出如下:
位:1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
输出:27 26 25 24 23 22 21 20 – – – – – – 29 28
第一个传输的字节(CD十六进制)地址为27-20的输出,最低有效位地址为20。
接下来传输的字节(01十六进制)地址为29-28的输出,最低有效位地址为28)。最后一个数据字节中未使用的位应填充为零。
简单来说就是以8位为单位分割,8位内倒序就行了。

 在该实例中,请求报文为:

功能码0F+起始地址高位00+起始地址低位0x13(十进制19)+输出数量高位00+输出数量低位0A(十进制10)+字节数02+输出值高位CD(11001101)+输出值低位01(00000001)

响应报文为:

功能码0F+起始地址高位00+起始地址低位13+输出数量高位00+输出数量低位0A

注意此处的响应报文和写单线圈不同,并不会回显写入的内容

0x10 写多个寄存器(Write Multiple registers)

和读寄存器(0x03和0x04功能码)基本一致。该功能码用于在远程设备中写入一块连续的寄存器(1到123个寄存器)。请求的写入值在请求数据字段中指定。数据以每个寄存器两个字节的方式打包。正常的响应返回功能码、起始地址和写入的寄存器数量。
PS:至于此处为何是123,而非125,尚且还没有弄清楚。

 

 

   

 实例如图,请求报文的格式为:

功能码10+起始地址高位00+起始地址低位01+寄存器数量高位00+寄存器数量低位02+数据字节数04+寄存器1高位00+寄存器1低位0A+寄存器2高位01+寄存器2低位02

响应报文的格式为:

功能码10+起始地址高位00+起始地址低位01+寄存器数量高位00+寄存器数量低位02

 

0x11 报告服务器ID(仅串行线)(Report Server ID (Serial Line only) )

0x12-0x13无

0x14 读文件记录(Read File Record )

0x15 写文件记录(Write File Record)

0x16 带屏蔽字写入寄存器(Mask Write Register )

0x17 读/写多个寄存器(Read/Write Multiple registers)

0x18 读取先进先出(FIFO)队列(Read FIFO Queue)

0x19-0x2A无

0x2B 封装接口传输(Encapsulated Interface Transport .)

热门相关:冉冉心动   本法官萌萌哒   后福   大妆   拒嫁豪门,前妻太抢手