打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Modbus Rtu 通信协议

Modbus Rtu 通信协议(3,6,16号命令)

 1 读取保持寄存器(单个和多个,以字为最小单位)

发送命令帧:

设备地址

功能码

地址H

地址L

数据量H

数据量L

CRC H

CRC L

Addr0

3 H

HoldStart

DataNum

CRC高位

CRC低位

度:8个字节

设备地址:1247

码:3H

数据地址:065535   具体范围与相关设备有关

   量:165535   具体范围与相关设备有关

码:CRC16校验

 返回命令帧:

设备地址

功能码

数据量

数据1

数据N

CRC H

CRC L

Addr1

3 H

返回数据的字节数N

Data (1N)

CRC高位

CRC低位

度:5N 个字节

设备地址:1247

码:3H

量:实际的读取数据数量

   据:返回数据的意义

aHoldStart

n= DataNum1

VW a VB a

VWaVB a+1

VW a+nVB a+n

VWa+nVB a+n+1

Data1

Data(2)

Data(N-1)

Data(N)

码:CRC16校验

命令有误:

1)       没有任何返回

2)       返回异议帧

设备地址

功能码

错误信息

CRC H

CRC L

Addr1

83 H

一个字节的错误信息

CRC高位

CRC低位

2设置保持寄存器(多个,以字为最小单位)

发送命令帧:

 设备地址

功能码

地址H

地址L

数据量H

数据量L

数据字节数

具体

数据

CRC H

CRC L

Addr0

10 H

HoldStart

DataNum

bytN

1bytN

CRC高位

CRC低位

度:9bytN 个字节

设备地址:1247

码:10H

数据地址:065535   具体范围与相关设备有关

   量:1122     具体范围与相关设备有关

数:设置的字节个数 bytN= DataNum×2

   据:具体的字节数据

码:CRC16校验

 返回命令帧:

设备地址

功能码

地址H

地址L

数据量H

数据量L

CRC H

CRC L

Addr1

10 H

HoldStart

DataNum

CRC高位

CRC低位

度:8 个字节

设备地址:1247

码:10H

数据地址:065535   具体范围与相关设备有关    

   量:1122     具体范围与相关设备有关

码:CRC16校验

 命令有误:

1    没有任何返回

2     返回异议帧

地址

功能码

错误信息

CRC H

CRC L

Addr1

90 H

一个字节的错误信息

CRC高位

CRC低位

 

设置单个寄存器(06H

发送命令帧:

 设备地址

功能码

地址H

地址L

数据H

数据L

CRC H

CRC L

Addr0

06H

HoldStart

Data

CRC高位

CRC低位

 

返回命令帧:

设备地址

功能码

地址H

地址L

数据H

数据L

CRC H

CRC L

Addr1

06H

HoldStart

Data

CRC高位

CRC低位

 

 


CRC-16(循环冗余错误校验)

       CRC-16错误校验程序如下:报文(此处只涉及数据位,不指起始位、停止位和任选的奇偶校验位)被看作是一个连续的二进制,其最高有效位(MSB)首选发送。报文先与X^16相乘(左移16位),然后看X^16+X^15+X^2+1除,X^16+X^15+X^2+1可以表示为二进制数1 1000 0000 0000 0101。整数商位忽略不记,16位余数加入该报文(MSB先发送),成为2CRC校验字节。余数中的1全部初始化,以免所有的零成为一条报文被接收。经上述处理而含有CRC字节的报文,若无错误,到接收设备后再被同一多项式(X^16+X^15+X^2+1)除,会得到一个零余数(接收设备核验这个CRC字节,并将其与被传送的CRC比较)。全部运算以2为模(无进位)。

     习惯于成串发送数据的设备会首选送出字符的最右位(LSB-最低有效位)。而在生成CRC情况下,发送首位应是被除数的最高有效位MSB。由于在运算中不用进位,为便于操作起见,计算CRC时设MSB在最右位。生成多项式的位序也必须反过来,以保持一致。多项式的MSB略去不记,因其只对商有影响而不影响余数。

生成CRC-16校验字节的步骤如下:

①装如一个16位寄存器,所有数位均为1

②该16位寄存器的高位字节与开始8位字节进行“异或”运算。运算结果放入这个16位寄存器。

③把这个16寄存器向右移一位。

④若向右(标记位)移出的数位是1,则生成多项式1010000000000001和这个寄存器进行“异或”运算;若向右移出的数位是0,则返回③。

⑤重复③和④,直至移出8位。

⑥另外8位与该十六位寄存器进行“异或”运算。

⑦重复③~⑥,直至该报文所有字节均与16位寄存器进行“异或”运算,并移位8次。

⑧这个16位寄存器的内容即2字节CRC错误校验,被加到报文的最高有效位。

 

        另外,在某些非ModBus通信协议中也经常使用CRC16作为校验手段,而且产生了一些CRC16的变种,他们是使用CRC16多项式X16+X15+X2+1,单首次装入的16位寄存器为0000;使用CRC16的反序X16+X14+X1+1,首次装入寄存器值为0000FFFFH

 

LRC(纵向冗余错误校验)

       LRC错误校验用于ASCII模式。这个错误校验是一个8位二进制数,可作为2ASCII十六进制字节传送。把十六进制字符转换成二进制,加上无循环进位的二进制字符和二进制补码结果生成LRC错误校验(参见图)。这个LRC在接收设备进行核验,并与被传送的LRC进行比较,冒号(:)、回车符号(CR)、换行字符(LF)和置入的其他任何非ASCII十六进制字符在运算时忽略不计。

 

十六进制

二进制

地址 0 20000 0010

功能码 0 10000 0001

起始地址高位0 0 0000 0000

起始地址低位0 0 0000 0000

单元数量 00 0000 0000

0 8 + 0000 1000

0000 1011

变成补码1111 0101

错误校验 F5 F 5

       接受PC把所有收到的数据字节(包括最后的LRC)加在一起,8位应全部为0(注意:和可能超过8位,应略去最低位) 0000 0010

0000 0001

0000 0000

0000 0000

0000 0000

0000 1000

错误校验1111 0101 00000000

3CRC检测

       使用RTU模式,消息包括了一基于CRC方法的错误检测域。CRC域检测了整个消息的内容。

CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

 

       CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。

 

       CRC产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。LSB被提取出来检测,如果LSB1,寄存器单独和预置的值或一下,如果LSB0,则不进行。整个过程要重复8次。在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。最终寄存器中的值,是消息中所有的字节都执行之后的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

} ;

 

转自生活安全网(http://anquanweb.com)

1 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 884MICRO 84 可使主机模拟编程功能,修改PC状态逻辑

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

20 读取通用参数(584L 显示扩展存储器文件中的数据信息

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

2264 保留作扩展功能备用

6572 保留以备用户功能所用 留作用户功能的扩展编码

73119 非法功能

120127 保留 留作内部作用

128255 保留 用于异常应答

 

unsigned int alex_crc16(unsigned char *buf,unsigned int len)
{
    int i,j;
    unsigned int c, crc = 0xFFFF;
    for (i = 0; i < len; i++){
   c = *(buf+i) & 0x00FF;
   crc^=c;
        for(j=0;j<8;j++){
             if(crc & 0x0001)
                {

crc>>=1;

crc^=0xA001;

}
             elsecrc>>=1;
        }
    }

    return(crc);
}

int main(void)
{
char buf[] = {0x01, 0x02, 0x00, 0xc4, 0x00, 0x16};
printf("CRC:%x\n",alex_crc16(buf, 6));
return 0;
}

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
CRC8校验分析和DS18B20的CRC8编程实现方法
CRC从原理到实现
CRC校验原理与程序设计
CRC 算法原理及C 语言实现
DL/T 698.42-2010 电能信息采集与管理系统 第4-2部分 通信协议-集中器下行通信
XModem协议源码-crc16.h,crc16.c
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服