编辑
2025-09-22
C#
00

目录

Modbus协议概述
位操作功能码详解
读取线圈和离散输入状态(功能码0x01和0x02)
功能说明
报文结构和数据排列
写操作:写单个线圈与写多个线圈(功能码0x05和0x0F)
写单个线圈(功能码0x05)
写多个线圈(功能码0x0F)
寄存器操作功能码详解
读取保持寄存器与输入寄存器(功能码0x03和0x04)
功能说明
报文结构
写操作:写单个与写多个保持寄存器(功能码0x06和0x10)
写单个保持寄存器(0x06)
写多个保持寄存器(0x10)
诊断功能码及高级异常处理
诊断功能码的概念与子功能说明
异常响应机制与功能码偏移
报文结构详解
查询报文的结构与字段说明
响应报文的结构与数据格式
数据校验与CRC算法
总结
结论

Modbus协议作为工业自动化领域中最为广泛应用的通信协议之一,由于其简洁、开放和高效的特点,一直以来被用于PLC、传感器、远程监控系统等设备之间的数据交换。其中,功能码在整个Modbus通信过程中起到了至关重要的作用,其不仅决定了设备所需执行的具体动作,同时也直接反映了报文结构及数据传输的细节。


Modbus协议概述

Modbus协议由Modbus协会制定,其核心思想是建立主从(主机-从机)的通信模型,以实现多个设备之间可靠、高效的数据交换。在该协议中,消息帧(或报文)由从站地址、功能码、数据域以及错误校验部分(通常为CRC校验)构成。功能码是一字节数据,其取值范围为0~127(0x00~0x7F),而出现异常时,功能码会在基础功能码上加上0x80(如功能码0x01异常响应为0x81),从而反映出错误信息。

Modbus通信支持多种物理层(如RS-232、RS-485、TCP/IP等)以及多种数据类型,主要包括位操作(针对单个bit)、寄存器操作(针对16位字)和部分特殊诊断操作。各类功能码对应不同操作内容,如读取或写入线圈状态、读取或写入保持寄存器、诊断功能以及自定义操作指令等,这些都在后续章节中逐一详述。

此外,Modbus设备中数据通常存储在四大数据块中:线圈(Coils)、离散输入(Discrete Inputs)、保持寄存器(Holding Registers)及输入寄存器(Input Registers),这为不同应用场景提供了丰富的数据类型。本文研究重点聚焦在功能码的工作原理和报文结构方面,而不额外展开设备应用场景或与其他协议的比较。


位操作功能码详解

Modbus中涉及位操作的数据主要指单个位(bit)数据,即用于表示布尔量(ON/OFF)的状态。位操作相关的功能码通常包括读取线圈状态、读取离散输入状态以及写操作中的单个或多个线圈控制。下面我们从读取操作和写入操作两大类依次解析。

image.png

读取线圈和离散输入状态(功能码0x01和0x02)

功能说明

读取线圈状态(功能码0x01)主要用于获取从站设备中线圈(输出继电器、数字输出等)的当前状态;而读取离散输入状态(功能码0x02)则针对只读的数字输入(如开关、传感器状态)进行数据采集。在Modbus协议中,所有地址均以0起始计数,即第一个线圈的地址为0,而后依次递增。

报文结构和数据排列

  • 查询报文
    • 从站地址:1字节,指定目标从机设备。
    • 功能码:1字节(0x01或0x02),表示所请求的操作。
    • 起始地址:2字节(高位在前),指定线圈或离散输入的起始位置。
    • 数量:2字节,指明需要读取的连续位数,其取值范围通常为1~2000。
    • CRC校验:2字节,确保数据传输的完整性。
  • 响应报文
    • 从站地址:1字节。
    • 功能码:1字节,与查询报文一致。
    • 字节计数:1字节,表示后续数据字节数。
    • 数据域:多个字节,每个字节包含8个位状态,数据排列采用从低到高位方式(即第一个数据字节的最低有效位对应起始地址的状态,其余依次排列)。若最后一个字节不满8位,则高位用0填充。

下表描述了读取线圈/离散输入状态功能的查询和响应报文结构:

字段字节数说明
从站地址1指定目标从机
功能码10x01或0x02,根据操作类型
起始地址2指定读取开始位置(大端序)
数量2读取的线圈数/离散输入数
CRC校验2用于校验数据
——响应报文字段——
从站地址1与请求相同
功能码1与请求相同
字节计数1后续数据的字节数量
数据域N每位代表一个状态,若位数不满8则补0

图 1:读取线圈状态的报文结构示意图

Mermaid
flowchart TD A["开始:从站地址(1字节)"] B["功能码 0x01/0x02 (1字节)"] C["起始地址 (2字节) [大端序]"] D["数量 (2字节)"] E["CRC校验 (2字节)"] A --> B --> C --> D --> E

此图展示了Modbus读取操作中查询报文的基本结构及字段顺序.


写操作:写单个线圈与写多个线圈(功能码0x05和0x0F)

写单个线圈(功能码0x05)

  • 功能说明

    写单个线圈功能主要用于改变一个线圈的状态,即将某个物理输出设置为ON或OFF。报文中通过写入特定的数值来指定状态,通常为0xFF00表示ON,0x0000表示OFF,其他值为非法且会导致异常响应.

  • 查询报文结构

    • 从站地址:1字节。
    • 功能码:1字节(0x05)。
    • 起始地址:2字节,指明需要写入的线圈地址。
    • 数据值:2字节,仅允许0xFF00或0x0000。
    • CRC校验:2字节。
  • 响应报文

    响应报文的数据字段返回与查询报文一致的内容(从站地址、功能码、起始地址和数据值),表明写操作成功执行;如果操作失败,则返回异常响应信息.

写多个线圈(功能码0x0F)

  • 功能说明

    写多个线圈功能允许同时设置一组连续线圈的状态。查询报文中除了包含起始地址和线圈数量外,还包含数据域位信息,此数据域的每一个位对应相应线圈的状态(1表示ON,0表示OFF),各字节以低位到高位的顺序依次排列.

  • 查询报文结构

    • 从站地址:1字节。
    • 功能码:1字节(0x0F)。
    • 起始地址:2字节。
    • 线圈数量:2字节,范围一般为1~2000,根据不同设备可有所变化。
    • 字节计数:1字节,表示后续数据字节总数。
    • 数据域:N字节,按位表示各个线圈状态;最后一字节若不足8位,则高位补0。
    • CRC校验:2字节。
  • 响应报文

    包含功能码、起始地址和写入的线圈数量,用于确认写入操作已经被正确执行.

下表对写操作两种功能码的主要参数进行了对比:

参数写单个线圈 (0x05)写多个线圈 (0x0F)
起始地址2字节2字节
操作数量固定为12字节,表示连续线圈数
数据格式2字节,固定0xFF00/0x0000多字节,每个位表示ON/OFF状态
广播模式支持支持广播模式支持广播模式
异常响应返回查询相同结构返回查询相同结构

此表清晰地展示了两种写操作在参数、数据格式和响应机制上的异同,有助于工程师在实际应用中正确构造报文.


寄存器操作功能码详解

与位操作不同,寄存器数据的最小单位为“字”(16位),这使得Modbus协议不仅可以传输单个位的信息,还能够传输整数、浮点数甚至字符数据。寄存器操作主要涉及读取和写入保持寄存器以及输入寄存器,其中二者在使用上存在只读和可读写的区别。

读取保持寄存器与输入寄存器(功能码0x03和0x04)

功能说明

  • 读取保持寄存器(0x03):用于从从站中读取连续的多个保持寄存器。保持寄存器常用于保存设备的状态、参数或配置数据,主机既可以读取也可以写入这些寄存器。
  • 读取输入寄存器(0x04):用于读取只读的输入寄存器数据,输入寄存器一般对应传感器采集的模拟输入等,只供读取,不允许写入.

报文结构

  • 查询报文
    • 从站地址:1字节。
    • 功能码:1字节(0x03或0x04)。
    • 起始地址:2字节(高在前,下同),表示从哪个寄存器开始读取;协议地址与PLC实际地址存在转换关系(例如,40001对应协议地址0x0000).
    • 寄存器数量:2字节,规定一次读取的寄存器数目;由于每个寄存器为2字节,要求连续性数据存储。
    • CRC校验:2字节。
  • 响应报文
    • 从站地址:1字节。
    • 功能码:1字节。
    • 字节计数:1字节,表示后续数据总字节数(等于寄存器数量乘以2)。
    • 数据域:N字节,分别表示每个寄存器的值。数据通常按照大端顺序传输,即高位字节先传输。

通过下面的流程图可以直观理解读取寄存器操作的报文构造过程。

图 2:读取保持/输入寄存器查询报文流程图

Mermaid
flowchart TD A["从站地址 (1 字节)"] B["功能码 0x03/0x04 (1 字节)"] C["起始地址 (2 字节)"] D["寄存器数量 (2 字节)"] E["CRC校验 (2 字节)"] A --> B --> C --> D --> E

该图展示了请求读取连续寄存器时查询报文的各个字段顺序及内容.

写操作:写单个与写多个保持寄存器(功能码0x06和0x10)

写单个保持寄存器(0x06)

  • 功能说明

    此功能码用于向指定的保持寄存器写入一个新值,常用于修改设备参数和状态。写入值为2个字节,同时寄存器在写操作后返回与请求相同的消息结构,以确认操作成功.

  • 查询报文结构

    • 从站地址:1字节。
    • 功能码:1字节(0x06)。
    • 寄存器地址:2字节。
    • 写入值:2字节(根据数据类型可以表示整型、浮点数的部分数据或字符数据)。
    • CRC校验:2字节。
  • 响应报文

    返回内容与查询报文中相同字段,表示写操作成功完成。

写多个保持寄存器(0x10)

  • 功能说明

    用于对多个连续的保持寄存器进行写入操作。查询报文中不仅包含起始地址和寄存器数量,还包含字节计数和实际待写数据。由于每个寄存器占2字节,因此数据区域的字节数为寄存器数量×2.

  • 查询报文结构

    • 从站地址:1字节。
    • 功能码:1字节(0x10)。
    • 起始地址:2字节。
    • 寄存器数量:2字节。
    • 字节计数:1字节,等于寄存器数量×2。
    • 数据域:N字节,依次排列每个寄存器的值(大端顺序)。
    • CRC校验:2字节。
  • 响应报文

    返回的响应报文包含从站地址、功能码、起始地址和写入数量,用于确认多寄存器写入操作的成功性。

下面的表格总结了读取和写入寄存器操作的主要参数和数据格式:

参数读取保持/输入寄存器 (0x03/0x04)写单个/多个保持寄存器 (0x06/0x10)
数据单位16位(2字节)16位(2字节)
数据顺序大端顺序(高位在前)大端顺序(高位在前)
查询报文字段起始地址 (2字节)、数量 (2字节)起始地址 (2字节)、数据值 (2字节)/数据域
响应报文内容返回数据字节数 = 数量 ×2返回起始地址及写入数量

此表清楚地对比了读取和写入寄存器操作在报文构造以及数据处理上的主要区别,有助于对寄存器操作有全面认识.


诊断功能码及高级异常处理

诊断功能码的概念与子功能说明

除了常规的数据读写操作外,Modbus协议还定义了一些用于设备诊断和维护的功能码,其中最常见的是诊断功能码(例如0x08)。诊断功能码主要用于检查通信链路、设备状态以及执行特定的自诊断操作。常见的诊断子功能包括:

  • 回送诊断(Echo Diagnostic):主设备向从机发送数据,从机原封不动地回送,实现链路及设备响应测试。
  • 监听模式测试:用于检测总线上是否存在故障(具体子功能码可能因设备实现而略有不同)。

在实际操作中,诊断功能码除了返回正常数据外,还会对异常情况作出响应,此时功能码会加上0x80(例如,正常的0x08对应异常响应为0x88)。

异常响应机制与功能码偏移

当从站无法按请求执行功能(例如,地址无效、数据值错误或内部错误)时,从站将返回异常响应报文,其功能码字节由原始功能码加上0x80构成,而附带一个异常码,指明具体出错原因。例如:

  • 非法函数:若请求的功能码不被支持,从站返回异常码01表示非法功能。
  • 非法数据地址:异常码02表示请求的数据地址超出从站支持范围。
  • 非法数据值:异常码03表示请求中的数据值不在允许范围内。
  • 设备故障:异常码04则表示从站内部发生故障,无法执行请求.

这种设计机制不仅便于主站快速判断错误原因,而且在复杂的工业环境中极大提高了系统的稳定性和故障诊断效率。

下图展示了异常响应流程的简化流程图:

Modbus异常响应流程图

Mermaid
flowchart TD A["接收到请求报文"] B["校验请求报文"] C{"执行正常?"} D["执行请求操作"] E["返回正常响应报文"] F["检测异常,生成异常码"] G["返回异常响应,功能码+0x80"] A --> B --> C C -- 是 --> D --> E C -- 否 --> F --> G

此流程图说明了在接收到请求后,从站如何根据请求内容决定返回正确信息或异常响应的整个过程.


报文结构详解

Modbus报文的构造直接关系到数据传输的正确性和设备间的通信效率。一般来说,每个Modbus报文(无论是查询报文还是响应报文)均包含如下字段:从站地址、功能码、数据域以及CRC校验。下面分别对查询报文和响应报文的字段及其作用进行详细说明。

查询报文的结构与字段说明

  • 从站地址(1字节):标识目标从机设备,范围通常为1~247。
  • 功能码(1字节):标识请求执行哪类操作,如读取线圈、写入寄存器等。
  • 起始地址(2字节):根据所操作的数据类型(线圈、寄存器等),指明从哪起始的地址开始。
  • 数量或数据长度(2字节或1字节)
    • 对于读取操作,代表要读取的连续位数或寄存器个数;
    • 对于写入操作,可能包含写入的数据长度,以及实际待写入的数据。
  • 数据域(可变长度)
    • 读取操作中不包含该部分;
    • 写入操作中包含具体数据,需要按照特定的字节顺序和排列规则发送。
  • CRC校验(2字节):运用循环冗余校验算法,确保报文在传输过程中未被破坏.

例如,对于读取线圈状态的查询报文,其数据结构为:

“[从站地址 (1)] + [功能码 (1)] + [起始地址 (2)] + [线圈数量 (2)] + [CRC (2)]”

而写操作如写单个寄存器报文则为:

“[从站地址 (1)] + [功能码 (1)] + [寄存器地址 (2)] + [写入值 (2)] + [CRC (2)]”.

响应报文的结构与数据格式

响应报文通常由从站返回,其结构取决于所执行的操作:

  • 读取操作响应
    • 从站地址(1字节);
    • 功能码(1字节);
    • 字节计数(1字节);
    • 数据域(N字节):若为位操作,则每位表示一个状态,排列顺序固定;若为寄存器操作,则每个寄存器占用2字节,按照大端顺序。
  • 写操作响应
    • 从站地址(1字节);
    • 功能码(1字节);
    • 起始地址(2字节);
    • 写入数量或写入值(2字节);
    • CRC校验(2字节)。

对于写操作而言,响应报文往往与查询报文承载相同信息,起到确认写入成功的作用。异常响应则仅包含一个异常码,用以标识错误类型,例如功能码加上0x80以及异常码字段。

数据校验与CRC算法

CRC校验是Modbus报文数据完整性的重要保障。CRC(循环冗余校验)通过对报文中除CRC字段之外的所有字节计算获得,接收方同样计算CRC并对比,若不一致则丢弃报文进行重传。这一机制确保了在噪声干扰或信号衰减环境下,数据传输的可靠性.

下表展示了查询报文和响应报文各字段的详细说明:

字段查询报文说明响应报文说明
从站地址指定目标从机,1字节返回与查询一致的从站地址
功能码表示当前请求操作,1字节与查询报文相同,若异常则为原功能码+0x80
起始地址指明起始数据地址,2字节对于写操作,返回确认写入的起始地址
数量/字节计数对于读取操作,表示连续数据个数(2字节)或数据字节数(1字节)读取操作中,字节计数表明返回数据长度,写操作中为写入的数据
数据域写操作时包含具体写入值;读取操作无此字段存储读取到的数据,长度依赖于操作内容
CRC校验两字节,用于确认报文数据准确性同查询报文,确保响应数据在传输过程中未出错

上述报文结构及字段说明构成了Modbus协议数据传输的基础,确保主机与从机间的交互能够快速、准确地完成各项数据操作。


总结

功能码类别主要功能查询报文字段响应报文字段
位操作:读取读取线圈状态/离散输入状态从站地址、功能码、起始地址、数量、CRC从站地址、功能码、字节计数、数据
位操作:写入写单个线圈/多个线圈从站地址、功能码、起始地址、数据(或数据域)、CRC返回写入确认信息
寄存器操作:读取读取保持寄存器/输入寄存器从站地址、功能码、起始地址、寄存器数量、CRC从站地址、功能码、字节计数、数据
寄存器操作:写入写单个/多个保持寄存器从站地址、功能码、寄存器地址、数据值/数据域、CRC返回写入确认信息
诊断/异常设备自检及异常反馈从站地址、功能码、数据域、CRC异常响应:功能码+0x80及异常码

结论

本文对Modbus功能码的工作原理及报文结构进行了全面详细的解析,主要得出以下几点关键结论:

  • Modbus协议采用主从模式,由功能码驱动各种数据操作,其结构设计体现出极高的灵活性和兼容性;
  • 位操作功能码(0x01、0x02、0x05、0x0F)专注于单比特状态的数据读写,采用低位到高位排列的方式,具有明确的填充规则;
  • 寄存器操作功能码(0x03、0x04、0x06、0x10)则聚焦于16位字数据的传输,支持整数、浮点数等多种数据格式,数据传输采用大端方式;
  • 诊断功能码及异常响应机制为系统提供了自诊断能力,在异常情况下能够快速反馈错误类型,便于故障定位;
  • 报文结构清晰划分为从站地址、功能码、数据域和CRC校验,这一标准结构确保了数据传输的精确性和安全性。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!