Modbus协议作为工业自动化领域中最为广泛应用的通信协议之一,由于其简洁、开放和高效的特点,一直以来被用于PLC、传感器、远程监控系统等设备之间的数据交换。其中,功能码在整个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)的状态。位操作相关的功能码通常包括读取线圈状态、读取离散输入状态以及写操作中的单个或多个线圈控制。下面我们从读取操作和写入操作两大类依次解析。
读取线圈状态(功能码0x01)主要用于获取从站设备中线圈(输出继电器、数字输出等)的当前状态;而读取离散输入状态(功能码0x02)则针对只读的数字输入(如开关、传感器状态)进行数据采集。在Modbus协议中,所有地址均以0起始计数,即第一个线圈的地址为0,而后依次递增。
下表描述了读取线圈/离散输入状态功能的查询和响应报文结构:
字段 | 字节数 | 说明 |
---|---|---|
从站地址 | 1 | 指定目标从机 |
功能码 | 1 | 0x01或0x02,根据操作类型 |
起始地址 | 2 | 指定读取开始位置(大端序) |
数量 | 2 | 读取的线圈数/离散输入数 |
CRC校验 | 2 | 用于校验数据 |
——响应报文字段—— | ||
从站地址 | 1 | 与请求相同 |
功能码 | 1 | 与请求相同 |
字节计数 | 1 | 后续数据的字节数量 |
数据域 | N | 每位代表一个状态,若位数不满8则补0 |
图 1:读取线圈状态的报文结构示意图
Mermaidflowchart TD A["开始:从站地址(1字节)"] B["功能码 0x01/0x02 (1字节)"] C["起始地址 (2字节) [大端序]"] D["数量 (2字节)"] E["CRC校验 (2字节)"] A --> B --> C --> D --> E
此图展示了Modbus读取操作中查询报文的基本结构及字段顺序.
功能说明:
写单个线圈功能主要用于改变一个线圈的状态,即将某个物理输出设置为ON或OFF。报文中通过写入特定的数值来指定状态,通常为0xFF00表示ON,0x0000表示OFF,其他值为非法且会导致异常响应.
查询报文结构:
响应报文:
响应报文的数据字段返回与查询报文一致的内容(从站地址、功能码、起始地址和数据值),表明写操作成功执行;如果操作失败,则返回异常响应信息.
功能说明:
写多个线圈功能允许同时设置一组连续线圈的状态。查询报文中除了包含起始地址和线圈数量外,还包含数据域位信息,此数据域的每一个位对应相应线圈的状态(1表示ON,0表示OFF),各字节以低位到高位的顺序依次排列.
查询报文结构:
响应报文:
包含功能码、起始地址和写入的线圈数量,用于确认写入操作已经被正确执行.
下表对写操作两种功能码的主要参数进行了对比:
参数 | 写单个线圈 (0x05) | 写多个线圈 (0x0F) |
---|---|---|
起始地址 | 2字节 | 2字节 |
操作数量 | 固定为1 | 2字节,表示连续线圈数 |
数据格式 | 2字节,固定0xFF00/0x0000 | 多字节,每个位表示ON/OFF状态 |
广播模式支持 | 支持广播模式 | 支持广播模式 |
异常响应 | 返回查询相同结构 | 返回查询相同结构 |
此表清晰地展示了两种写操作在参数、数据格式和响应机制上的异同,有助于工程师在实际应用中正确构造报文.
与位操作不同,寄存器数据的最小单位为“字”(16位),这使得Modbus协议不仅可以传输单个位的信息,还能够传输整数、浮点数甚至字符数据。寄存器操作主要涉及读取和写入保持寄存器以及输入寄存器,其中二者在使用上存在只读和可读写的区别。
通过下面的流程图可以直观理解读取寄存器操作的报文构造过程。
图 2:读取保持/输入寄存器查询报文流程图
Mermaidflowchart TD A["从站地址 (1 字节)"] B["功能码 0x03/0x04 (1 字节)"] C["起始地址 (2 字节)"] D["寄存器数量 (2 字节)"] E["CRC校验 (2 字节)"] A --> B --> C --> D --> E
该图展示了请求读取连续寄存器时查询报文的各个字段顺序及内容.
功能说明:
此功能码用于向指定的保持寄存器写入一个新值,常用于修改设备参数和状态。写入值为2个字节,同时寄存器在写操作后返回与请求相同的消息结构,以确认操作成功.
查询报文结构:
响应报文:
返回内容与查询报文中相同字段,表示写操作成功完成。
功能说明:
用于对多个连续的保持寄存器进行写入操作。查询报文中不仅包含起始地址和寄存器数量,还包含字节计数和实际待写数据。由于每个寄存器占2字节,因此数据区域的字节数为寄存器数量×2.
查询报文结构:
响应报文:
返回的响应报文包含从站地址、功能码、起始地址和写入数量,用于确认多寄存器写入操作的成功性。
下面的表格总结了读取和写入寄存器操作的主要参数和数据格式:
参数 | 读取保持/输入寄存器 (0x03/0x04) | 写单个/多个保持寄存器 (0x06/0x10) |
---|---|---|
数据单位 | 16位(2字节) | 16位(2字节) |
数据顺序 | 大端顺序(高位在前) | 大端顺序(高位在前) |
查询报文字段 | 起始地址 (2字节)、数量 (2字节) | 起始地址 (2字节)、数据值 (2字节)/数据域 |
响应报文内容 | 返回数据字节数 = 数量 ×2 | 返回起始地址及写入数量 |
此表清楚地对比了读取和写入寄存器操作在报文构造以及数据处理上的主要区别,有助于对寄存器操作有全面认识.
除了常规的数据读写操作外,Modbus协议还定义了一些用于设备诊断和维护的功能码,其中最常见的是诊断功能码(例如0x08)。诊断功能码主要用于检查通信链路、设备状态以及执行特定的自诊断操作。常见的诊断子功能包括:
在实际操作中,诊断功能码除了返回正常数据外,还会对异常情况作出响应,此时功能码会加上0x80(例如,正常的0x08对应异常响应为0x88)。
当从站无法按请求执行功能(例如,地址无效、数据值错误或内部错误)时,从站将返回异常响应报文,其功能码字节由原始功能码加上0x80构成,而附带一个异常码,指明具体出错原因。例如:
这种设计机制不仅便于主站快速判断错误原因,而且在复杂的工业环境中极大提高了系统的稳定性和故障诊断效率。
下图展示了异常响应流程的简化流程图:
Modbus异常响应流程图
Mermaidflowchart TD A["接收到请求报文"] B["校验请求报文"] C{"执行正常?"} D["执行请求操作"] E["返回正常响应报文"] F["检测异常,生成异常码"] G["返回异常响应,功能码+0x80"] A --> B --> C C -- 是 --> D --> E C -- 否 --> F --> G
此流程图说明了在接收到请求后,从站如何根据请求内容决定返回正确信息或异常响应的整个过程.
Modbus报文的构造直接关系到数据传输的正确性和设备间的通信效率。一般来说,每个Modbus报文(无论是查询报文还是响应报文)均包含如下字段:从站地址、功能码、数据域以及CRC校验。下面分别对查询报文和响应报文的字段及其作用进行详细说明。
例如,对于读取线圈状态的查询报文,其数据结构为:
“[从站地址 (1)] + [功能码 (1)] + [起始地址 (2)] + [线圈数量 (2)] + [CRC (2)]”
而写操作如写单个寄存器报文则为:
“[从站地址 (1)] + [功能码 (1)] + [寄存器地址 (2)] + [写入值 (2)] + [CRC (2)]”.
响应报文通常由从站返回,其结构取决于所执行的操作:
对于写操作而言,响应报文往往与查询报文承载相同信息,起到确认写入成功的作用。异常响应则仅包含一个异常码,用以标识错误类型,例如功能码加上0x80以及异常码字段。
CRC校验是Modbus报文数据完整性的重要保障。CRC(循环冗余校验)通过对报文中除CRC字段之外的所有字节计算获得,接收方同样计算CRC并对比,若不一致则丢弃报文进行重传。这一机制确保了在噪声干扰或信号衰减环境下,数据传输的可靠性.
下表展示了查询报文和响应报文各字段的详细说明:
字段 | 查询报文说明 | 响应报文说明 |
---|---|---|
从站地址 | 指定目标从机,1字节 | 返回与查询一致的从站地址 |
功能码 | 表示当前请求操作,1字节 | 与查询报文相同,若异常则为原功能码+0x80 |
起始地址 | 指明起始数据地址,2字节 | 对于写操作,返回确认写入的起始地址 |
数量/字节计数 | 对于读取操作,表示连续数据个数(2字节)或数据字节数(1字节) | 读取操作中,字节计数表明返回数据长度,写操作中为写入的数据 |
数据域 | 写操作时包含具体写入值;读取操作无此字段 | 存储读取到的数据,长度依赖于操作内容 |
CRC校验 | 两字节,用于确认报文数据准确性 | 同查询报文,确保响应数据在传输过程中未出错 |
上述报文结构及字段说明构成了Modbus协议数据传输的基础,确保主机与从机间的交互能够快速、准确地完成各项数据操作。
功能码类别 | 主要功能 | 查询报文字段 | 响应报文字段 |
---|---|---|---|
位操作:读取 | 读取线圈状态/离散输入状态 | 从站地址、功能码、起始地址、数量、CRC | 从站地址、功能码、字节计数、数据 |
位操作:写入 | 写单个线圈/多个线圈 | 从站地址、功能码、起始地址、数据(或数据域)、CRC | 返回写入确认信息 |
寄存器操作:读取 | 读取保持寄存器/输入寄存器 | 从站地址、功能码、起始地址、寄存器数量、CRC | 从站地址、功能码、字节计数、数据 |
寄存器操作:写入 | 写单个/多个保持寄存器 | 从站地址、功能码、寄存器地址、数据值/数据域、CRC | 返回写入确认信息 |
诊断/异常 | 设备自检及异常反馈 | 从站地址、功能码、数据域、CRC | 异常响应:功能码+0x80及异常码 |
本文对Modbus功能码的工作原理及报文结构进行了全面详细的解析,主要得出以下几点关键结论:
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!