Modbus通讯协议在串行链路上应用实例详解

Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。MODBUS是一个请求/应答协议,并且提供功能码规定的服务。

Modbus通讯物理接口可以选用串口(包括RS232和RS485),也可以选择以太网口。根据应用通信规程的不同,Modbus协议可分为串行链路上的 MODBUS和TCP/IP 上的 MODBUS;其中,应用于串行链路上的Modbus协议根据传输模式的不同,又可以分为RTU模式和ASCII模式,RTU传输模式凭借其简单、高效得到广泛应用,本文主要介绍RTU模式。


                                                                第一部分:Modbus 协议

1 引言

MODBUS是OSI模型第7层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信。

自从1979年出现工业串行链路的事实标准以来,MODBUS 使成千上万的自动化设备能够通信。目前,继续增加对简单而雅观的MODBUS 结构支持。互联网组织能够使 TCP/IP栈上的保留系统端口502 访问MODBUS。

MODBUS是一个请求/应答协议,并且提供功能码规定的服务。MODBUS功能码是MODBUS请求/应答PDU的元素。本文件的作用是描述MODBUS事务处理框架内使用的功能码。

2 总体协议描述

2.1 协议描述

MODBUS协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网络上的MODBUS协议映射能够在应用数据单元(ADU)上引入一些附加域。

                           

                                                                                  图1 通用MODBUS帧

启动 MODBUS 事务处理的客户机创建 MODBUS 应用数据单元。功能码向服务器指示将执行哪种操作。

MODBUS 协议建立了客户机启动的请求格式。

用一个字节编码 MODBUS 数据单元的功能码域。有效的码字范围是十进制 1-255(128-255 为异常响应保留)。当从客户机向服务器设备发送报文时,功能码域通知服务器执行哪种操作。

向一些功能码加入子功能码来定义多项操作。

从客户机向服务器设备发送的报文数据域包括附加信息,服务器使用这个信息执行功能码定义的操作。这个域还包括离散项目和寄存器地址、处理的项目数量以及域中的实际数据字节数。

在某种请求中,数据域可以是不存在的(0 长度),在此情况下服务器不需要任何附加信息。功能码仅说明操作。

如果在一个正确接收的 MODBUS ADU 中,不出现与请求MODBUS功能有关的差错,那么服务器至客户机的响应数据域包括请求数据。如果出现与请求 MODBUS 功能有关的差错,那么域包括一个异常码,服务器应用能够使用这个域确定下一个执行的操作。

例如,客户机能够读一组离散量输出或输入的开/关状态,或者客户机能够读/写一组寄存器的数

据内容。

当服务器对客户机响应时,它使用功能码域来指示正常(无差错)响应或者出现某种差错(称为异常响应)。对于一个正常响应来说,服务器仅对原始功能码响应。

                                  

                                                                   图2 MODBUS事务处理(无差错)

                                  

                                                                           图3 MODBUS事务处理(异常响应) 

注释便地等能不出现的应

串行链路上第一个 MODBUS 执行的长度约束限制了 MODBUS PDU 大小(最大 RS485ADU=256字节)。因此,对串行链路通信来说,MODBUS PDU=256-服务器地址(1 字节)-CRC(2 字节)=253字节。

     从而:

            RS232/RS485ADU=253字节+服务器地址(1byte)+CRC(2字节)=256字节。

            TCP MODBUS ADU = 249 字节+ MBAP (7 字节) = 256 字节。

    MODBUS 协议定义了三种PDU。它们是:

    ●MODBUS 请求PDU,mb_req_pdu

    ●MODBUS 响应PDU,mb_rsp_pdu

    ●MODBUS 异常响应PDU,mb_excep_rsp_pdu

 2.2 数据编码

MODBUS 使用一个‘big-Endian’表示地址和数据项。这意味着当发送多个字节时,首先发送最高有效位。例如:

16–比特  0x1234  发送的第一字节为 0x12  然后 0x34

  2.3 MODBUS数据模型

MODBUS以一系列具有不同特征表格上的数据模型为基础。四个基本表格为:

                           

关于数据模型的理解可以结合下文的具体应用实例,重点注意对象类型是bit还是字节,以及访问类型是只读还是读写。

对于基本表格中任何一项,协议都允许单个地选择 65536 个数据项,而且设计那些项的读写操作可以越过多个连续数据项直到数据大小规格限制,这个数据大小规格限制与事务处理功能码有关。

很显然,必须将通过 MODBUS 处理的所有数据放置在设备应用存储器中。但是,存储器的物理地址不应该与数据参考混淆。要求仅仅是数据参考与物理地址的链接。

MODBUS 功能码中使用的 MODBUS 逻辑参考数字是以 0 开始的无符号整数索引。

3 RTU传输模式

当设备使用 RTU (Remote Terminal Unit) 模式在 Modbus串行链路通信,报文中每个8位字节含有两个4位十六进制字符。这种模式的主要优点是较高的数据密度,在相同的波特率下比 ASCII 模式有更高的吞吐率。每个报文必须以连续的字符流传送。

RTU 模式每个字节 ( 11 位 ) 的格式为 :

编码系统:      8–位二进制

报文中每个8位字节含有两个4位十六进制字符(0–9,A–F)

Bits per Byte: 1 起始位

8 数据位, 首先发送最低有效位

1 位作为奇偶校验

1 停止位

偶校验是要求的,其它模式 ( 奇校验, 无校验 ) 也可以使用。 为了保证与其它产品的最大兼

容性,同时支持无校验模式是建议的。默认校验模式模式 必须为偶校验。

注 : 使用无校验要求 2 个停止位。

字符是如何串行传送的:

每个字符或字节均由此顺序发送(从左到右):

最低有效位 (LSB) . . . 最高有效位 (MSB)

                                                           

                                                                                  图4 RTU模式位序列

设备配置为奇校验、偶校验或无校验都可以接受。如果无奇偶校验,将传送一个附加的停止位以填充字符帧:

                                                           

                                                              图5 RTU模式位序列(无校验的特殊情况)

帧检验域: 循环冗余校验 (CRC)

帧描述 :

                                                        

                                                                                         图6 RTU报文帧

→ Modbus RTU 帧最大为 256 字节。

3.1 Modbus 报文 RTU 帧

由发送设备将 Modbus报文构造为带有已知起始和结束标记的帧。这使设备可以在报文的开始接收新帧,并且知道何时报文结束。不完整的报文必须能够被检测到而错误标志必须作为结果被设置。在RTU模式,报文帧由时长至少为3.5个字符时间的空闲间隔区分。在后续的部分,这个时间区间被称作t3.5。

                                    

                                         

                                                                                图7 RTU报文帧

整个报文帧必须以连续的字符流发送。如果两个字符之间的空闲间隔大于1.5个字符时间,则报文帧被认为不完整应该被接收节点丢弃。

                                             

注 :

RTU接收驱动程序的实现,由于t1.5和t3.5的定时,隐含着大量的对中断的管理。在高通信速率下,这导致CPU负担加重。因此,在通信速率等于或低于19200 Bps时,这两个定时必须严格遵守;对于波特率大于19200Bps的情形,应该使用2个定时的固定值:建议的字符间超时时间(t1.5)为 750µs,帧间的超时时间 (t3.5) 为1.750ms。

3.2 CRC校验

在 RTU 模式包含一个对全部报文内容执行的,基于循环冗余校验 (CRC - Cyclical Redundancy Checking) 算法的错误检验域。CRC 域检验整个报文的内容。不管报文有无奇偶校验,均执行此检验。
CRC 包含由两个 8 位字节组成的一个 16 位值。CRC 域作为报文的最后的域附加在报文之后。计算后,首先附加低字节,然后是高字节。CRC高字节为报文发送的最后一个子节。附加在报文后面的 CRC 的值由发送设备计算。接收设备在接收报文时重新计算 CRC 的值,并将计算结果于实际接收到的 CRC 值相比较。如果两个值不相等,则为错误。CRC 的计算, 开始对一个 16 位寄存器预装全 1。 然后将报文中的连续的 8 位子节对其进行后续的计算。只有字符中的 8 个数据位参与生成 CRC 的运算,起始位,停止位和校验位不参与 CRC计算。
CRC 的生成过程中, 每个 8–位字符与寄存器中的值异或。然后结果向最低有效位(LSB)方向移动(Shift) 1 位,而最高有效位(MSB)位置充零。 然后提取并检查 LSB:如果 LSB 为 1, 则寄存器中的值与一个固定的预置值异或;如果 LSB 为 0, 则不进行异或操作。这个过程将重复直到执行完 8 次移位。完成最后一次(第 8 次)移位及相关操作后,下一个 8位字节与寄存器的当前值异或,然后又同上面描述过的一样重复 8 次。当所有报文中子节都运算之后得到的寄存器忠的最终值,就是 CRC。
当 CRC 附加在报文之后时,首先附加低字节,然后是高字节。Modbus中文版协议在附录 B 含有 CRC 生成的详细示例。


                                                  第二部分:Modbus--RTU协议在串行链路上的实现

1 AI模拟量输入

举例:读取12节单体电压数据,子板地址为1:

Modbus RTU 格式: 《十六进制》

上位机发送:01 04 00 00 00 0C F0 0F

从机应答:  01 04 18 80 00 81 00 82 00 83 00 84 00 85 00 86 00 87 00 88 00 89 00 90 00 91 00 0F 3C

报文详解:

上位机发送的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

04H

读输入寄存器<READ_INPUT>

起始寄存器地址

2

0000H

输入寄存器地址:

0000H—对应电压采集通道0<30001寄存器>

0001H—对应电压采集通道1<30002寄存器>

 

。。。。。。。。。

000CH—对应电压采集通道12<30012寄存器>

该寄存器地址位于【3】区

数据发送顺序:高字节在前,如0007H,顺序00 07

读取寄存器数量

2

000CH

读取12个寄存器里的内容<30001-30012寄存器>

数据发送顺序:高字节在前,如000C,则顺序:00 0C

CRC校验

2

0FF0H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

下位机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

04H

读输入寄存器 <READ_INPUT>

字节数

1

18H

其中: 18H = AI 个数* 2 = 12 * 2

返回数据

24

8000H

8100H

······

9100H

0通道,32768

1通道,33024

······

11通道,37120

数据发送顺序:高字节在前,如8000,则顺序:80 00

CRC校验

2

3C0FH

前面所有数据的CRC校验

数据发送顺序:低字节在前,如A050,则顺序:50 A0

2 DI开关量输入

举例:读取8路DI,模块地址=1:

Modbus RTU格式: 《十六进制》

上位机发送:01 02 00 00 00 08 79 CC

从机应答:  01 02 01 02 20 49

举例:读取18路DI,模块地址=1:

Modbus RTU格式: 《十六进制》

上位机发送:01 02 00 00 00 12 F8 07

从机应答:  01 02 03 03 FF FF 89 FE

报文详解:

上位机发送的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

02H

读离散量输入 <READ_STATE>

起始寄存器地址

2

0000H

0000H-该寄存器对应DI0的开关状态<10001寄存器>

0001H-该寄存器对应DI1的开关状态<10002寄存器>

。。。。。。。。。

0007H-该寄存器对应DI7 的开关状态<10012寄存器>

该寄存器地址位于【1】区

数据发送顺序:高字节在前,如0007H,则顺序:00 07

输入数量

2

0008H

读取8个开关量输入状态<10001-10008寄存器>

数据发送顺序:高字节在前,如0008,则顺序:00 08

CRC校验

2

CC79H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

下位机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址 = 1

功能码

1

02H

读取寄存器 <READ_STATE>

返回字节长度

1

01H

返回01个字节的开关量输入状态

其中: 01H = 开关量个数 / 8 = 8 / 8

 

如果个数>8,且<=16,则 01H 应该改为:02H

以此类推个数

返回数据

1

02H

02H从低位到高位代表DI0-DI7的输入状态

02H即表示:DI1高电平ON,其他低电平OFF

如果个数>8,且<=16

则字节数=2 发送报文=0102H

数据发送顺序为高字节在前: 01 02

01 即表示: DI0-DI7

02 即表示: DI8-DI15 以此类推个数

CRC校验

2

4920H

前面所有数据的CRC校验

数据发送顺序:低字节在前,如A050,则顺序:50 A0

3 AO单路模拟量输出

举例:模块地址=1:

Modbus RTU 格式: 《十六进制》

主机发送《AO-0 输出十进制 10000》: 01 06 00 00 27 10 93 F6

从机应答《AO-0 输出十进制 10000》: 01 06 00 00 27 10 93 F6

主机发送《AO-1 输出十进制 10000》: 01 06 00 01 27 10 C2 36

从机应答《AO-1 输出十进制 10000》: 01 06 00 01 27 10 C2 36

主机发送《AO-2 输出十进制 10000》: 01 06 00 02 27 10 32 36

从机应答《AO-2 输出十进制 10000》: 01 06 00 02 27 10 32 36

报文详解:

主机发送的报文格式:《设置模拟量输出AO-0通道》

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

06H

写寄存器 <WRITE_1_HOLD>

寄存器地址

2

0000H

0000H -该寄存器对应通道 0 <40001 寄存器>

。。。。。。

0003H - 该寄存器对应通道 3 <40004 寄存器>

该寄存器地址位于【4】区

数据发送顺序:高字节在前,如 0003,则顺序: 00 03

写入数据

2

2710H

设置通道 0 的输出值为 10000

CRC校验

2

F693H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

从机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

06H

写寄存器 <WRITE_1_HOLD>

寄存器地址

2

0000H

“主机发送的报文格式”解析

写入数据

2

2710H

“主机发送的报文格式”解析

CRC校验

2

F693H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

4 AO模拟量输出

举例:读取4路AO 数据,模块地址=1:

Modbus RTU 格式: 《十六进制》

主机发送: 01 03 00 00 00 04 44 09

从机应答: 01 03 08 21 E6 3B 95 11 22 00 00 7C 6D

报文详解:

主机发送的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

03H

读取寄存器 <READ_HOLD>

起始寄存器地址

2

0000H

0000H -该寄存器对应通道AO0<40001 寄存器>

。。。。。。

0003H - 该寄存器对应通道AO3 <40004 寄存器>

该寄存器地址位于【4】区

数据发送顺序:高字节在前,如 0003,则顺序: 00 03

读取寄存器数量

2

0004H

读取4个寄存器里的内容 <40001-40004 寄存器>

数据发送顺序:高字节在前,如0004,则顺序:00 04

CRC校验

2

0944H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

从机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

03H

读取寄存器 <READ_HOLD>

返回数据长度

1

08H

其中: 08H = AO个数* 2 = 4 * 2

返回数据

8

21E6H

。。。。。。

0000H

0 通道, 8678

。。。。。。

3 通道, 0000

数据发送顺序:高字节在前,如21E6,则顺序:21 E6

CRC校验

2

6D7CH

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

5 DO单路开关量输出(例如继电器控制)

举例:模块地址=1:

Modbus RTU 格式: 《十六进制》

主机发送《DO-0 闭合》: 01 05 00 00 FF 00 8C 3A 《Modbus RTU》

从机应答《DO-0 闭合》: 01 05 00 00 FF 00 8C 3A 《Modbus RTU》

主机发送《DO-0 断开》: 01 05 00 00 00 00 CD CA 《Modbus RTU》

从机应答《DO-0 断开》: 01 05 00 00 00 00 CD CA 《Modbus RTU》

 

主机发送《DO-1 闭合》: 01 05 00 01 FF 00 DD FA 《Modbus RTU》

从机应答《DO-1 闭合》: 01 05 00 01 FF 00 DD FA 《Modbus RTU》

主机发送《DO-1 断开》: 01 05 00 01 00 00 9C 0A 《Modbus RTU》

从机应答《DO-1 断开》: 01 05 00 01 00 00 9C 0A 《Modbus RTU》

报文详解:

主机发送的报文格式: 《设置 DO_0 闭合,模块地址=1:》

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

05H

写寄存器 <WRITE_1_COIL>

起始寄存器地址

2

0000H

寄存器地址:

0000H -开关量输出通道0的开关状态<00001寄存器>

。。。 。。。

0007H -开关量输出通道7的开关状态<00008寄存器>

该寄存器地址位于【0】区

数据发送顺序:高字节在前,如 0007,则顺序: 00 07

写入数据

2

FF00H

FF00H 写入0000H 寄存器中

FF00H:表示 DO 闭合

0000H:表示 DO 断开

数据发送顺序:高字节在前,如FF00,则顺序: FF 00

CRC校验

2

3A8C

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

从机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

05H

写寄存器 <WRITE_1_COIL>

起始寄存器地址

2

0000H

“主机发送的报文格式”解析

写入数据

2

FF00H

“主机发送的报文格式”解析

CRC校验

2

3A8C

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

6 DO开关量输出(例如读取继电器状态)

举例:读取8路DO状态,模块地址=1:

Modbus RTU 格式: 《十六进制》

主机发送: 01 01 00 00 00 08 3D CC

从机应答: 01 01 01 03 11 89

举例:读取 12 路 DO 状态,模块地址=7:

Modbus RTU 格式: 《十六进制》

主机发送: 07 01 00 00 00 0C 3C 69

从机应答: 07 01 02 03 FF 71 4C

报文详解:

主机发送的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

01H

读取寄存器 <READ_COIL>

起始寄存器地址

2

0000H

0000H –开关量输出通道0状态<00001 寄存器>

。。。 。。。

0007H -开关量输出通道7状态<00008 寄存器>

该寄存器地址位于【0】区

数据发送顺序:高字节在前,如0007,则顺序:00 07

读取线圈数量

2

0008H

读取 8 个寄存器里的内容 <00001-00008 寄存器>

数据发送顺序:高字节在前,如 0008,则顺序: 00 08

CRC校验

2

CC3D

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

从机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

01H

读取开关量输出 <READ_COIL>

返回数据长度

1

01H

其中: 01H = 通道个数 / 8 = 8 / 8

如果个数>8,且<=16,则 01H 应该改为: 02H

以此类推个数

返回数据

1

03H

03H 从低位到高位代表 DO0-DO7 的开关状态

03H 即表示:DO0DO1闭合ON DO2-7断开OFF

如果个数>8,且<=16

则字节数=2 发送报文=0302H

数据发送顺序为高字节在前: 03 02

03 即表示: DO0-DO7

02 即表示: DO8-DO15

以此类推个数

CRC校验

2

8911H

前面所有数据的CRC校验

数据发送:低字节在前,如A050,则顺序:50 A0

7 AD模块采集的温度数值

举例:读取 8 路 PT100 数据,模块地址=1:

Modbus RTU 格式: 《十六进制》

主机发送: 01 04 00 00 00 08 F1 CC

从机应答: 01 04 10 A0 B0 A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A6 B6 A7 B7 E2 B0

报文详解:

上位机发送的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

04H

读取寄存器 <READ_INPUT>

起始寄存器地址

2

0000H

输入寄存器地址:

0000H—对应电压采集通道0<30001寄存器>

。。。。。。。。。

0007H—对应电压采集通道12<30012寄存器>

该寄存器地址位于【3】区

数据发送顺序:高字节在前,如0007H,顺序00 07

读取寄存器数量

2

0008H

读取8个寄存器里的内容<30001-30008寄存器>

数据发送顺序:高字节在前,如0008,则顺序: 00 08

CRC校验

2

CCF1H

前面所有数据的CRC校验

数据发送:低字节在前,如CCF1,则顺序:F1 CC

下位机应答的报文格式:

发送内容

字节数

发送报文

备注

模块地址

1

01H

模块地址=1

功能码

1

04H

读取寄存器 <READ_INPUT>

返回数据长度

1

10H

其中:10H = 通道个数* 2 = 8 * 2

返回数据

16

21E6H

3B95H

。。。。。。

0000H

0 通道,8678

1 通道,15253

。。。。。。

7 通道, 0000

数据发送顺序:高字节在前,如21E6,则顺序:21 E6

CRC校验

2

B0E2H

前面所有数据的CRC校验

数据发送:低字节在前,如B0E2,则顺序: E2 B0

附录说明:

本文档仅对Modbus-RTU协议在串行链路上的使用做简单说明,文中对于Modbus协议各个阶段的错误处理并未涉及,Modbus中文版协议对于各个错误状态的反馈报文有较为详细的解释,有需要可以参阅。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值