写点什么

MODBUS RTU 485 协议简要说明

作者:矜辰所致
  • 2022 年 9 月 16 日
    江苏
  • 本文字数:4249 字

    阅读完需:约 14 分钟

MODBUS RTU 485 协议简要说明

1、 RS485

RS-485 是通信线路,只是一种硬件接口。


基本概念引用百度百科:



RS485 具有 A、B 两根线,并且通过 A、B 两根线之间的压差来定义 1 和 0。A 和 B 的压差在+(2-6)V 内为高电平,A 和 B 的压差在-(2-6)V 内为低电平。由此就定义了 RS485 的电气接口和电平。


虽然 RS485 通讯链 用一对双绞线将 各个接口的 “A”“ B” 端连接起来,就可以进行通信了,但是为了保证整个系统的稳定性,还是建议将各个设备共 负极(地),来确保网络的稳定性。


半双工!!!

2、 Modbus 协议


基本概念引用至百度百科:



在进行多机通信的时候,Modbus 协议规定每个控制器必须要知道它们的设备地址,识别按照地址发送过来的数据,决定是否要产生动作,产生何种动作,如果要回应,控制器将生成的反馈信息用 Modbus 协议发出。


Modbus 有两种通信传输方式,一种是 ASCII 模式,一种是 RTU 模式。


  • RTU 方式:也叫十六进制 例如:发送 0x03:0000 0011

  • ASC 方式:0x03 (发送 0 :0x30:0011 0000 )( 发送 3:0x33:0011 0011)


ASCII 的通信效率低,方便调试,实际应用比较少;工业上都采用 RTU 方式,效率高。


工业应用上一般 Modbus 485 波特率为 9600 或者 4800,因为速度越快传输距离越短,速度太慢也影响效率。

3、 Modbus 和 RS485 关系和区别


RS485 只是一种硬件接口,他只是把来自单片机 UART 的信号,翻转电平进行传输,并驱动线缆。

所以 RS485 其实只是一种硬件接口驱动芯片。

Modbus 是一种软件协议,规定了一种语言。

Modbus 可以跑在 RS485 上,也能跑在 RS232 上,也能跑在 POWERBUS。

Modbus 可以支持多种电气接口,如 RS-232、RS-485 等,还可以在各种介质上传送,如双绞线、光纤、无线等。


同理。RS485 上可以用 Modbus,也能用其他自拟协议。协议只是软件。


Modbus 只是通信协议的一种,就像汉语和英语一样,就是一种交流的语言,一种机器之间交流的语言。那么在交流之前肯定要有沟通的桥梁吧,那就是传输媒介 485 或 232 或其他电气规则,同一种协议可以用不同的传输媒介方式如 485 或 232 但是同一传输线路上不能同时存在两种协议.

4、 Modbus-RTU 协议格式说明

4.1 地址码

通讯地址: 0~247


对于一主多从的通讯架构中,主机为 0。从设备地址为 1~247。


主机发送地址码 0 ,一般称为广播数据,从机无需响应。

4.2 功能码

ModBus 通讯规约可定义的功能码为 1 到 127。


比如: 03H 为 读寄存器功能码; 16H 为 写寄存器功能码。



从设备的回应数据包格式:


  • 回应数据包和主机查询的数据包格式包是一致的

  • 正常回应时,功能码与主机发的功能码一致(1~127)

  • 异常的回应,功能码要在收到的功能码基础上加上 128 例如:发 0x03 收:0x03 +128

4.3 数据区

常见的功能码的数据吗格式一般在 Modbus 通用协议中已经做了规范。


比如: 对于 03 功能码,后面跟的数据包括 2 个字节表示寄存器地址,2 个字节表示想要读取的寄存器个数



返回的 3 功能码,后接 1 个字节的返回字节个数(该个数应为上述读取寄存器个数的两倍,因为一个寄存器对应两个字节,比如上图中返回的数据应该是 01 03 20 xxxxxxx)


再看个例子:


4.4 CRC 校验

CRC 校验:CRC - 16 ,低位在前,高位在后。这个 CRC 校验代码网上也很多,下面是我自己用的:


check.c:


#include "check.h"

//CRC16 高位字节表const u8 CRC16HiTable[]={ 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 };
//CRC16低位字节表const u8 CRC16LoTable[]={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 };
//CRC8 字节表const u8 CRC8Table[]= { 0,94,188,226,97,63,221,131,194,156,126,32,163,253,31,65, 157,195,33,127,252,162,64,30, 95,1,227,189,62,96,130,220,35,125,159,193,66,28,254,160,225,191,93,3,128,222,60,98,190,224,2,92,223,129,99,61,124,34,192,158,29,67,161,255,70,24,250,164,39,121,155,197,132,218,56,102,229,187,89,7,219,133,103,57,186,228,6,88,25,71,165,251,120,38,196,154,101,59,217,135,4,90,184,230,167,249,27,69,198,152,122,36,248,166,68,26,153,199,37,123,58,100,134,216,91,5,231,185,140,210,48,110,237,179,81,15,78,16,242,172,47,113,147,205,17,79,173,243,112,46,204,146,211,141,111,49,178,236,14,80,175,241,19,77,206,144,114,44,109,51,209,143,12,82,176,238,50,108,142,208,83,13,239,177,240,174,76,18,145,207,45,115,202,148,118,40,171,245,23,73,8,86,180,234,105,55,213,139,87,9,235,181,54,104,138,212,149,203, 41,119,244,170,72,22,233,183,85,11,136,214,52,106,43,117,151,201,74,20,246,168,116,42,200,150,21,75,169,247,182,232,10,84,215,137,107,53 };


//求和校验 所有字节之和为0u32 Checksum_Sum(u8* buf,u16 len){ u8 check = 0; while(len--){ check += *buf++; } check = 256 - check%256; return (check)&0x00ff;}
//异或校验 所有字节异或u16 Checksum_XOR(u8* buf, u16 len){ u8 check = 0; u8 checkTemp = 0; check = *buf; buf++; checkTemp = *buf; buf++; check = check^checkTemp; len -=2; while(len--){ check = check^(*buf++); } return (check)&0x00ff;
}
//CRC8 校验u16 Checksum_CRC8(u8 *buf,u16 len){ u8 check = 0; while(len--){ check = CRC8Table[check^(*buf++)]; } return (check)&0x00ff; }
//CRC16 校验//高字节在前,低字节在后u16 Checksum_CRC16(u8 *buf,u16 len){ int index; u16 check = 0; u8 crc_low=0xff; u8 crc_high=0xff; while(len--){ index = crc_high^(*buf++); crc_high = crc_low^CRC16HiTable[index]; crc_low = CRC16LoTable[index]; } check +=crc_high; check <<=8; check +=crc_low; return check;}
复制代码


4.5 寄存器地址与 PLC 组态地址


如图所示,对于从机而言,有寄存器地址 和 PLC 组态地址的分别:


  • 寄存器 PLC 地址指存放于控制器中的地址,这些控制器可以是 PLC,也可以使触摸屏,或是文本显示器。PLC 地址一般采用 10 进制描述,共有 5 位,其中第一位代码寄存器类型。

  • 寄存器 modbus 协议地址指通信时使用的寄存器地址,在实际编程中,由于寄存器 PLC 地址前缀的区分作用,所以只需说明后 4 位数,而且需转换为 4 位十六进制地址。例如 PLC 地址 40001 对应寻址地址 0x0000,40002 对应寻址地址 0x0001,寄存器寻址地址一般使用 16 进制描述。再如,PLC 寄存器地址 40003 对应协议地址 0002,PLC 寄存器地址 30003 对应协议地址 0002。在实际编程中,由于前缀的区分作用,所以只需说明后 4 位数,而且需转换为 4 位十六进制地址。

  • 支持 Modbus 协议的设备或软件,使用时用户直接设置或看到的应当是 Modbus 数据地址。Modbus 地址所访问的数据,是通过各种 “功能”读写而来。 功能码是 Modbus 地址的底层。 如果 Modbus 通 信的一方提供的所谓 Modbus 协议只有功能码,则需要注意了解此 功能号与 Modbus 地址间的对应关系。

发布于: 刚刚阅读数: 5
用户头像

矜辰所致

关注

CSDN、知乎、微信公众号: 矜辰所致 2022.08.02 加入

不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开! 为了活下去的嵌入式工程师,画画板子,敲敲代码,玩玩RTOS,搞搞Linux ...

评论

发布
暂无评论
MODBUS RTU 485 协议简要说明_Modbus_矜辰所致_InfoQ写作社区