
RS-485软件层协议之Modbus-RTU
Modbus-RTU是一种比较简单、可靠的协议,本文梳理了一下标准中一些常用的功能码,并举例介绍了具体使用方法。
RS-485软件层协议之Modbus-RTU
RS-485软件层协议之Modbus-RTU
RS-485概述
RS-485 采用差分信号传输方式。发送端将原始信号分成两个相位相反的信号,在两条传输线上同时发送;接收端通过比较这两条线上的信号差值来确定传输的逻辑状态。例如,当两线间的电压差为+(2 ~ 6)V时表示逻辑“1”,电压差为-(2 ~ 6)V时表示逻辑“0”。
特点:
· RS-485接口信号电平相比 RS-232 降低了,不易损坏接口电路的芯片。
· 在短距离(10米)时,数据最高传输速率可达35Mbps;在较远传输距离(1200 米)时,传输速度可达100Kbps。
· 由于采用平衡驱动器和差分接收器的组合,抗共模干扰能力强,适合在电磁环境复杂的工业场景中使用。
· RS-485总线最长可以传输1200米以上(速率≤100Kbps)。一般最大支持32个节点,如果使用特制的485芯片,可以达到128个或者256个节点,甚至最大可支持到400个节点。
· RS-485通讯使用双绞线或四线电缆进行连接,接线相对简单,安装方便。并且其通信协议相对简单,系统的稳定性较高,维护成本也相对较低。在一些对通信系统的安装和维护要求不高的场景中,RS485 能够快速搭建起可靠的通信网络。
· RS-485通讯遵循一定的标准和规范,具有较好的兼容性和互操作性,能够与多种设备和系统进行无缝连接和通信。这使得不同厂家生产的设备可以通过RS-485进行通信,方便了系统的集成和扩展。
接线方式与拓扑结构:
RS-485有两线制和四线制两种接线方式,但四线制只能实现点对点的通信方式,现很少采用,目前多采用两线制接线方式,这种接线方式为总线式拓扑结构。在构建网络时,应采用一条双绞线电缆作总线,将各个节点串接起来,并且注意总线特性阻抗的连续性和终端负载电阻的问题。
注意:在连接RS-485通信链路时,需要注意信号地的连接以及终端匹配电阻的设置。终端匹配电阻一般为120Ω,相当于电缆特性阻抗的电阻。如果没有正确连接信号地或设置终端匹配电阻,可能会影响通信的稳定性和可靠性 。
常见的 RS-485 软件层协议
· Modbus-RTU:远程终端单元模式。采用二进制数据表示,消息中的每个8位字节含有两个4位十六进制字符。通常用于串口通信,在RS-485上应用广泛。它具有较高的通信效率和可靠性,适用于对数据传输实时性要求较高的工业自动化场景。
· Modbus-ASCII:报文使用ASCII字符。每个8位字节作为两个ASCII字符发送。其数据传输效率较低,但可读性较强,适用于对通信速率要求不高,但需要方便调试和查看数据的场合。
· Modbus-TCP/IP:这是一种基于TCP/IP网络协议的Modbus变体。虽然RS-485是串口通信,但Modbus-TCP/IP可以将RS-485设备通过以太网进行通信,实现了跨网络的远程通信,支持大规模的分布式系统。
其中Modbus-RTU是使用最广泛的一种Modbus协议,采用CRC-16_Modbus校验算法。
小结
Modbus如上所说,是一种串行协议,起始是Modicon在1979年为了使用PLC所发表,但现在已成为一种工业标准的协议。而Modbus-RTU因其简单易用,更是在物联网、工业控制领域被广泛使用。
Modbus-RTU详述
· Modbus-RTU总线上的设备分为主设备和从设备,采用并联的方式接入总线;
· 每个设备都有自己的地址;
· Modbus RTU收发主要流程很简单,即主设备针对不同从设备地址发送读取或者写入(控制)命令及要操作的数据,从设备判断地址是否是自己地址,若是则根据具体命令做出不同响应,不是的话则一般情况下会忽略此数据。
举个不太恰当例子帮助大家理解:你和B、C在同一个关了灯的房间里,你在房间里喊:C,借给我一样东西,橡皮。B:这是和C说的,不是和我说的,我什么都没听见。而C一听是在和自己对话,于是就回复:我是C,借给你一样东西,给你橡皮;
对你你说的话:其中C相当于从设备的地址,这里“借给我一样东西”相当于功能码,起到命令、控制的作用,“橡皮”相当于主机对于从机发出的具体控制内容,告诉C你借的是橡皮;
对于C的回复:“我是C“也是地址码,在Modbus RTU中与你说的“C”是一样的数据,是告诉你现在是C在和你说话,“借给你一样东西”在Modbus RTU与“借给我一样东西”是同样的数据,可以理解成与你所说的话做个确认、对应,“给你橡皮”是从机针对主机发出功能码+有效数据(借给我一块橡皮)做出的具体响应。
当主机发送广播地址数据时,所有设备应当都有所响应。
下面将对Modbus-RTU的具体使用做出介绍。
帧格式
Modbus RTU的帧格式也是简单易懂的,具体组成如下:
地址 | 功能码 | 数据 | CRC校验 |
---|---|---|---|
1 Byte | 1 Byte | N Byte | 2 Byte |
地址:占用一个字节,范围0-255,但有效地址范围是1-247,其他地址有特殊用途,例如广播地址;
功能码:占用一个字节,功能码的意义就是,告诉目标地址我要做什么操作,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能;
数据:根据功能码不同,有不同的数据结构,在下面的实例中将有说明;
CRC校验:验证数据用,把前面的数据进行CRC-16_Modbus计算,将结果填入此位置,接收方可通过此校验验证数据正确性。
功能码
Modbus RTU常用功能码如下:
0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x0F | 0x10 |
---|---|---|---|---|---|---|---|
读从机线圈寄存器,位操作,可读单个或者多个 | 读离散输入寄存器,位操作,可读单个或多个,协议类似功能码0X01协议 | 读保持寄存器,字节操作,可读单个或者多个 | 读输入寄存器,字节操作,可读单个或者多个 | 写单个线圈,位操作,只能写一个,写0xff00表示设置线圈状态为ON,写0x0000表示设置线圈状态为OFF | 写单个保持寄存器,字节操作,只能写一个 | 写多个线圈寄存器,位操作,若数据区的某位值为“1”表示被请求的相应线圈状态为ON,若某位值为“0”,则为状态为OFF | 写多个保持寄存器,字节操作,可写多个 |
位操作:最小单位为一位,包括读线圈状态、读离散输入状态、写单个线圈、写多个线圈。
字操作:最小单位为一个字节,包括读保持寄存器、读输入寄存器、写单个保持寄存器、写多个保持寄存器。
寄存器
因为起初Modbus-RTU是应用在PLC中,数据存储在不同类型的寄存器里。每种类型的寄存器都有其特定的用途。就像上面提到的一些线圈、寄存器,下面对这些名词做下介绍,方便理解:
线圈寄存器(输出线圈) | 离散输入寄存器(输入线圈) | 输入寄存器 | 保持寄存器(输出寄存器) |
---|---|---|---|
可读可写;用于读取和写入某位的值,通常代表开/关状态。线圈寄存器通常用于控制一个设备的继电器、开关等二进制设备。输出端口,可设定端口的输出状态,也可以读取该位的输出状态 | 只读;用于只读单个位的值,通常代表外部设备的状态。离散输入寄存器常用来读取传感器或其他输入设备的二进制状态,例如门是否关闭、按钮是否被按下。输入端口,通过外部设定改变输入状态 | 只读;用于只读存储模拟输入如温度、压力等的16位值。输入寄存器常用于保存模拟信号转换后的数字值,输入参数。控制器运行时从外部设备获得的参数 | 可读可写;用于读写存储模拟输出或可变参数的16位值。输出参数或保持参数,控制器运行时被设定的某些参数。 |
帧详解
下面将分节针对上述功能码进行详细介绍。
0x01
读线圈寄存器当前状态,位操作,可读单个或者多个。
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 寄存器起始地址高八位 | 寄存器起始地址低八位 | 寄存器数量高八位 | 寄存器数量低八位 | CRC16 |
0x0C | 0x01 | 0x00 | 0x15 | 0x00 | 0x14 | 0xXX 0xXX |
读取的从机地址为0x0C,从0x0015开始读取20个线圈的数据。
响应:
1 Byte | 1 Byte | 1 Byte | N Byte | 2 Byte |
---|---|---|---|---|
从机地址 | 功能码 | 返回字节数 | 返回数据 | CRC16 |
0x0C | 0x01 | 0x03 | 0xCD 0xB2 0x08 | 0xXX 0xXX |
返回数据中每一位代表一个线圈状态,即线圈0x001C~0x0015分别为0xCD=1100 1101,线圈0x0024~0x001D分别为0xB2=1011 0010,线圈0x0028~0x0025分别为0x8=1000。可以看到第三组数据,只读了四个线圈,但数据是按字节发送,于是该字节剩余位是用“0”填充。
注:注意线圈地址与数据位的对应关系。
0x02
读离散输入寄存器状态,位操作,可读单个或多个。
协议与功能码0x01相似,只不过功能码为0x02,此处不再赘述。
0x03
读保持寄存器,字节操作,可读单个或多个。
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 寄存器起始地址高八位 | 寄存器起始地址低八位 | 寄存器数量高八位 | 寄存器数量低八位 | CRC16 |
0x0C | 0x03 | 0x00 | 0x75 | 0x00 | 0x03 | 0xXX 0xXX |
读取的从机地址为0x0C,从0x0075开始读取3个保持寄存器
响应:
1 Byte | 1 Byte | 1 Byte | N Byte | 2 Byte |
---|---|---|---|---|
从机地址 | 功能码 | 返回字节数 | 返回数据 | CRC16 |
0x0C | 0x03 | 0x06 | 0x00 0x06 0xA5 0xD4 0x00 0x00 | 0xXX 0xXX |
返回数据中每两个字节代表一个保持寄存器,即保持寄存器0x0075~0x0077分别为0x0006,0xA5D4,0x0000。
注:注意保持寄存器地址、高低字节与返回数据字节的对应关系。
0x04
读输入寄存器,字节操作,可读单个或多个。
协议与功能码0x03相似,只不过功能码为0x04,此处不再赘述。
0x05
写单个线圈,位操作,只能写一个
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 线圈地址高八位 | 线圈地址低八位 | 数据高八位 | 数据低八位 | CRC16 |
0x0C | 0x05 | 0x00 | 0xAC | 0xFF | 0x00 | 0xXX 0xXX |
对从机地址为0x0C的线圈0x00AC进行写操作,写0xFF00表示设置线圈状态为ON,写0x0000表示设置线圈状态为OFF
响应:
与发送帧一致。
0x06
写单个保持寄存器,字节操作,只能写一个。
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 保持寄存器地址高八位 | 保持寄存器地址低八位 | 数据高八位 | 数据低八位 | CRC16 |
0x0C | 0x06 | 0x00 | 0x00 | 0x32 | 0x12 | 0xXX 0xXX |
对从机地址为0x0C的保持寄存器0x0000的值设置为0x3212。
响应:
与发送帧一致。
0x0F
写多个线圈寄存器,位操作,若数据区的某位值为“1”表示相应线圈状态为ON,若某位值为“0”,则为状态为OFF。
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | N Byte | 2 Byte |
---|---|---|---|---|---|---|---|---|
从机地址 | 功能码 | 线圈寄存器起始地址高八位 | 线圈寄存器起始地址第八位 | 线圈寄存器数量高八位 | 线圈寄存器数量低八位 | 写入数据字节数 | 写入数据 | CRC16 |
0x0C | 0x0F | 0x00 | 0x18 | 0x00 | 0xA0 | 0x02 | 0x8C 0x02 | 0xXX 0xXX |
对从机地址为0x0C的多个线圈寄存器进行写操作,从线圈寄存器地址0x0018开始写10个,即线圈寄存器从0x001F~0x0018的分别为0x8C=1000 1100,0x0021~0x0020分别为10。可以看到第二组数据,只写了两个线圈,但数据是按字节发送,于是该字节剩余位是用“0”填充。
注:注意线圈地址与数据位的对应关系
响应:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 线圈寄存器起始地址高八位 | 线圈寄存器起始地址第八位 | 线圈寄存器数量高八位 | 线圈寄存器数量低八位 | CRC16 |
0x0C | 0x0F | 0x00 | 0x18 | 0x00 | 0xA0 | 0xXX 0xXX |
返回的帧对应位置与发送帧一致。
0x10
写多个保持寄存器,字节操作,可写多个。
发送:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | N Byte | 2 Byte |
---|---|---|---|---|---|---|---|---|
从机地址 | 功能码 | 保持寄存器起始地址高八位 | 保持寄存器起始地址第八位 | 保持寄存器数量高八位 | 保持寄存器数量低八位 | 写入数据字节数 | 写入数据 | CRC16 |
0x0C | 0x10 | 0x00 | 0x20 | 0x00 | 0x02 | 0x04 | 0x8C 0x02 0x45 0xA0 | 0xXX 0xXX |
对从机地址为0x0C的多个保持寄存器进行写操作,从保持寄存器地址0x0020开始写入2个寄存器,0x0020寄存器的值为0x8C02,0x0021寄存器的值为0x45A0。
响应:
1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 2 Byte |
---|---|---|---|---|---|---|
从机地址 | 功能码 | 保持寄存器起始地址高八位 | 保持寄存器起始地址第八位 | 保持寄存器数量高八位 | 保持寄存器数量低八位 | CRC16 |
0x0C | 0x10 | 0x00 | 0x20 | 0x00 | 0x02 | 0xXX 0xXX |
返回的帧对应位置与发送帧一致。
总结
Modbus-RTU是一种比较简单、可靠的协议,本文梳理了一下标准中一些常用的功能码,并举例介绍了具体使用方法,其中寄存器数据代表的含义可以根据实际情况进行自适应。
希望本文可以对大家学习Modbus-RTU协议起到帮助。
更多推荐
所有评论(0)