当前位置: 代码迷 >> 综合 >> Modbus TCPIP基础知识-------------ModbusTCPIP协议概述(一)
  详细解决方案

Modbus TCPIP基础知识-------------ModbusTCPIP协议概述(一)

热度:29   发布时间:2024-01-22 06:05:24.0

文章目录

    • 1 ModbusTcp
      • 1.1 MBAP报文
      • 1.2 MODBUS请求的生成
      • 1.3 MODBUS响应的生成
    • 2 Modbus介绍
      • 2.1 MODBUS数据模型
      • 2.2 公共功能码定义
    • 3 功能码描述
      • 3.1 01(0x01)功能码---读线圈
      • 3.2 02(0x02)功能码---读离散量输入
      • 3.3 03(0x03)功能码---读保持寄存器
      • 3.4 04(0x04)功能码---读输入寄存器
      • 3.5 05(0x05)功能码---写单个线圈
      • 3.6 06(0x06)功能码---写单个寄存器
      • 3.7 15(0x0F)功能码---写多个线圈
      • 3.8 16(0x10)功能码---写多个寄存器

1 ModbusTcp

简单的理解一下Modbus TCP/IP协议的内容,就是去掉了modbus协议本身的CRC校验,增加了MBAP 报文头。TCP/IP上的MODBUS的请求/响应如下图所示:

TCPIPmodbus请求和响应

1.1 MBAP报文

首先来看一下,MBAP 报文头都包括了哪些信息和内容

长度 描述 客户机 服务器
事务元标识符 2个字节 MODBUS请求/响应事务处理的识别码 客户机启动 服务器从接收的请求中重新复制
协议标识符 2个字节 0=MODBUS协议 客户机启动 服务器从接收的请求中重新复制
长度 2个字节 以下字节的数量 客户机启动(请求) 服务器(响应)启动
单元标识符 1个字节 串行链路或其它总线上连接的远程从站的识别码 客户机启动 服务器从接收的请求中重新复制

事务元标识符(2个字节):用于事务处理配对。在响应中,MODBUS服务器复制请求的事务处理标识符。这里在以太网传输中存在一个问题,就是先发后至,我们可以利用这个事务处理标识符做一个TCP序列号,来防止这种情况所造成的数据收发错乱(这里我们先不讨论这种情况,这个事务处理标识符我们统一使用0x00,0x01)
协议标识符(2个字节):modbus协议标识符为0x00,0x00
长度(2个字节):长度域是下一个域的字节数,包括单元标识符和数据域。
单元标识符(1个字节):该设备的编号。(可以使用PLC的IP地址标识)。

在MODBUS MODBUS+串行链路子网中对设备进行寻址时,这个域是标识设备地址。在这种情况下,“Unit Identifier”携带一个远端设备的MODBUS从站地址:

  • 如果MODBUS服务器连接到MODBUS+或MODBUS串行链路子网,并通过一个桥或网关配置地址这个服务器,MODBUS单元标识符对识别连接到网桥或网关后的子网的从站设备是必需的。目的IP地址识别了网桥本身的地址,而网桥则使用MODBUS单元标识符将请求转交给正确的从站设备。
  • 分配串行链路上MODBUS从站设备地址为1~247(10进制),地址0作为广播地址。
    对 TCP/IP 来说,利用IP地址寻址MODBUS服务器;因此,MODBUS单元标识符是无用的。必需使用值0xFF。
  • 当对直接连接到TCP/IP网络上的MODBUS服务器寻址时,建议不要在“单元标识符”域使用有效的MODBUS从站地址。在一个自动系统中重新分配IP地址的情况下,并且如果以前分配给MODBUS服务器的IP地址又被指配给网关,使用一个有效的从站地址可能会由于网关的路由不畅而引起麻烦。使用无效的从站地址,网关仅是简单地废弃MODBUD PDU,而不会有任何问题。建议:在采用0xFF作为“单元标识符”的无效值。

    注:0也可以用作与MODBUS/TCP设备直接通信。

1.2 MODBUS请求的生成

在收到来自用户应用的需求后,客户端必须生成一个MODBUS请求,并发送到TCP管理。下表显示MODBUS请求ADU编码:

类型 描述 字节大小 实例
MBAP报文头 事务处理标识符Hi 1 0x15
事务处理标识符Lo 1 0x01
协议标识符 2 0x0000
长度 2 0x0006
单元标识符 1 0xFF
MODBUS请求 功能码 1 0x03
起始地址 2 0x0005
寄存器数量 2 0x0001

1.3 MODBUS响应的生成

一旦处理请求,MODBUS 服务器必须使用适当的MODBUS服务器事务处理生成一个响应,并且必须将响应发送到TCP管理组件。

根据处理结果,可以生成两类响应:

  • 肯定的MODBUS响应:
    • 响应功能码 = 请求功能码
  • MODBUS异常响应:
    • 目的是为客户机提供与处理过程检测到的错误相关的信息
    • 响应功能码 = 请求功能码+0x80
    • 提供异常码来表明出错的原因。
异常码 MODBUS名称 备注
01 非法的功能码 服务器不了解功能码
02 非法的数据地址 与请求有关
03 非法的数据值 与请求有关
04 服务器故障 在执行过程中,服务器故障
05 确认 服务器接受服务调用,但是需要相对长的时间完成服务。因此,服务器仅返回一个服务调用接收的确认。
06 服务器繁忙 服务器不能接受MODBUS请求PDU。客户应用由责任决定是否和何时重发请求。
0A 网关故障 网关路经是无效的。
0B 网关故障 目标设备没有响应。网关生成这个异常信息。

通过上面的两步,一个Modbus TCP的客户端连接已经建立起来了,下面我们就来分析Modbus TCP协议的具体内容与实现方式了。

2 Modbus介绍

2.1 MODBUS数据模型

基本表格 对象类型 访问类型 内容
离散量输入 单个比特 只读 I/O系统提供这种类型数据
线圈 单个比特 读写 通过应用程序改变这种类型数据
输入寄存器 16比特字 只读 I/O系统提供这种类型数据
保持寄存器 16比特字 读写 通过应用程序改变这种类型数据

2.2 公共功能码定义

功能码
子码 十六进制
比特访问
物理离散量输入 读输入离散量 02 02
内部比特或物理线圈 读线圈 01 01
写单个线圈 05 05
写多个线圈 15 0F
16比特访问 输入寄存器 读输入寄存器 04 04
内部存储器或物理输出存储器 读多个寄存器 03 03
写单个寄存器 06 06
写多个寄存器 16 10
读/写多个寄存器 23 17
屏蔽写寄存器 22 16
文件记录访问 读文件记录 20 6 14
写文件记录 21 6 15

3 功能码描述

我们直接用实例来说明。

3.1 01(0x01)功能码—读线圈

在一个远程设备中,使用该功能码读取线圈的1 至2000 连续状态。请求PDU 详细说明了起始地址,即指定的第一个线圈地址和线圈编号。从零开始寻址线圈。因此寻址线圈1-16 为0-15。

根据数据域的每个比特将响应报文中的线圈分成为一个线圈。指示状态为1= ON 和0= OFF。

第一个数据字节的LSB(最低有效位)包括在询问中寻址的输出。其它线圈依次类推,一直到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。

如果返回的输出数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。

请求与响应格式

请求PDU

功能码 1个字节 0x01
起始地址 2个字节 0x0000至0xFFFF
线圈数量 2个字节 1至2000(0x7D0)

响应PDU

功能码 1个字节 0x01
字节数 1个字节 N*
线圈状态 N个字节 n=N或N+1

注:*N=输出数量/8,如果余数不等于0,那么N=N+1

错误

差错码 1个字节 功能码+0x80
异常码 1个字节 01或02或03或04

这是一个请求读离散量输出20-38 的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 01 功能 01
起始地址Hi 00 字节数 03
起始地址Lo 13 输出状态27-20 CD
输出数量Hi 00 输出状态35-28 6B
输出数量Lo 13 输出状态38-36 05

将输出27-20 的状态表示为十六进制字节值CD,或二进制1100 1101。输出27 是这个字节的MSB,输出20 是LSB。
通常,将一个字节内的比特表示为MSB 位于左侧,LSB 位于右侧。第一字节的输出从左至右为27至20。下一个字节的输出从左到右为35至28。当串行发射比特时,从LSB向MSB传输:20 . . .27、28 . . . 35 等等。
在最后的数据字节中,将输出状态38-36表示为十六进制字节值05,或二进制0000 0101。输出38 是左侧第六个比特位置,输出36 是这个字节的LSB。用零填充五个剩余高位比特。

注:用零填充五个剩余比特(一直到高位端)。

3.2 02(0x02)功能码—读离散量输入

在一个远程设备中,使用该功能码读取离散量输入的1 至2000 连续状态。请求PDU 详细说明了起始地址,即指定的第一个输入地址和输入编号。从零开始寻址输入。因此寻址输入1-16 为0-15。

根据数据域的每个比特将响应报文中的离散量输入分成为一个输入。指示状态为1= ON 和0=OFF。第一个数据字节的LSB(最低有效位)包括在询问中寻址的输入。其它输入依次类推,一直
到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。

如果返回的输入数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。

请求PDU

功能码 1个字节 0x02
起始地址 2个字节 0x0000至0xFFFF
线圈数量 2个字节 1至2000(0x7D0)

响应PDU

功能码 1个字节 0x82
字节数 1个字节 N*
线圈状态 N*x1个字节

注:*N=输出数量/8,如果余数不等于0,那么N=N+1

错误

差错码 1个字节 功能码+0x82
异常码 1个字节 01或02或03或04

这是一个请求读离散量输入197-218 的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 02 功能 02
起始地址Hi 00 字节数 03
起始地址Lo C4 输出状态204-197 AC
输出数量Hi 00 输出状态212-205 DB
输出数量Lo 16 输出状态218-213 35

将离散量输入状态204-197表示为十六进制字节值AC,或二进制1010 1100。输入204是这个字节的MSB,输入197 是这个字节的LSB。

将离散量输入状态218-213表示为十六进制字节值35,或二进制0011 0101。输入218位于左侧第3 比特,输入213 是LSB。

注:用零填充2 个剩余比特(一直到高位端)。

3.3 03(0x03)功能码—读保持寄存器

在一个远程设备中,使用该功能码读取保持寄存器连续块的内容。请求PDU说明了起始寄存器地址和寄存器数量。从零开始寻址寄存器。因此,寻址寄存器1-16 为0-15
将响应报文中的寄存器数据分成每个寄存器有两字节,在每个字节中直接地调整二进制内容。
对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。

请求

功能码 1个字节 0x03
起始地址 2个字节 0x0000至0xFFFF
寄存器数量 2个字节 1至125(0x7D)

响应

功能码 1个字节 0x03
字节数 1个字节 2xN*
寄存器值 N*x2个字节

注:*N=输出数量/8,如果余数不等于0,那么N=N+1

错误

差错码 1个字节 0x83
异常码 1个字节 01或02或03或04

这是一个请求读保持寄存器108-110 的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 03 功能 03
高起始地址 00 字节数 06
低起始地址 6B 寄存器值Hi(108) 02
高寄存器编号 00 寄存器值Lo(108) 2B
低寄存器编号 03 寄存器值Hi(109) 00
寄存器值Lo(109) 00
寄存器值Hi(110) 00
寄存器值Lo(110) 64

将寄存器108的内容表示为两个十六进制字节值02 2B,或十进制555。将寄存器109-110的内容分别表示为十六进制00 00 和00 64,或十进制0 和100。

3.4 04(0x04)功能码—读输入寄存器

在一个远程设备中,使用该功能码读取1 至大约125 的连续输入寄存器。请求PDU 说明了起始地址和寄存器数量。从零开始寻址寄存器。因此,寻址输入寄存器1-16 为0-15
将响应报文中的寄存器数据分成每个寄存器为两字节,在每个字节中直接地调整二进制内容。
对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。

请求

功能码 1个字节 0x04
起始地址 2个字节 0x0000至0xFFFF
寄存器数量 2个字节 0x0000至0x007D

响应

功能码 1个字节 0x04
字节数 1个字节 2xN*
寄存器值 N*x2个字节

注:*N=输入寄存器的数量

错误

差错码 1个字节 0x84
异常码 1个字节 01或02或03或04

这是一个请求读输入寄存器9的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 04 功能 04
起始地址Hi 00 字节数 02
起始地址Lo 08 输入寄存器值Hi(9) 00
输入寄存器数量Hi 00 输入寄存器值Lo(9) 0A
输入寄存器Lo 01

将输入寄存器9 的内容表示为两个十六进制字节值00 0A,或十进制10。

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

在一个远程设备上,使用该功能码写单个输出为ON 或OFF。
请求数据域中的常量说明请求的ON/OFF状态。十六进制值FF 00请求输出为ON。十六进制值00 00 请求输出为OFF。其它所有值均是非法的,并且对输出不起作用。
请求PDU说明了强制的线圈地址。从零开始寻址线圈。因此,寻址线圈1 为0。线圈值域的常量说明请求的ON/OFF 状态。十六进制值0XFF00请求线圈为ON。十六进制值0X0000请求线圈为
OFF。其它所有值均为非法的,并且对线圈不起作用。
正常响应是请求的应答,在写入线圈状态之后返回这个正常响应。

请求

功能码 1个字节 0x05
输出地址 2个字节 0x0000至0xFFFF
输出值 2个字节 0x0000至0xFF00?

响应

功能码 1个字节 0x05
输出地址 2个字节 0x0000至0xFFFF
输出值 2个字节 0x0000至0xFF00

错误

差错码 1个字节 0x85
异常码 1个字节 01或02或03或04

这是一个请求写线圈173为ON的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 05 功能 05
输出地址Hi 00 输出地址Hi 00
输出地址Lo AC 输出地址Lo AC
输出值Lo 00 输出值Lo 00

3.6 06(0x06)功能码—写单个寄存器

在一个远程设备中,使用该功能码写单个保持寄存器。
请求PDU 说明了被写入寄存器的地址。从零开始寻址寄存器。因此,寻址寄存器1 为0。
正常响应是请求的应答,在写入寄存器内容之后返回这个正常响应。

请求

功能码 1个字节 0x06
寄存器地址 2个字节 0x0000至0xFFFF
寄存器值 2个字节 0x0000至0xFFFF

响应

功能码 1个字节 0x06
寄存器地址 2个字节 0x0000至0xFFFF
寄存器值 N*x2个字节 0x0000至0xFFFF

错误

差错码 1个字节 0x86
异常码 1个字节 01或02或03或04

这是一个请求将十六进制 00 03 写入寄存器2的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 06 功能 06
寄存器地址Hi 00 输出地址Hi 00
寄存器地址Lo 01 输出地址Lo 01
寄存器值Hi 00 输出值Hi 00
寄存器值Lo 03 输出值Hi 03

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

在一个远程设备中,使用该功能码强制线圈序列中的每个线圈为ON 或OFF。请求PDU说明了强制的线圈参考。从零开始寻址线圈。因此,寻址线圈1 为0。
请求数据域的内容说明了被请求的ON/OFF 状态。域比特位置中的逻辑“1”请求相应输出为ON。域比特位置中的逻辑“0”请求相应输出为OFF。
正常响应返回功能码、起始地址和强制的线圈数量。

请求PDU

功能码 1个字节 0x0F
起始地址 2个字节 0x0000至0xFFFF
输出数量 2个字节 0x0001至0x07B0
字节数 1个字节 N*
输出值 N*X1个字节

注:*N=输出数量/8,如果余数不等于0,那么N=N+1

响应PDU

功能码 1个字节 0x0F
起始地址 2个字节 0x0000至0xFFFF
输出数量 2个字节 0x0001至0x07B0

错误

差错码 1个字节 0x8F
异常码 1个字节 01或02或03或04

这是一个请求从线圈20开始写入10个线圈的实例:

请求的数据内容为两个字节:十六进制CD 01 (二进制1100 1101 0000 0001)。使用下列方法,二进制比特对应输出。

比特 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
输出 27 26 25 24 23 22 21 20 29 28

传输的第一字节(十六进制CD)寻址为输出27-20,在这种设置中,最低有效比特寻址为最低输出(20)。
传输的下一字节(十六进制01)寻址为输出29-28,在这种设置中,最低有效比特寻址为最低输出(28)。
应该用零填充最后数据字节中的未使用比特。

请求 响应
域名 十六进制 域名 十六进制
功能 0F 功能 0F
起始地址Hi 00 起始地址Hi 00
起始地址Lo 13 起始地址Lo 13
输出数量Hi 00 输出数量Hi 00
输出数量Lo 0A 输出数量Lo 0A
字节数 02
输出值Hi CD
输出数量Lo 01

3.8 16(0x10)功能码—写多个寄存器

在一个远程设备中,使用该功能码写连续寄存器块(1 至约120 个寄存器)。
在请求数据域中说明了请求写入的值。每个寄存器将数据分成两字节。
正常响应返回功能码、起始地址和被写入寄存器的数量。

请求

功能码 1个字节 0x10
起始地址 2个字节 0x0000至0xFFFF
寄存器数量 2个字节 0x0001至0x0078
字节数 1个字节 2xN*
寄存器值 N*x2个字节

注:*N=寄存器的数量

响应

功能码 1个字节 0x04
起始地址 2个字节 0x0000至0xFFFF
寄存器数量 2个字节 1至123(0x7B)

错误

差错码 1个字节 0x90
异常码 1个字节 01或02或03或04

这是一个请求将十六进制00 0A 和01 02 写入以2 开始的两个寄存器的实例:

请求 响应
域名 十六进制 域名 十六进制
功能 10 功能 10
起始地址Hi 00 起始地址Hi 00
起始地址Lo 01 起始地址Lo 01
寄存器数量Hi 00 寄存器数量Hi 00
寄存器数量Lo 02 寄存器数量Lo 02
字节数 04
寄存器值Hi 00
寄存器值Lo 0A
寄存器值Hi 01
寄存器值Lo 02
  相关解决方案