第一部分 Modbus
1.Modbus相关术语
ADU(Application Data Unit) 应用数据单元
PDU(Protocol Data Unit) 协议数据单元
LSB(Least Significant Bit) 最低有效位
MSB(Most Significant Bit) 最高有效位
MB(Modbus Protocol) Modbus协议
2.Modbus协议简介
Modbus是一个请求/应答协议,并且提供功能码规定的服务。Modbus功能码是Modbus请求/应答PDU的元素。【PDU表示协议数据单元】
Modbus是应用层报文传输协议,它在通过不同类型的总线或网络连接的设备之间的客户机/服务器通信。
目前,通过下列方式实现Modbus通信:
——以太网上的TCP/IP
——各种介质(有线;EIA/TIA-232-E、EIA-422、EIA/TIA-485-A;光纤、无线等等)上的异步串行传输。【我们要使用和介绍的就是这种方式】
——Modbus+,一种高速令牌传输网络
Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网络上的Modbus协议映射能够在应用数据单元(ADU)上引用一些附加域。
启动Modbus事务处理的客户机创建Modbus应用数据单元。功能码向服务器指示将执行哪种操作。Modbus协议建立了客户机启动的请求格式。
用一个字节编码Modbus数据单元的功能码域。有效的码字范围是十进制1~255(128~255为异常响应保留)。当从客户机向服务器设备发送报文时,功能码域通知服务器执行哪种操作。
从客户机向服务器设备发送的报文数据域包括附加信息,服务器使用这个信息执行功能码定义的操作。这个域还包括离散量和寄存器地址、处理的项目数量以及字段中的实际数据字节数。
在某种请求中,数据字段可以是不存在的(0长度),在此情况下服务器不需要任何附加信息。功能码仅说明操作。
如果在一个正确接收的Modbus ADU中,不出现与所请求的Modbus功能有关的差错,那么服务器至客户机的响应数据字段包含所请求的数据。如果出现与所请求的Modbus功能有关的差错,那么该域包含一个异常码,服务器应用能够使用这个字段确定下一个执行的操作。
当服务器对客户机响应时,它使用功能码字段来指示正常(无差错)响应(见图2)或者出现某种差错(称为异常响应),见图3。对于一个正常响应来说,服务器仅复制原始功能码。
3.数据编码
Modbus使用最高有效字节在低地址存储的方式表示地址和数据项。这意味着当发送多个字节时,首先发送最高有效字节。例如:
寄存器大小值
16位0x1234 发送的第一字节为:0x12 然后是0x34
4.Modbus 数据模型
Modbus 的数据模型是以一组具有不同特征的表为基础建立的。4个基本表见表1:
基本表 |
对象类型 |
访问类型 |
注释 |
离散量输入 |
单个位 |
只读 |
I/O系统可提供这种类型数据 |
线圈 |
单个位 |
读写 |
通过应用程序可改变这种类型数据 |
输入寄存器 |
16位字 |
只读 |
I/O系统可提供这种类型数据 |
保持寄存器 |
16位字 |
读写 |
通过应用程序可改变这种类型数据 |
输入与输出之间以及位寻址的和字寻址的数据项之间的区别并不意味着应用特性的差别。如果所有4个表相互覆盖是对该目标机器最自然的解释,也是完全可接收的,而且很普遍。
对于每个基本表,协议都允许单个的选择65536个数据项,而且其读写操作被设计为可以越过多个连续数据项直到数据大小规格限制,这个数据大小规格限制与事务处理功能码有关。
很显然,必须将Modbus处理的所有数据(位,寄存器)放置在设备应用寄存器中,但是,存储器的物理地址不应该与数据引用混淆。仅要求将数据引用与物理地址链接。
Modbus功能码中使用的Modbus寄存器逻辑编号是以0开始的无符号整数。
——Modbus模型实现的实例
下列实例表示了两种在设备中组织数据的方法。有多种组织数据的方法,在本部分中没有被全部描述。每个设备根据其应用都有它自己的组织数据的方法。
实例1:有4个独立块的设备
图4表示了含有数字量和模拟量、输入量和输出量的设备中的数据组织。由于不同
块中的数据不相关,每个块是独立的。可通过不同Modbus功能码访问每个块。
5.公共功能码定义
5.1.01(0x01)读线圈
见表2~表4。
使用该功能码从一个远程设备中读1~2000个连续的线圈状态。请求PDU指定了起始地址,即指定了第一个线圈地址和线圈数目。在PDU中,从17(十六进制11)返回从站忙计数。
响应报文中的线圈按数据域的每位一个线圈进行打包。状态被表示成1=ON和0=OFF。第一个数据字节的LSB(最低有效位)包含询问中所寻址的输出。其他线圈依次类推,一直到这个字节的高位端为止,并在后续字节中按照从低位到高位的顺序排列。
如果返回的输出数量不是8的倍数,将用零填充最后数据字节中的剩余位(一直到字节的最高位端)。字节数量域说明了数据的全部字节数。
表2
请求
功能码 |
1字节 |
0x01 |
起始地址 |
2字节 |
0x0000~0xFFFF |
线圈数量 |
2字节 |
1~2000(0x07D0) |
表3
响应
功能码 |
1字节 |
0x01 |
字节计数 |
1字节 |
N* |
线圈状态 |
n字节 |
n=N或N+1 |
*N=输出数量/8,如果余数不等于0,那么N=N+1。 |
表4
错误
功能码 |
1字节 |
功能码+0x80 |
异常码 |
1字节 |
01或02或03或04 |
5.2.02(0x02)读离散量输入
见表5~表7。
使用该功能从一个远程设备中读1~2000个连续的离散量输入状态。请求PDU指定了起始地址,即指定了第一个离散量输入地址和离散量输入数目。在PDU中,从零开始寻址离散量输入。因此标号1~16的离散量输入寻址为0~15。
响应报文中的离散量输入按数据域的每位一个离散量输入进行打包。状态被表示成1=ON和0=OFF。第一个数据字节的LSB(最低有效位)包含讯问中所寻址的输入。其他离散量输入依次类推,一直到这个字节的高位端为止,并在后续字节中按照从低位到高位的顺序排列。
如果返回的输出数量不是8的倍数,将用零填充最后数据字节中的剩余位(一直到字节的最高位端)。字节数量域说明了数据的全部字节数。
表5
请求
功能码 |
1字节 |
0x02 |
起始地址 |
2字节 |
0x0000~0xFFFF |
线圈数量 |
2字节 |
1~2000(0x07D0) |
表6
响应
功能码 |
1字节 |
0x02 |
字节计数 |
1字节 |
N* |
线圈状态 |
N*×1个字节 |
… |
*N=输出数量/8,如果余数不等于0,那么N=N+1 |
表7
错误
差错码 |
1字节 |
0x82 |
异常码 |
1字节 |
01或02或03或04 |
5.3.03(0x03)读保持寄存器
见表8~表10。
使用该功能码从远程设备中读保持寄存器连续块的内容。请求PDU说明了起始寄存器地址和寄存器数量。在PDU中,从零开始寻址寄存器。因此编号1~16的寄存器寻址为0~15。
将响应报文中的寄存器数据打包成每个寄存器有两字节。
对于每个寄存器,第一个字节为高位字节,第二个字节为低位字节。
表8
请求
功能码 |
1字节 |
0x03 |
起始地址 |
2字节 |
0x0000~0xFFFF |
寄存器数量 |
2字节 |
1~125(0x007D) |
表9
响应
功能码 |
1字节 |
0x03 |
字节数 |
1字节 |
2×N* |
寄存器值 |
N*×2个字节 |
… |
*N=寄存器的数量 |
表10
错误
差错码 |
1字节 |
0x83 |
异常码 |
1字节 |
01或02或03或04 |
5.4.04(0x04)读输入寄存器
见表11~表13。
使用该功能码从一个远程设备中读1~125个连续输入寄存器。请求PDU说明了起始地址和寄存器数量。在PDU中,从零开始寻址寄存器。因此,标号为1~16的输入寄存器寻址为0~15。
将响应报文中的寄存器数据打包成每个寄存器为两字节。
对于每个寄存器,第一个字节包括高位位,第二个字节包括低位位。
表11
请求
功能码 |
1字节 |
0x04 |
起始地址 |
2字节 |
0x0000~0xFFFF |
输入寄存器数量 |
2字节 |
0x0001~0x007D |
表12
响应
功能码 |
1字节 |
0x04 |
字节计数 |
1字节 |
2×N* |
输入寄存器 |
N*×2个字节 |
… |
*N=输入寄存器的数量。 |
表13
错误
差错码 |
1字节 |
0x84 |
异常码 |
1字节 |
01或02或03或04 |
5.5.05(0x05)写单个线圈
见表14~表16。
使用该功能码将一个远程设备中的一个输出写ON或OFF。
请求数据域中的常数指定所请求的ON/OFF状态。十六进制值FF00请求输出为ON。十六进制值0000请求输出为OFF。其他所有值均是非法的,并且对输出不起作用。
请求PDU说明了被强制的线圈地址。从零开始寻址线圈。因此,编号为1的线圈被寻址为0。线圈值域的常数指定所请求的ON/OFF状态。十六进制值0xFF00请求线圈为ON。十六进制值0x0000请求线圈为OFF。其他所有值均为非法的,并且对线圈不起作用。
正常的响应是请求的复制,在写入线圈状态后被返回。
表14
请求
功能码 |
1字节 |
0x05 |
输出地址 |
2字节 |
0x0000~0xFFFF |
输出值 |
2字节 |
0x0000或0xFF00 |
表15
响应
功能码 |
1字节 |
0x05 |
输出地址 |
2字节 |
0x0000~0xFFFF |
输出值 |
2字节 |
0x0000或0xFF00 |
表16
错误
差错码 |
1字节 |
0x85 |
异常码 |
1字节 |
01或02或03或04 |
5.6.06(0x06)写单个寄存器
见表17~19。
使用该功能码在一个远程设备中写一个保持寄存器。
请求PDU指定了被写入寄存器的地址。从零开始寻址寄存器。因此,编号为1的寄存器被寻址为0。
正常的响应是请求的复制,在写入寄存器内容之后被返回。
表17
请求
功能码 |
1字节 |
0x06 |
寄存器地址 |
2字节 |
0x0000~0xFFFF |
寄存器值 |
2字节 |
0x0000~0xFFFF |
表18
响应
功能码 |
1字节 |
0x06 |
寄存器地址 |
2字节 |
0x0000~0xFFFF |
寄存器值 |
2字节 |
0x0000~0xFFFF |
表19
错误
差错码 |
1字节 |
0x86 |
异常码 |
1字节 |
01或02或03或04 |
5.7.07(0x07)读异常状态
见表20~22。
使用这个功能码从一个远程设备中读8个异常状态输出的内容。
因为异常码输出参量是已知的(在这个功能中不需要输出参量),该功能提供一种简单的访问这种信息的方法。
正常的响应包括8个异常状态输出的内容。将这些输出打包成一个字节,每个输出一个位。在该字节的最低有效位中包含最低位输出参量的状态。
表20
请求
表21
响应
功能码 |
1字节 |
0x07 |
输出数据 |
1字节 |
0x00~0xFF |
表22
错误
差错码 |
1字节 |
0x87 |
异常码 |
1字节 |
01或04 |
5.8.08(0x08)诊断
公司产品通讯协议中不使用该功能码,不进行描述。
5.9.11(0x0B)获得通信事件计数器
公司产品通讯协议中不使用该功能码,不进行描述。
5.10.12(0x0C)获得通信事件记录
公司产品通讯协议中不使用该功能码,不进行描述。
5.11.15(0x0F)写多个线圈
见表23~25。
该功能码将一个远程设备中的一个线圈序列的每个线圈强制为ON或OFF。请求PDU指定了被强制的线圈编号。从零开始寻址线圈。因此,编号为1的线圈被寻址为0。
请求数据域的内容指定了被请求的ON/OFF状态。数据域中为逻辑“1”的位请求相应输出为ON。为逻辑“0”的位请求相应输出为OFF。
正常的响应返回功能码、起始地址和被强制的线圈数量。
表23
请求
功能码 |
1字节 |
0x0F |
起始地址 |
2字节 |
0x0000~0xFFFF |
输出数量 |
2字节 |
0x0001~0x07B0 |
字节计数 |
1字节 |
N* |
输出值 |
N*×1字节 |
… |
*N=输出数量/8,如果余数不等于0,那么,N=N+1。 |
表24
响应
功能码 |
1字节 |
0x0F |
起始地址 |
2字节 |
0x0000~0xFFFF |
输出数量 |
2字节 |
0x0001~0x07B0 |
表25
错误
差错码 |
1字节 |
0x8F |
异常码 |
1字节 |
01或02或03或04 |
5.12.16(0x10)写多个寄存器
见表26~28。
使用该功能码在一个远程设备中写连续寄存器块(1~123个寄存器)。
在请求数据域中指定了请求写入的值。将数据打包成每个寄存器两字节。
正常的响应返回功能码、起始地址和被写入寄存器的数量。
表26
请求
功能码 |
1字节 |
0x10 |
起始地址 |
2字节 |
0x0000~0xFFFF |
寄存器数量 |
2字节 |
0x0001~0x007B |
字节计数 |
1字节 |
2×N* |
寄存器值 |
N*×2字节 |
值 |
*N=寄存器数量 |
表27
响应
功能码 |
1字节 |
0x10 |
起始地址 |
2字节 |
0x0000~0xFFFF |
寄存器数量 |
2字节 |
1~123(0x007B) |
表28
错误
差错码 |
1字节 |
0x90 |
异常码 |
1字节 |
01或02或03或04 |
5.13.17(0x11)报告从站ID
公司产品通讯协议中不使用该功能码,不进行描述。
5.14.20/6(0x14/0x06)读文件记录
公司产品通讯协议中不使用该功能码,不进行描述。
5.15.21/6(0x15/0x06)写文件记录
公司产品通讯协议中不使用该功能码,不进行描述。
5.16.22(0x16)屏蔽写寄存器
公司产品通讯协议中不使用该功能码,不进行描述。
5.17.23(0x17)读/写多个寄存器
公司产品通讯协议中不使用该功能码,不进行描述。
5.18.24(0x18)读FIFO队列
公司产品通讯协议中不使用该功能码,不进行描述。
5.19.43(0x2B)封装接口传输
公司产品通讯协议中不使用该功能码,不进行描述。
5.20.43/14(0x2B/0x0E)读设备标识
见表29~32。
这个功能码允许读取与远程设备的物理和功能描述相关的标识和附加信息。
读设备识别接口由包含一组可寻址数据元素组成的地址空间构成。数据元素被称作对象,由对象Id识别它们。
接口由3类对象组成:
——基本设备识别。所有此类对象都是必备的:厂商名称、产品代码和修订版本号。
——常规设备识别。除基本数据对象以外,设备提供了附加的和可选择的识别以及数据
对象描述。本标准定义了所有类别的对象,但是它们的实现是可选的。
——扩展设备识别。除常规数据对象以外,设备提供了附加的和可选的识别以及专用数
据描述。所有这些数据都是与设备有关的。
表29
对象Id |
对象名称/描述 |
类型 |
M/O |
类别 |
0x00 |
厂商名称 |
ASCII字符串 |
强制的 |
基本 |
0x01 |
产品代码 |
ASCII字符串 |
强制的 |
基本 |
0x02 |
主要修订本 |
ASCII字符串 |
强制的 |
基本 |
0x03 |
厂商网址 |
ASCII字符串 |
可选的 |
常规 |
0x04 |
产品名称 |
ASCII字符串 |
可选的 |
常规 |
0x05 |
模式名称 |
ASCII字符串 |
可选的 |
常规 |
0x06 |
用户应用名称 |
ASCII字符串 |
可选的 |
常规 |
0x07
…
0x7F |
保留 |
— |
可选的 |
常规 |
0x80
…
0xFF |
可选择的定义专用对象
范围(0x80~0xFF)与产品有关 |
ASCII字符串 |
可选的 |
扩展 |
表30
请求
功能码 |
1字节 |
0x2B |
MEI类型 |
1字节 |
0x0E |
ReadDevId码 |
1字节 |
01/02/03/04 |
对象Id |
1字节 |
0x00~0xFF |
表31
响应
功能码 |
1字节 |
0x2B |
MEI类型 |
1字节 |
0x0E |
ReadDevId码 |
1字节 |
01/02/03/04 |
一致性等级 |
1字节 |
… |
接续标识 |
1字节 |
0x00/0xFF |
下一个对象Id |
1字节 |
对象ID号 |
对象数量 |
1字节 |
… |
列表 |
|
|
对象ID |
1字节 |
… |
对象长度 |
1字节 |
… |
对象值 |
对象长度 |
与对象ID有关 |
表32
错误
功能码 |
1字节 |
0xAB:Fc 0x2B+0x80 |
MEI类型 |
1字节 |
0x0E |
异常码 |
1字节 |
01或02或03或04 |
第二部分 Modbus协议在电力仪表中的应用
Modbus协议定义了两种串行传输模式:RTU模式和ASCII模式。这两种模式定义了串行链路上传送报文域的位内容,确定了信息如何打包为报文域和如何解码。
Modbus要求每个设备必须实现RTU模式,ASCII模式作为可选模式。我公司目前主要采用Modbus RTU模式。暂时不考虑使用Modbus ASCII模式。
2.1. RTU传输模式
在Modbus串行链路上通信时,报文中每个8位字节含有两个4位十六进制字符。
RTU模式中每个字节(10位)的格式为:
编码系统: 每个8位字节含有两个4位十六进制字符(0~9、A~F)
每个字节的位: 1个起始位
8个数据位
无校验位
1个停止位
注:帧校验域: 循环冗余校验(CRC)。
通讯波特率:可选范围为2400bps、4800bps、9600bps、19200bps。
2.2. Modbus报文RTU帧
在RTU模式中,时长至少为3.5个字符的间隔时间将报文帧区分开。这个时间称为
t3.5。
图7表示了一个典型的报文帧。
起始 |
地址 |
功能码 |
数据 |
CRC |
结束 |
≥3.5字符 |
8位 |
8位 |
N×8位 |
16位 |
≥3.5字符 |
图7
必须以连续的字符流发送整个报文帧。
如果字符之间的空闲间隔大于1.5个字符时间,那么认为报文帧不完整,并且接收站应该丢弃这个报文帧。
字符间间隔
所谓1个字符的时间t,是指发送完每个10位所用的时间,计算方法为“1÷波特率×10”。但是,实现RTU的定时会对系统带来大量中断管理。在较高的通讯波特率下,这将导致CPU负担加重。因此,当波特率等于或低于19200 bit/s时,必须严格遵守这两个定时;波特率大于19200 bit/s时,两个定时器应该使用固定值:建议字符间隔超时时间(t1.5)为750μs,帧间的超时时间(t3.5)为1.750ms。
2.3. CRC校验
RTU模式包含一个差错校验域,该域是基于冗余循环校验(CRC)方法对全部报文内容执行的。
CRC域校验整个报文的内容。无论单个字符报文使用何种奇偶校验,均应用这种CRC校验。
CRC包括两个8位字节组成的一个16位值。
CRC域作为报文的最后域附加到报文上。当进行这种附加时,首先附加域的低位字节,然后附加域的高位字节。CRC高位字节是报文中发送的最后字节。
2.4. Mosbus功能码简介
仪表中使用了Modbus中的部分功能码,分别是:
功能码 |
Modbus中定义 |
仪表中定义 |
操作说明 |
0x02 |
读离散量输入 |
读开关量输入DI |
读取一路或多路开关量状态输入数(遥信) |
0x 01 |
读线圈 |
读状态量输出OUT |
读取一路或多路开关量输出状态数据 |
0x 03 |
读多个保持寄存器 |
读寄存器数据 |
读取一个或多个寄存器的数据 |
0x 05 |
写单个线圈 |
写开关量输出OUT |
控制一路继电器“合/分”输出(遥控) |
0x 06 |
写单个保持寄存器 |
写单路寄存器 |
把一组数据写入单个寄存器 |
0x 10 |
写多个保持寄存器 |
写多路寄存器 |
把多组数据写入多个寄存器 |
2.4.1. 功能码“0x02”
功能码0x02的意思是:读1路或多路开关量输入状态DI
例如:主机要读取仪表地址为0x01的开关量(DI1—DI4)的输入状态。
从机(仪表)中保存开关量输入状态的寄存器地址和数据为:
起始DI地址 |
DI寄存器数据 |
备注 |
0x0000 |
0x0B |
DI1/DI2/DI4的状态为“1”,DI3状态为“0” |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
02 |
读开关量输入状态 |
起始BIT位 |
2 |
0000 |
起始BIT位的地址为0000 |
读数据长度 |
2 |
0004 |
读取4路开关量输入状态位 |
CRC码 |
2 |
79C9 |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
从机响应 |
字节数 |
返回的信息 |
备注 |
从机地址 |
1 |
01 |
来自地址为01的从机 |
功能码 |
1 |
02 |
读开关量输入状态 |
数据长度 |
1 |
01 |
1个字节(8个bit位) |
DI状态数据 |
1 |
0B |
DI寄存器内容(bit0对应DI1)
DI1/DI2/DI4状态为“1”,DI3状态为“0” |
CRC码 |
2 |
E04F |
由仪表计算得到CRC码 |
2.4.2. 功能码“0x01”
功能码0x01的意思是:读1路或多路开关量输出状态
例如:主机要读取仪表地址为0x01的输出状态量(OUT1、OUT2)的输出状态。
从机(仪表)数据寄存器的地址和数据为:
起始位地址 |
DO寄存器数据 |
备注 |
0x0000 |
0x02 |
DO2输出状态为“1”,DO1输出状态为“0” |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
01 |
读开关量输出状态 |
起始BIT位 |
2 |
0000 |
起始BIT位的地址为0000 |
读数据长度 |
2 |
0002 |
读取2路继电器输出状态位 |
CRC码 |
2 |
BDCB |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
从机响应 |
字节数 |
返回的信息 |
备注 |
从机地址 |
1 |
01 |
来自地址为01的从机 |
功能码 |
1 |
01 |
读开关量输出状态 |
数据长度 |
1 |
01 |
1个字节(8个bit位) |
OUT状态数据 |
1 |
0B |
OUT寄存器内容(bit0对应OUT1)
DO2输出状态为“1”,DO1输出状态为“0” |
CRC码 |
2 |
D049 |
由仪表计算得到CRC码 |
2.4.3. 功能码“0x03”
功能码0x03的意思是:读多路寄存器输入
例如:主机要读取仪表地址为0x01,起始地址为0x017A的3个从机寄存器数据。
从机(仪表)数据寄存器的地址和数据为:
寄存器地址 |
寄存器数据 |
对应仪表电量值 |
0x017A |
0x1784 |
UA A相电压 |
0x017B |
0x1780 |
UA B相电压 |
0x017C |
0x178A |
UA C相电压 |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
03 |
读取寄存器 |
起始地址 |
2 |
017A |
起始寄存器地址为017A |
数据长度 |
2 |
0003 |
读取3个寄存器(共6个字节) |
CRC码 |
2 |
25EE |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
从机响应 |
字节数 |
返回的信息 |
备注 |
从机地址 |
1 |
01 |
来自地址为01的从机 |
功能码 |
1 |
03 |
读取寄存器 |
返回字节数 |
1 |
06 |
3个寄存器共6个字节 |
寄存器数据1 |
2 |
1784 |
寄存器地址为017A的内容 |
寄存器数据2 |
2 |
1780 |
寄存器地址为017B的内容 |
寄存器数据3 |
2 |
178A |
寄存器地址为017C的内容 |
CRC码 |
2 |
5847 |
由仪表计算得到CRC码 |
2.4.4.功能码“0x04”
功能码0x04的意思是:读多路寄存器输入
例如:主机要读取仪表地址为0x01,起始地址为0x017A的3个从机寄存器数据。
从机(仪表)数据寄存器的地址和数据为:
寄存器地址 |
寄存器数据 |
对应仪表电量值 |
0x017A |
0x1784 |
UA A相电压 |
0x017B |
0x1780 |
UA B相电压 |
0x017C |
0x178A |
UA C相电压 |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
04 |
读取寄存器 |
起始地址 |
2 |
017A |
起始寄存器地址为017A |
数据长度 |
2 |
0003 |
读取3个寄存器(共6个字节) |
CRC码 |
2 |
902E |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
从机响应 |
字节数 |
返回的信息 |
备注 |
从机地址 |
1 |
01 |
来自地址为01的从机 |
功能码 |
1 |
04 |
读取寄存器 |
返回字节数 |
1 |
06 |
3个寄存器共6个字节 |
寄存器数据1 |
2 |
1784 |
寄存器地址为017A的内容 |
寄存器数据2 |
2 |
1780 |
寄存器地址为017B的内容 |
寄存器数据3 |
2 |
178A |
寄存器地址为017C的内容 |
CRC码 |
2 |
19A1 |
由仪表计算得到CRC码 |
2.4.5. 功能码“0x05”
功能码“0x05”的意思是:写1路开关量输出(遥控)
例1:开关量输出点OUT1,其当前状态为“分”,主机要控制该路继电器“合”。
控制命令为:
“0xFF00”为控制继电器“合”;
“0x0000”为控制继电器“分”;
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
05 |
写开关量输出状态 |
输出BIT位 |
2 |
0000 |
对应输出继电器BIT0位(OUT1) |
控制命令 |
2 |
FF00 |
控制该路继电器输出为“合”状态位 |
CRC码 |
2 |
8C3A |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
与主机发送的报文格式及数据内容完全相同。
例2:开关量输出点OUT2,其当前状态为“合”,主机要控制该路继电器“分”。
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
05 |
写开关量输出状态 |
输出BIT位 |
2 |
0001 |
对应输出继电器BIT1位(OUT2) |
控制命令 |
2 |
0000 |
控制该路继电器输出为“分”状态位 |
CRC码 |
2 |
8C3A |
由主机计算得到CRC码 |
从机(仪表)响应返回的报文格式:
与主机发送的报文格式及数据内容完全相同。
2.4.6. 功能码“0x06”
功能码“0x06”的意思是:写单路寄存器
例如:主机要把数据0x07D0保存到地址为0x002C的从机寄存器中去(从机地址为0x01)。通讯数据保存结束后,寄存器地址0x002C的仪表原存储信息为:
寄存器地址 |
原来存储数据 |
0x002C |
0x04B0 |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
06 |
写单路寄存器 |
起始地址 |
2 |
002C |
要写入的寄存器地址 |
写入数据 |
2 |
07D0 |
对应的新数据 |
CRC码 |
2 |
4BAF |
由主机计算得到CRC码 |
从机(仪表)相应返回的报文格式:
与主机发送的报文格式及数据内容完全相同
2.4.7. 功能码“0x10”
功能码0x10的意思是:写多路寄存器
主机利用这个功能码把多个数据保存到仪表的数据存储器中去。Modbus通讯协议中的寄存器指的是16位(2字节),并且高位在前。这样仪表的存储器都是2个字节。由于Modbus通讯协议允许每次最多保存60个寄存器,因此仪表一次最多允许保存60个数据寄存器。
例如:主机要把0x0064,0x0010保存到地址为0x002C,0x002D的从机寄存器中去(从机地址为0x01)。通讯数据保存结束后,地址为0x002C/0x002D的仪表内存储信息为:
地址 |
原来存储的数据 |
通讯结束后存储的数据 |
0x002C |
0x04B0 |
0x0064 |
0x002D |
0x1388 |
0x0010 |
主机发送的报文格式:
主机发送 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
发送至地址为01的从机 |
功能码 |
1 |
10 |
写多路寄存器 |
起始地址 |
2 |
002C |
要写入的寄存器起始地址 |
保存数据字长度 |
2 |
0002 |
保存数据的字长度(共2字) |
保存数据字节长 |
1 |
04 |
保存数据的字节长度(共4字节) |
保存数据1 |
2 |
04B0 |
数据地址为0x002C |
保存数据2 |
2 |
1388 |
数据地址为0x002D |
CRC码 |
2 |
FC63 |
由主机计算得到CRC码 |
从机(仪表)相应返回的报文格式:
主机相应 |
字节数 |
发送的信息 |
备注 |
从机地址 |
1 |
01 |
来自地址为01的从机 |
功能码 |
1 |
10 |
写多路寄存器 |
起始地址 |
2 |
002C |
起始地址为002C |
保存数据字长度 |
2 |
0002 |
保存2个字长度的数据 |
CRC码 |
2 |
8001 |
由仪表计算得到CRC码 |
2.4.8. 通讯错误信息及数据的处理
当仪表检测到除了CRC码出错以外的错误时,必须向主机回送信息,功能码的最高位置为1,即从机返送给主机的功能码是在主机发送的功能码的基础上加128。以下的这些代码表明有意外的错误发生。
仪表从主机接收到信息如有CRC错误,则将被仪表忽略。
仪表返送的错误码的格式如下(CRC码除外):
地址码: 1字节
差错码: 1字节(最高位为1)
异常码: 1字节
CRC码: 2字节
仪表相应回送如下异常码:
01:非法的错误码
接收到的功能码仪表不支持。
02:读取非法的数据地址
指定的数据位置超出仪表的可读取的地址范围。
03:非法的数据值
接收到主机发送的数据值超出仪表相应地址的数据范围。