Modbus RTU是一种基于串行通信(RS232/RS485)的开放协议,采用二进制编码和主从架构,通过设备地址识别、功能码操作及CRC校验实现工业设备间的数据读写。其特点是协议简单、实时性强,支持PLC、传感器等设备的高效轮询通信,广泛应用于工业自动化领域的设备监控与控制,成为现场总线中的通用标准之一。
一、 Modbus RTU协议概述
Modbus RTU(Remote Terminal Unit)是一种广泛应用于工业自动化领域的串行通信协议,由Modicon公司(现施耐德电气的一部分)于1979年推出,最初用于可编程逻辑控制器(PLC)及其周边设备之间的通信。该协议采用二进制数据传输方式,具有简单高效、开放性强、兼容性好等特点,支持RS-232、RS-485等物理接口,能够满足工业环境对可靠性和抗干扰能力的严格要求。
Modbus RTU协议基于主从架构(Master/Slave)进行通信,网络中一个主设备负责发起请求,最多可寻址247个从设备(地址范围1-247),每个从设备都有唯一地址标识。这种通信模式采用请求-响应机制,主设备向特定从设备发送命令帧,从设备处理命令后返回响应帧,保证了通信的有序性和确定性。协议支持多种功能操作,包括读取线圈状态、读取输入寄存器、写入单个寄存器、写入多个寄存器等,覆盖了工业自动化中大部分数据交换需求。

Modbus RTU的典型应用场景包括:
- 工业过程控制:连接PLC、DCS、传感器和执行器等设备
- 数据采集与监控系统(SCADA) :实时采集现场设备数据
- 楼宇自动化:控制 HVAC、照明等系统
- 智能家居与物联网:设备间数据交换与控制
二、 Modbus RTU报文帧结构详解
Modbus RTU协议采用紧凑的二进制帧格式,具有结构简单、传输效率高的特点,特别适合对实时性要求较高的工业应用场景。一个完整的Modbus RTU帧由多个字段组成,每个字段都有其特定功能和作用。
1. 帧结构组成
Modbus RTU消息帧的基本结构如下表所示:
| 组成字段 | 长度 | 描述 | 取值范围 |
|---|---|---|---|
| 地址域 | 1字节 | 标识目标从设备的地址 | 0x00-0xFF (0为广播地址) |
| 功能码 | 1字节 | 指定要执行的操作类型 | 0x01-0xFF |
| 数据域 | N字节 | 包含请求或响应的具体数据,长度可变 | 0-252字节 |
| CRC校验 | 2字节 | 循环冗余校验码,用于检测传输错误 | 0x0000-0xFFFF |
表1:Modbus RTU帧结构组成
整个RTU帧的最大长度为256字节,其中数据域的最大长度为252字节,确保了数据传输的效率和可靠性。与Modbus ASCII协议不同,RTU协议没有明确的起始和结束符,而是通过静默时间(至少3.5个字符传输时间)来界定帧的边界。这种设计使得RTU协议比ASCII协议传输效率更高,在相同波特率下能够传输更多有效数据。
2. 帧起始与结束判定
Modbus RTU帧的识别不依赖于特定的起始和结束字符,而是通过时间间隔进行判断:
- 帧起始:在线路上检测到至少3.5个字符时间的静默期后,第一个接收到的字节标识为新帧的开始
- 帧结束:在最后一个字节接收完成后,再次出现3.5个字符时间的静默期,标识帧结束
这种基于时间的帧界定方法要求接收设备具有精确的定时机制,以确保正确识别完整的消息帧。静默时间的计算基于波特率,例如在9600bps的波特率下,传输1个字符(包括起始位、数据位、停止位)通常需要约1.04ms(11位/字符 ÷ 9600位/秒),因此3.5个字符时间约为3.64ms。
三、 地址域、功能码和数据域详解
1. 地址域(Address Field)
地址域是Modbus RTU帧中的第一个字节,用于标识通信的目标设备。该字段长度为1字节(8位),理论上可支持0-255共256个地址,但实际应用中通常只使用1-247地址范围。
地址域的功能特点包括:
唯一性:每个从设备在总线上的地址必须唯一,避免地址冲突
寻址功能:主设备通过地址域指定要通信的从设备,只有地址匹配的从设备才会响应请求
广播地址:地址0(0x00)作为广播地址,主设备向该地址发送消息时,所有从设备都会接收并执行命令,但从设备不响应广播消息
地址域的运用使得Modbus网络可以支持多设备连接,通过RS-485接口最多可连接256个设备(包括广播地址),满足复杂工业系统的组网需求。
2. 功能码(Function Code)
功能码是Modbus RTU帧中紧随地址域后的第二个字节,用于指定从设备需要执行的操作类型。功能码长度为1字节(8位),取值范围为1-255(0x01-0xFF),其中1-127为公共功能码,128-255用于异常响应。
常用功能码及其功能如下表所示:
| 功能码(十六进制) | 名称 | 功能描述 | 访问数据类型 |
|---|---|---|---|
| 0x01 | Read Coils | 读取线圈状态(离散量输出) | 位(可读写) |
| 0x02 | Read Discrete Inputs | 读取离散输入状态(开关量输入) | 位(只读) |
| 0x03 | Read Holding Registers | 读取保持寄存器内容 | 16位字(可读写) |
| 0x04 | Read Input Registers | 读取输入寄存器内容 | 16位字(只读) |
| 0x05 | Write Single Coil | 写入单个线圈状态 | 位(可写) |
| 0x06 | Write Single Register | 写入单个保持寄存器 | 16位字(可写) |
| 0x0F | Write Multiple Coils | 写入多个线圈状态 | 位(可写) |
| 0x10 | Write Multiple Registers | 写入多个保持寄存器 | 16位字(可写) |
表2:Modbus常用功能码一览
功能码决定了后续数据域的格式和内容。当从设备正常执行请求时,响应帧中的功能码与请求帧保持一致;当出现异常时,响应帧中的功能码会将最高位设置为1(即原功能码+0x80),并在数据域中包含异常代码。
3. 数据域(Data Field)
数据域是Modbus RTU帧中长度可变的部分,位于功能码之后、CRC校验字段之前,最大长度为252字节。数据域的具体内容和格式取决于功能码的类型,主要包括以下几种形式:
请求帧数据域:通常包含要访问的寄存器地址、数据数量或要写入的数据值
例如:读取保持寄存器请求中,数据域包含起始地址(2字节)和寄存器数量(2字节)
例如:写入多个寄存器请求中,数据域包含起始地址(2字节)、写入数量(2字节)、字节计数(1字节)和寄存器数据(N字节)
响应帧数据域:包含请求执行的结果,如读取的数据值或写入操作确认
例如:读取保持寄存器响应中,数据域包含字节计数(1字节)和寄存器数据(N字节)
例如:写入单个寄存器响应中,数据域包含写入地址(2字节)和写入数据(2字节)
异常响应数据域:当发生错误时,数据域包含异常代码,指示错误类型
例如:异常码0x02表示”非法数据地址”,即请求的寄存器地址不存在或不可访问
数据域的组织方式体现了Modbus协议的灵活性,能够适应不同类型的数据访问需求。需要注意的是,Modbus协议中所有数据地址均从零开始编址,但许多设备厂商在文档中使用基于1的地址编号(如40001、30002等),实际通信时需要转换为零基地址。
四、 CRC校验机制
CRC(Cyclic Redundancy Check,循环冗余校验)是Modbus RTU协议中用于检测传输错误的机制,位于帧的末尾,长度为2字节。CRC校验能够有效检测单比特错误、双比特错误、奇偶校验错误以及突发错误,保证数据传输的完整性和可靠性。
1. CRC校验原理
Modbus RTU使用CRC-16校验算法,具体多项式为:x¹⁶ + x¹⁵ + x² + 1(对应的十六进制表示为0x8005)。该算法对从地址域到数据域的所有字节进行计算,生成16位的校验值,附加在消息帧的末尾。
CRC校验值的计算步骤如下:
初始化一个16位寄存器为0xFFFF(全1)
将消息帧中的每个字节与寄存器中的值进行异或操作
对寄存器中的每一位进行检测,如果最低位为1.则右移一位并与多项式0x8005进行异或;如果最低位为0.则仅右移一位
重复步骤3直至处理完所有8位
重复步骤2-4处理消息中的下一个字节,直至所有字节处理完毕
最终寄存器中的值即为CRC校验码
2. CRC校验码的传输
CRC校验码在消息帧中以低字节在前(little-endian)的顺序传输,即先传输CRC的低字节,然后传输高字节。例如,如果计算出的CRC值为0x3B80.则在消息帧中传输顺序为0x80后跟0x3B。
在接收端,设备会对收到的消息(除CRC字段外)重新计算CRC值,并将其与接收到的CRC值进行比较:
如果两者一致,认为数据传输正确,处理该消息
如果两者不一致,认为数据传输过程中出现错误,丢弃该消息
这种校验机制确保了Modbus RTU通信在噪声环境下的可靠性,是其在工业现场广泛应用的重要基础。
五、 典型Modbus RTU报文实例分析
1. 读取保持寄存器请求与响应
读取保持寄存器是Modbus RTU最常见的操作之一,用于获取从设备中保持寄存器的值。以下是一个完整的请求响应示例:
主机请求帧(读取从设备地址0x01的保持寄存器,起始地址0x0004.数量0x0002):
01 03 00 04 00 02 85 CA
01:从设备地址(1字节)
03:功能码(Read Holding Registers,1字节)
00 04:起始地址(2字节,地址0x0004对应Modbus地址40005)
00 02:寄存器数量(2字节,读取2个寄存器)
85 CA:CRC校验码(2字节)
从机正常响应帧:
01 03 04 00 00 00 00 21 33
01:从设备地址(1字节)
03:功能码(与请求一致,1字节)
04:字节计数(1字节,表示后面有4个数据字节)
00 00 00 00:寄存器数据(4字节,两个寄存器的值均为0x0000)
21 33:CRC校验码(2字节)
从机异常响应帧(如果请求的寄存器地址不存在):
01 83 02 80 3B
01:从设备地址(1字节)
83:异常功能码(0x03 + 0x80 = 0x83)
02:异常代码(0x02表示非法数据地址)
80 3B:CRC校验码(2字节)
2. 写入单个寄存器请求与响应
写入单个寄存器操作用于修改从设备中单个保持寄存器的值。以下是一个完整的请求响应示例:
主机请求帧(向从设备地址0x01的保持寄存器0x0004写入值0x1234):
01 06 00 04 12 34 4A B0
01:从设备地址(1字节)
06:功能码(Write Single Register,1字节)
00 04:寄存器地址(2字节,地址0x0004对应Modbus地址40005)
12 34:写入值(2字节,值为0x1234)
4A B0:CRC校验码(2字节)
从机正常响应帧:
01 06 00 04 12 34 4A B0
响应帧与请求帧完全一致,表示写入操作成功完成
3. 写入多个线圈请求与响应
写入多个线圈操用于同时修改从设备中多个线圈的状态。以下是一个完整的请求响应示例:
主机请求帧(向从设备地址0x01的线圈起始地址0x0001写入2个线圈值):
01 0F 00 01 00 02 02 01 00 6C 09
01:从设备地址(1字节)
0F:功能码(Write Multiple Coils,1字节)
00 01:起始地址(2字节,地址0x0001对应Modbus地址00002)
00 02:线圈数量(2字节,写入2个线圈)
02:字节计数(1字节,表示后面有2个数据字节)
01 00:线圈值(2字节,第一个线圈ON,第二个线圈OFF)
6C 09:CRC校验码(2字节)
从机正常响应帧:
01 0F 00 01 00 02 54 0D
01:从设备地址(1字节)
0F:功能码(与请求一致,1字节)
00 01:起始地址(2字节)
00 02:线圈数量(2字节)
54 0D:CRC校验码(2字节)
通过这些实例可以看出,Modbus RTU协议的请求-响应机制十分清晰,正常响应通常回显请求中的地址和参数信息,而异常响应则通过功能码最高位置1和异常代码来指示错误类型。




