MODBUS_RTU协议:是指国际上通用的 MODBUS_RTU议。以后简称MODBUS协议。详见《MODBUS协议中文版》官方文档。
JM_MOD协议:是指完全在遵循MODBUS_RTU 协议的规则下,为了适应某些捷麦公司的设备和应用场景对MODBUS_RTU协议做了更细致定义的协议。 支持JM_MOD协议的设备或软件接口可与任何标准MODBUS主机或设备直接通信。
JM_BUS协议:是指基于JM_MOD协议,为了 适应远程测控一个站点的各种数据一次采集的特性做了相应改动的协议。此协议不遵循MODBUS_RTU, 不能与标准MODBUS主机或设备直接通信。详见《JMBUS协议说明》。
本协议适用于JM公司所有标明“支持MODBUS协议”的硬件设 备和应用软件。例如T40S无线PLC、KZ04扩展模块、远程通PC组态和远程通手机组态等。
JM_MOD的数据通信指令为标准的MODBUS_RTU协议,以下为 MODBUS的基本协议规范。
图1. 通用的MODBUS帧
图2:MODBUS事务处理(无差错)
图3:MODBUS事务处理(异常响应)
下文中,讲接收数据并处理响应的“服务器”称之 为“MODBUS从机”。
JM_MOD 使用一个‘big-Endian’ 表示地址和数据项。这意 味着当发射多个字节时,首先发送最高有效位。例如:
寄存器大小 | 值 | |
16-比特 | 0x1234 | 发送的第一字节为0x12然后0x34 |
同样的道理,如果传送实数数据时,首先发送最高有效位, 最后发送最低位(组态软件中的HV1 HV2 HV3 HV4)。
特例:起始地址为330001和430001的浮点型数据内容是小端模式,组态软件中的HV4 HV3 HV2 HV1。
JM_MOD以一系列具有不同特征的数据模型为基础。几个基本数据模型为:
基本类型 | 对象类型 | 属性 | 内容 | 常用符号 |
离散量输入 | 单个bit | 只读 | I/O 系统提供数据 | DI、I*.* |
线圈 | 单个bit | 读写 | 通过应用程序改变数据 | DO、Q*.* |
整数输入存储区 | 16bit | 只读 | I/O 系统提供数据 | -- |
字节输入存储区 | 8bit | 只读 | I/O 系统提供数据 | -- |
实数输入存储区 | 32bit | 只读 | I/O 系统提供数据 | AI |
整数保持存储区 | 16bit | 读写 | 通过应用程序改变数据 | -- |
字节保持存储区 | 8bit | 读写 | 通过应用程序改变数据 | V |
实数保持存储区 | 32bit | 读写 | 通过应用程序改变数据 | AQ |
由于MODBUS协议中没有所有的字节和实数的定义,而只有一个笼统的“04输入寄存器和03/06/10保持寄存器”的说法。在JM_MOD协议中需要在借用MODBUS协议中的偏移地址的最高位的值和命令码来区分设备中的不同存储区,详见下一章节。
定义汇总,离散和整数部分本文说明,字节和实数部分在“下篇”中说明。
存储区名称 | 意义 | 功能码 | 地址(标准表示) | 数据类型 | 传输形式 |
离散输入 | 读离散输入 | 02 | 0 (10001) | bit | bit |
离散输出 | 读离散输出 | 01 | 0 (00001) | bit | bit |
写单个离散输出 | 05 | 0 (00001) | bit | bit | |
写多个离散输出 | 0x0F | 0 (00001) | bit | bit | |
字节输入 | 读字节输入 | 04 | 10000 (310001) | 16bit | 16bit |
字节输出 | 读字节输出 | 03 | 10000 (410001) | 16bit | 16bit |
写单个字节输出 | 06 | 10000 (410001) | 16bit | 16bit | |
写多个字节输出 | 0x10 | 10000 (410001) | 16bit | 16bit | |
整数输入 | 读整数输入 | 04 | 0 (30001) | 8bit | 16bit |
整数输出 | 读整数输出 | 03 | 0 (30001) | 8bit | 16bit |
写单个整数输出 | 06 | 0 (30001) | 8bit | 16bit | |
写多个整数输出 | 0x10 | 0 (30001) | 8bit | 16bit | |
实数输入 | 读实数输入 | 04 | 30000 (330001) | 32bit | 16bit |
实数输出 | 读实数输出 | 03 | 30000 (430001) | 32bit | 16bit |
写单个实数输出 | 06 | 30000 (430001) | 32bit | 16bit | |
写多个实数输出 | 0x10 | 30000 (430001) | 32bit | 16bit |
此功能代码是远程安装设备驱动程序用来读取离散输入的1~ 2000个邻近状态。请求PDU详细说明了离散输入的起始地址,第一个离散输入的地址域和离散输入的 个数。离散输入的地址从0开始,因此,离散输入感应器1-16的地址为0-15。
响应信息中的离散输入被按字节打包,数据字节的每一位都 有一个离散输入与之相对应。状态1=ON,0=OFF。起始地址的内容被放入数据第一字节的最低有效位 ,依次类推。
如果返回输出的位数不是8的倍数,剩余高位用0补。 计数字节详细记录了完整数据字节的数量。
请求协议数据单元
功能代码 | 1字节 | 0x02 |
起始地址 | 2字节 | 0x0000到0xFFFF |
输入量 | 2字节 | 1到2000(0x7D0) |
响应协议数据单元
功能代码 | 1字节 | 0x02 |
感应器状态 | N*X1字节 |
N*=输出量/8,如果剩余量不为0,则N=N+1
下面举一个例子是关于请求读离散输入(197-218):
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 起始地址高位 起始地址低位 输入位数的高位 输入位数的低位 | 02 | 功能 | 02 |
00 | 03 | ||
C4 | AC | ||
00 | DB | ||
16 | 35 |
从十六进制数AC或二进制数1010 1100中可以读出离散输入 204-197的状态,输入位204是这个字节最高位,197是这个字节的最低位。
从十六进制数35或二进制数0011 0101中可以读出离散输入 204-197的状态,输入位218是字节从左边数的第三位,输入位213是这个字节的最低位
注意:剩余的两位(高位)用0
01功能代码是MODBUS用来读取从机离散输出的10001~165535 状态。请求协议数据单元详细说明了离散输出的起始地址和个数。离散输出的地址从0开始,0x0000 对应MODBUS地址10001,0x0001对应地址10002,依次类推。
响应信息中的离散输出被按字节打包,数据字节的每一位都 有一个离散输出与之相对应。状态1=ON,0=OFF。起始地址的内容被放入数据第一字节的最低有效位 ,依次类推。
如果返回输出的位数不是8的倍数,剩余高位用0补。计数字 节详细记录了完整数据字节的数量。
请求协议数据单元
功能代码 | 1字节 | 0x01 |
起始地址 | 2字节 | 0x0000到0xFFFF |
感应器数量 | 2字节 | 1到2000(0x7D0) |
响应协议数据单元
功能代码 | 1字节 | 0x01 |
字节数 | n字节 | n=N或者N+1 |
N*=输出量/8,如果剩余量不为0,则n=N*+1
下面举一个例子是关于请求读离散输出(20-38):
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 起始地址高位 起始地址低位 输出位数的高位 输出位数的低位 | 01 | 功能 字节数 输出状态27-20 输出状态35-28 输出状态38-36 | 01 |
00 | 03 | ||
13 | CD | ||
00 | 6B | ||
13 | 05 |
十六进制数CD或二进制数1100 1101包含了输 出位27的状态“1”。输出位27是这个字节的最高位,输出位20为这个字节的最低位。
在通常的情况下,一个字节的左边是最高有效位,右边是最 低有效位。这样输出的第一个字节从左边到右边依次是位27到20,下一个字节从左到右依次是位35到 28。当传输这些输出状态时,从最低有效位到最高有效位:20…27,28…35依次类推。
在最后一个字节,十六进制数05或二进制数0000 0101 是输出位38-36的状态。输出位38是从左边开始数的第六位,输出位36是这个字节的最低位。剩余的 五个高位用0补。
注:北京捷麦PLC离散输出变量有256个,除了实际 硬件对应的之外,其它的都可以做中间继电器使用。
在一个MODBUS从机上,使用该功能码写单个输出为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 |
这是一个请求写线圈173 为ON 的实例:
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 输出地址Hi 输出地址Lo 输出值Hi 输出值Lo | 05 | 功能 输出地址Hi 输出地址Lo 输出值Hi 输出值Lo | 05 |
00 | 00 | ||
AC | AC | ||
FF | FF | ||
00 | 00 |
这个功能代码是MODBUS从机用来确定离散输出序列中的离散 输出的ON或OFF状态。请求协议数据单元详细说明了离散输出的起始地址和输出数量。离散输出的地 址是从0开始的,因此离散输出1的地址为0。
在请求协议数据单元详细说明了请求状态ON或OFF。这个区域 的相应位为逻辑“1”时,对应的输出为ON,逻辑“0”对应OFF。
正常的响应返回功能代码,起始地址和对应离散输出的数量 。
请求协议数据单元
功能代码 | 1字节 | 0FH |
起始地址 | 2字节 | 0000H到007FH |
输出数量 | 2字节 | 0001H到0080H |
输出值 | N*×1字节 |
N*=输出量/8, 如果剩余量不为0,则N=N+1
响应协议数据单元
功能代码 | 1字节 | 0FH |
起始地址 | 2字节 | 0000H到007FH |
输出数量 | 2字节 | 0001H到0080H |
下面举一个例子,写一连串的10个离散输出,从离散输出20 开始
被传输的数据的第一字节CD是存储器码为27-20的状态 ,最低位表达的是离散输出20的状态,下一字节存储器码为29-28的状态,最低位表达的是离散输出 28的状态。在最后一字节没有用到的位用0补。
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 起始地址Hi 起始地址Lo 输出数量Hi 输出数量Lo 字节数 输出值Hi 输出值Lo | 0F | 功功能 起始地址Hi 起始地址Lo 输出数量Hi 输出数量Lo | 0F |
00 | 00 | ||
13 | 13 | ||
00 | 00 | ||
0A | 0A | ||
02 | |||
CD | |||
01 |
这个功能代码是用来读MODBUS从机相邻的整数(16bit)输入 存储区单元。请求协议数据单元详细说明了存储区的起始地址和读出数量。每个存储区单元为两个字 节。正常的响应会返回功能代码,读出的存储区单元的数量及内容。
整数输入存储区的开始偏移地址:0 (0000H)
请求协议数据单元
功能代码 | 1字节 | 04H |
起始地址 | 2字节 | 0000H到2710H |
寄存器个数(*N) | 2字节 | 0001H-007DH |
*N=输入整数存储区单元的数量
响应协议数据单元
功能代码 | 1字节 | 04H |
字节数 | 1 个字节 | *N ×2 |
寄存器值 | *N ×2个字节 | 值 |
*N=输入整数存储区单元的数量
注意:由于每个整数存储区单元占用两个字节,而MODBUS读输入寄存器也是两个字节,因此 ,每一个偏移地址正好一一对应着每个整数存储区单元,由于整数存储区的开始偏移地址是从0000H 开始的,因此偏移地址00就是整数存储区单元1,偏移地址N就是整数存储区单元N+1。
下例是从整数输入存储区单元2开始读两个单元,读出的内容 为00 0A和01 02。
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 | 04 | 功能 | 04 |
起始地址高位 | 00 | 字节数 | 04 |
起始地址低位 | 01 | 单元2高字节 | 00 |
寄存器个数高位 | 00 | 单元2低字节 | 0A |
寄存器个数低位 | 02 | 单元3高字节 | 01 |
单元3低字节 | 02 |
这个功能代码是用来读MODBUS从机相邻的整数(16bit)输出 存储区单元。请求协议数据单元详细说明了存储区的起始地址和读出数量。每个存储区单元为两个字 节。正常的响应会返回功能代码,读出的存储区单元的数量及内容。
整数输出存储区的开始偏移地址:0 (0000H)
请求协议数据单元
功能代码 | 1字节 | 03H |
起始地址 | 2字节 | 0000H到2710H |
寄存器个数(*N) | 2字节 | 0001H-007DH |
*N=输出整数存储区单元的数量
响应协议数据单元
功能代码 | 1字节 | 03H |
字节数 | 1 个字节 | *N ×2 |
寄存器值 | *N ×2个字节 | 值 |
*N=输出整数存储区单元的数量
注意:由于每个整数存储区单元占用两个字节,而MODBUS读输出寄存器也是两个字节,因此 ,每一个偏移地址正好一一对应着每个整数存储区单元,由于整数存储区的开始偏移地址是从0000H 开始的,因此偏移地址00就是整数存储区单元1,偏移地址N就是整数存储区单元N+1。
下例是从整数输出存储区单元2开始读两个单元,读出的内容 为00 0A和01 02。
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 | 03 | 功能 | 03 |
起始地址高位 | 00 | 字节数 | 04 |
起始地址低位 | 01 | 单元2高字节 | 00 |
寄存器个数高位 | 00 | 单元2低字节 | 0A |
寄存器个数低位 | 02 | 单元3高字节 | 01 |
单元3低字节 | 02 |
这个功能代码是用来写MODBUS从机单个的整数(bit16)输出 存储区单元。请求协议数据单元说明了被写入寄存器的地址。每个寄存器为两个字节。正常的响应会 返回功能代码,起始地址和存储区单元的内容。
整数输出存储区的开始偏移地址:0 (0000H)
请求协议数据单元
功能代码 | 1字节 | 06H |
寄存器地址 | 2字节 | 0000H到2710H |
寄存器的值 | 2字节 | 0x0000 到0xFFFF |
响应协议数据单元
功能代码 | 1字节 | 06H |
寄存器地址 | 2字节 | 0000H到2710H |
寄存器的值 | 2字节 | 0x0000 到0xFFFF |
注意:由于每个整数存储区单元占用两个字节,而MODBUS读输出寄存器也是两个字节,因此 ,每一个偏移地址正好一一对应着每个整数存储区单元,由于整数存储区的开始偏移地址是从0000H 开始的,因此偏移地址00就是整数存储区单元1,偏移地址N就是整数存储区单元N+1。
下例是从将内容为00 03的数据写入整数输出存储区单元2中 。
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 | 06 | 功能 | 06 |
起始地址高位 | 00 | 起始地址高位 | 00 |
起始地址低位 | 01 | 起始地址低位 | 01 |
寄存器值高位 | 00 | 寄存器值高位 | 00 |
寄存器值低位 | 03 | 寄存器值低位 | 03 |
这个功能代码是用来写MODBUS从机相邻的整数(16bit)输出 存储区单元。请求协议数据单元详细说明了寄存器的起始地址、数量和数据内容。每个存储区单元为 两个字节。正常的响应会返回功能代码,起始地址和读出的存储区单元内容。
整数输出存储区的开始偏移地址:0 (0000H)
请求协议数据单元
功能代码 | 1字节 | 10H |
起始地址 | 2字节 | 0000H到2710H |
寄存器个数 | 2字节 | 0001H-007DH |
字节数 | 1字节 | *N ×2 |
数据 | *N ×2 | 值 |
*N=输出整数寄存器的数量
响应协议数据单元
功能代码 | 1字节 | 10H |
起始地址 | 2字节 | 0000H到2710H |
寄存器个数 | 2字节 | 0001H-007DH |
注意:由于每个整数存储区单元占用两个字节,而MODBUS读输出寄存器也是两个字节,因此 ,每一个偏移地址正好一一对应着每个整数存储区单元,由于整数存储区的开始偏移地址是从0000H 开始的,因此偏移地址00就是整数存储区单元1,偏移地址N就是整数存储区单元N+1。
下例是将内容为00 0A和01 02写从整数输出存储区单元2开始 的存储区中:
请求 | 响应 | ||
字段名称 | (Hex) | 字段名称 | (Hex) |
功能 | 10 | 功能 | 10 |
起始地址高位 | 00 | 起始地址高位 | 00 |
起始地址低位 | 01 | 起始地址低位 | 01 |
寄存器个数高位 | 00 | 寄存器个数高位 | 00 |
寄存器个数低位 | 02 | 寄存器个数低位 | 02 |
字节数 | 04 | ||
数据值VOD2高字节 | 00 0A 01 02 | ||
数据值VOD2低字节 | |||
数据值VOD3高字节 | |||
数据值VOD3低字节 |