锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

【知识分享】Modbus通信协议详解

时间:2022-09-15 18:30:00 低压并联电容器串接xd1电抗器连接器xcd45t4k1p40交流变送器参数电力电容电抗器限流电抗器xd1h09电子终端连接器de型连接器

协议

这里分两部分,Modbus协议是什么?百度的解释是意思是共同讨论、协商;协商协商制定的共同认可、共同遵守的文件。比如大学毕业找工作的时候,一般都要签一份叫三方协议的协议。

通信协议

那通信协议又是什么呢?通信是双方或多方的沟通,沟通协议是指双方或多方需要共同遵守的沟通方式。例如,现在规定两个人需要使用数字来代表文本,只定义1表示我,2表示你,3表示他,然后现在两个人说1,另一个立即知道说我,但如果一个人不按照规定说话,说4,另一个人不明白他在说什么。在这个例子中,定义的1、2、3是一个简单的通信协议。一般的通信协议,如TCP/IP、蓝牙协议等比Modbus复杂但万变不离其宗。

Modbus

概述

接下来是重头戏,Modbus通信协议。

Modbus串行通信协议是一种Modicon公司(现施耐德电气) Schneider Electric)1979年使用可编程逻辑控制器(PLC)发布通信。Modbus已成为工业通信协议的行业标准(De facto),现在是工业电子设备之间常用的连接方式。--摘自百度百科

Modbus它是一对一的一对一通信方式(主机发送一帧,从机器返回一帧),当然,也是一对一通信,但实际上是一对一通信,只有一个从机器才能同时响应。如果需要同时与多个从机通信,也支持使用广播,即主机发送指令,所有从机接收指令并执行,但不响应。可参国家标准(以下简称国家标准),GBT 19582-2。

主机通过一主多从通信时ID区分需要通信的从机设备。ID范围为1~247,0是广播地址,248~255是用户自定义的地址。

通信形式

目前四种通信形式,RTU、ASCII、TCP、Plus。

RTU:

RTU是指远程终端控制系统Modbus通信形式。一般基于串口通信。其报文格式为16进制,由Slave ID 数据 CRC三部分的验证。详见上述报文分析。剩下的是数据验证,这里用的是CRC验证(循环冗余验证,Cyclic Redundancy Check,简称CRC)。需要注意的是,数据部分的高数据在前面,低数据在后面,CRC验证是低位在前,高位在后。

名称

从机ID

数据部分

CRC低位

CRC高位

长度 1字节 n字节 1字节 1字节

CRC校验

CRC(Cyclic Redundancy Checksum)是一种代表循环冗余校验和的纠错技术。CRC这里不谈计算原理,直接上代码。

C语言实现-摘自FreeModbus里的实现

static const UCHAR aucCRCHi[] = {     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,     0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,     0x00, 0xC1, 0x81 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40
};

static const UCHAR aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80, 0x40
};

USHORT
usMBCRC16( UCHAR * pucFrame, USHORT usLen )
{
    UCHAR           ucCRCHi = 0xFF;
    UCHAR           ucCRCLo = 0xFF;
    int             iIndex;

    while( usLen-- )
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );
        ucCRCHi = aucCRCLo[iIndex];
    }
    return ( USHORT )( ucCRCHi << 8 | ucCRCLo );
}

帧的完整性判断

        国标里规定:一帧中两个字符之间间隔时间不超过1.5字符,以大于3.5字符间隔时间作为一帧的结束,波特率高于19200时,间隔时间固定为1.7ms。

        比如现在串口设置的波特率为9600,1个停止位,无校验位,8个数据位(这种设置以下会简写成9600-1N8的形式),那么代入如下公式:

可以算出,此设置下,断帧时间应该为3.645833……ms。

另外国标还规定,串口配置中,默认应该配置有偶校验。

tips:在9600-1N8的串口设置下,可以快速估算数据发送时间,即1个字节发送时间为1ms。

ASCII:

        ASCII是一种字符型的通信方式,一般也是基于串口进行通信。其报文格式是以ASCII码编码的,由帧头(:)+Slave ID+数据+LRC校验+帧尾(/r/n)五部分组成,其中Slave ID、数据部分跟RTU完全一样,只不过是以ASCII编码形式,如Slave ID,RTU是01一个字节的时候,ASCII表示就是30 31两个字节。所以实际工业应用场合很少会用到Modbus/ASCII,因为通信效率太低。

        另外跟RTU还有个不同的地方,就是这里使用的校验不是CRC校验,而是LRC校验。

LRC校验

        纵向冗余校验,Longitudinal Redundancy Check,简称:LRC。

LRC具体算法如下:

1、对需要校验的数据(2n个字符)两两组成一个16进制的数值求和。

2、将求和结果与256求模。

3、用256减去所得模值得到校验结果(另一种方法:将模值按位取反然后加1)。

C语言实现--摘自FreeModbus里的实现

static          UCHAR
prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
{
    UCHAR           ucLRC = 0;  /* LRC char initialized */

    while( usLen-- )
    {
        ucLRC += *pucFrame++;   /* Add buffer byte without carry */
    }

    /* Return twos complement */
    ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
    return ucLRC;
}

        因为整个报文都是ASCII的编码形式,所以如果使用单片机串口通信,可以把串口的数据位设置为7位(ASCII的码表范围就是0~0x7F,只用低7位)。

名称

帧头:

从机ID

数据部分

LRC

帧尾/r

帧尾/n

长度 1字节 2字节 n字节 2字节 1字节 1字节

TCP:

        TCP是一种网络协议,而Modbus/TCP就是基于网络协议上的一种应用层协议。其报文格式是十六进制的,由报头(2字节的帧序号+2字节的协议类型+2字节的数据长度+1字节的Slave ID)+数据两部分组成。由于该通信方式是基于TCP/IP这种可靠协议上,所以通信不需要有额外的校验机制。

名称

帧序号

协议类型

数据长度

从机ID

数据部分

长度

2字节

2字节

2字节

1字节

n字节

Plus:

        Modbus Plus(又称MB+)是一种高速现场总线网络,也是一种典型的令牌总线网。

报文解析

Modbus功能码表如下:

功能码

名称

作用

01

读取线圈状态

取得一组逻辑线圈的当前状态(ON/OFF)

02

读取输入状态

取得一组开关输入的当前状态(ON/OFF)

03

读取保持寄存器

在一个或多个保持寄存器中取得当前的二进制值

04

读取输入寄存器

在一个或多个输入寄存器中取得当前的二进制值

05

强置单线圈

强置一个逻辑线圈的通断状态

06

预置单寄存器

把具体二进值装入一个保持寄存器

07

读取异常状态

取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态

08

回送诊断校验

把诊断校验报文送从机,以对通信处理进行评鉴

09

编程(只用于484)

使主机模拟编程器作用,修改PC从机逻辑

10

控询(只用于484)

可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送

11

读取事件计数

可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时

12

读取通信事件记录

可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误

13

编程(184/384 484 584)

可使主机模拟编程器功能修改PC从机逻辑

14

探询(184/384 484 584)

可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送

15

强置多线圈

强置一串连续逻辑线圈的通断

16

预置多寄存器

把具体的二进制值装入一串连续的保持寄存器

17

报告从机标识

可使主机判断编址从机的类型及该从机运行指示灯的状态

18

(884和MICRO 84)

可使主机模拟编程功能,修改PC状态逻辑

19

重置通信链路

发生非可修改错误后,是从机复位于已知状态,可重置顺序字节

20

读取通用参数(584L)

显示扩展存储器文件中的数据信息

21

写入通用参数(584L)

把通用参数写入扩展存储文件,或修改之

22~64

保留作扩展功能备用

65~72

保留以备用户功能所用

留作用户功能的扩展编码

73~119

非法功能

120~127

保留

留作内部作用

128~255

保留

用于异常应答

        其中常用的功能码有8个(01、02、03、04、05、06、15、16),可以分为位操作和字操作两类,其中,位操作的是线圈和离散输入,两者区别在于,线圈是可读可写的,而离散输入是只读。字操作的是保持寄存器和输入寄存器,两者区别在于,保持寄存器是可读可写的,而输入寄存器是只读的。

线圈

读线圈功能码01,可读单个或多个

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 线圈起始地址 线圈个数 CRC校验

从节点正常应答帧格式:

序号 0 1 2 3 4 L+2 L+3 L+4
字段定义 ADDR CMD Length Data1 Data2 DataN LSB MSB
解释 从节点地址 命令类型 发送字节数L=n/8+(1) 第一个字节数据值 第二个字节数据值 第N个字节数据值 CRC校验

字节数据值的定义:

7 6 5 4 3 2 1 0
线圈值 首地址+7 首地址+6 首地址+5 首地址+4 首地址+3 首地址+2 首地址+1 首地址

注:数据从低位开始填充,填充完成后还有剩余的高位则全部补0。

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

写单个线圈功能码05

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 线圈地址 数据 CRC校验

注:数据为0xFF00表示设置线圈状态为ON,数据为0x0000表示设置线圈状态为OFF。

从节点正常应答帧格式(同发送帧):

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 线圈地址 数据 CRC校验

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

写多个线圈功能码15

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7 L+6 L+7 L+8
字段定义 ADDR CMD MSB LSB MSB LSB Length Data1 DataN LSB MSB
解释 从节点地址 命令类型 起始线圈地址 线圈数n 发送字节数L=n/8+(1) 第一个字节数据值 第N个字节数据值 CRC校验

字节数据值的定义:

7 6 5 4 3 2 1 0
线圈值 首地址+7 首地址+6 首地址+5 首地址+4 首地址+3 首地址+2 首地址+1 首地址

注:数据从低位开始填充,填充完成后还有剩余的高位则全部补0。

从节点正常应答帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 线圈起始地址 线圈个数 CRC校验

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

保持寄存器

读保持寄存器功能码03,可读单个或多个

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 寄存器起始地址 寄存器个数 CRC校验

从节点正常应答帧格式:

序号 0 1 2 3 4 L+1 L+2 L+3 L+4
字段定义 ADDR CMD Length MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 发送字节数L=n*2 第一个寄存器值 第N个寄存器值 CRC校验

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

写单个保持寄存器功能码06

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 寄存器地址 数据 CRC校验

从节点正常应答帧格式(同发送帧):

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 寄存器地址 数据 CRC校验

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

写多个保持寄存器功能码16

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7 8 L+5 L+6 L+7 L+8
字段定义 ADDR CMD MSB LSB MSB LSB Length MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 起始寄存器地址 寄存器数n 发送字节数L=n*2 第一个寄存器值 第N个寄存器值 CRC校验

从节点正常应答帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 ADDR CMD MSB LSB MSB LSB LSB MSB
解释 从节点地址 命令类型 寄存器起始地址 寄存器个数 CRC校验

从节点异常应答格式:

序号 0 1 2 3 4
字段定义 ADDR CMD+128 ErrCode LSB MSB
解释 从节点地址 命令类型+128 错误码 CRC校验

离散输入--只读

读离散输入的功能码02,可读单个或多个(协议格式跟01功能码完全一致)

主节点发送帧格式:

序号 0 1 2 3 4 5 6 7
字段定义 元器件数据手册、IC替代型号,打造电子元器件IC百科大全!

相关文章