简思PLC 之 MODBUS RTU通信讲解

简思PLC使用的是MODBUS-RTU通讯协议,MODBUS协议详细定义了校验码、数据序列等,这些都是特定数据交换的必要内容。MODBUS协议在一根通讯线上 使用主从应答式连接(半双工),这意味着在一根单独的通讯线上信号沿着相反的两个方向传输。首先,主计算机的信号寻址到一台唯一的终端设备(从机),然后,终端设备发出的应答信号以相反的方向传输给主机。MODBUS协议只允许在主机(PC,PLC等)和终端设备之间通讯,而不允许独立的终端设备之间的数据交换,这样各终端设备不会在它们初始化时占据通讯线路,而仅限于响应到达本机的查询信号。

1.MODBUS功能码简述

    下表列出MODBUS支持的部分功能代码:以十进制表示。

表1.1 MODBUS部分功能码

1.1功能码说明

    功能码可以分为位操作和字操作两类。位操作的最小单位为BIT,字操作的最小单位为两个字节。

        【位操作指令】    读线圈状态01H,读(离散)输入状态02H,写单个线圈06H和写多个线圈0FH。

        【字操作指令】    读保持寄存器03H,写单个寄存器06H,写多个保持寄存器10H。

1.2寄存器地址分配

表1.2 MODBUS寄存器地址分配

1.3寄存器种类说明

表1.3 MODBUS寄存器种类说明

1.4 PLC地址和协议地址区别

    PLC地址可以理解为协议地址的变种,在触摸屏和PLC编程中应用较为广泛。

1.4.1 寄存器PLC地址

    寄存器PLC地址指存放于控制器中的地址,这些控制器可以是PLC,也可以使触摸屏,或是文本显示器。

1.4.2 寄存器协议地址

    寄存器协议地址指指通信时使用的寄存器地址,例如PLC地址4x2001对应寻址地址G1,4x2002对应寻址地址G2,寄存器寻址地址使用10进制描述。再如,PLC寄存器地址3x5000对应协议地址Ai1,PLC寄存器地址4x5000对应协议地址Ao1,虽然两个PLC寄存器寄存器通信时使用相同的地址,但是需要使用不同的命令访问,所以访问时不存在冲突。

2、协议、字节格式说明 

协议采用MODBUS 协议,RTU 模式,默认参数如下。

波特率: 19200 

数据位: 8 

校验位: Even(偶校验) 

停止位 : 1

 

3、MODBUS 协议简单说明

 1. 帧基本格式 主机请求命令

 

从机回应命令

 

从机回应的地址和功能码和主机请求命令相同,数据长度和主机的数据长度相同,数据 内容字节数和数据长度有关,字节数 n=数据长度 L*2

 

4、具体指令说明:

4.1读离散输入状态  (适用于1x开头的位状态)

<02 Read Discrete Inputs(1x)>

如主机读取PLC输入端X2端口的状态

Modbus RTU 主机发送

PLC返回

 

例:

 a 读取PLC输入端X2端口的状态

主机发送 01 02 00 02 00 01 18 0A

如果X2是断状态,则返回数据 01 02 01 00 A1 88

如果X2是通状态,则返回数据 01 02 01 01 60 48

 b 读取PLC输入端X2->X6端口的状态

 

主机发送 01 02 00 02 00 05 19 C9

如果是全断状态,则返回数据 01 02 01 00 A1 88

如果X2,X3是通状态,则返回数据 01 02 01 03 E1 89 ,0x03的二进制码为0000 0011,则表示从起始地址两位X2,X3是通的。

4.2 读线圈状态  (适用于0x开头的位状态)

<01 Read Coils(0x)>

如主机读取PLC输出端Y2端口的状态

Modbus RTU 主机发送

PLC返回

例:

a.读取PLC输出端Y2端口的状态

主机发送 01 01 00 02 00 01 5C 0A

如果Y2是断状态,则返回数据 01 01 01 00 51 88

如果Y2是通状态,则返回数据 01 01 01 01 90 48

b读取PLC输出端Y2-Y11端口的状态

 

主机发送 01 01 00 02 00 0A 1D CD

如果Y2是通状态,则返回数据 01 01 02 01 00 B8 6C

注意: 如果用Modbus Poll读取多个位状态,则地址位需要是8的整数倍,如读M1060-M1065的状态,则起始地址为0x1056起读16个

4.3 读保持寄存器  (适用于4x开头的寄存器)

<03 Read Holding Registers(4x)>

如主机读取PLC通用寄存器G1的值

 

Modbus RTU 主机发送 

G1地址2001,转16进制是0x07D1

主机发送 01 03 07 D1 00 02 95 46

如:返回数据 01 03 04 00 64 00 C8 BA 7A

G1的值是0x0064,转10进制值100

G2的值是0x00C8,转10进制值200

4.4 读输入寄存器  (适用于3x开头的寄存器)

<04 Read Input Registers(3x)>

如主机读取PLC中时序环0的当前帧

 

Modbus RTU 主机发送 

0环的当前帧地址是3x2

主机发送 01 04 00 02 00 01 90 0A

如:返回数据 01 04 02 00 01 78 F0

当前帧为第1帧

如:返回数据 01 04 02 00 03 F9 31

当前帧为第3帧

4.5 写单个线圈 (适用于0x开头的位状态)

<05 Write Single Coil(0x)>

如主机发送指令控制Y4通断 (Y4的地址是0x04)

如触发Y4通

主机发送:   01 05 00 04 FF 00 CD FB

返回数据    01 05 00 04 FF 00 CD FB

如触发Y4断

主机发送:   01 05 00 04 00 00 8C DB

返回数据    01 05 00 04 00 00 8C DB

4.6 写单个保持寄存器  (适用于4x开头的寄存器)

<06 Write Single Register(4x)>

如主机发送指令修改G1的值 (G1的地址是4x2001)

 

如修改G1的值为10

主机发送:   01 06 07 D1 00 0A 58 80

返回数据    01 06 07 D1 00 0A 58 80

如修改G1的值为500

主机发送:   01 06 07 D1 01 F4 D8 90

返回数据    01 06 07 D1 01 F4 D8 90

4.6 写多个线圈   (适用于0x开头的位状态)

<15 Write Multiple Coils(0x)>

如主机发送指令控制M10-M14通断 (M10的地址是0x1010)

 

如触发M10断,M11通,M12通,M13断,M14断,则发送的二进制码为00110

主机发送:   01 0F 03 F2 00 05 01 06 D6 B2

03 F2 是地址 1010的16进制数。

00 05 是位数据个数。

01    数据占用字节数

06    需要控制的状态 00110

返回数据    01 0F 03 F2 00 05 34 7F

4.7 写多个保持寄存器   (适用于4x开头的寄存器)

<16 Write Multiple Registers(4x)>

如主机发送指令修改G0-G2的值 (G1的地址是4x2000)

如修改G0=4,G1=5,G2=6

主机发送:   01 10 07 D0 00 03 06 00 04 00 05 00 06 99 9E

 07 D0 是G0地址 2000的16进制数。

 00 03 是需要修改的数据个数。

 06    数据占用字节数

 00 04 第一个数值

 00 05 第二个数值

 00 06 第三个数值

 99 9E CRC校验

返回数据    01 10 07 D0 00 03 80 85

CRC检测

CRC添加到命令中时,低字节先加入,然后高字节。 

CRC简单函数如下: 

unsigned short CRC16(puchMsg, usDataLen)

unsigned char *puchMsg ; /* 要进行CRC校验的消息 */

unsigned short usDataLen ; /* 消息中字节数 */

{

unsigned char uchCRCHi = 0xFF ; /* CRC字节初始化 */

unsigned char uchCRCLo = 0xFF ; /* CRC 字节初始化 */

unsigned uIndex ; /* CRC循环中的索引 */

 

while (usDataLen--) /* 传输消息缓冲区 */

{

uIndex = uchCRCHi ^ *puchMsgg++ ; /* 计算CRC */

uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;

uchCRCLo = auchCRCLo[uIndex] ;

}

return (uchCRCHi << 8 | uchCRCLo) ;

}

 

/* CRC 高位字节值表 */

static unsigned char auchCRCHi[] = {

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

} ; 

/* CRC低位字节值表*/

static char auchCRCLo[] = {

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

} ;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

首页    PLC控制器    CAN扩展通信    简思PLC 之 MODBUS RTU通信讲解
创建时间:2024-04-28 14:06
浏览量:0