Modbus协议中CRC校验和LRC校验.docx
《Modbus协议中CRC校验和LRC校验.docx》由会员分享,可在线阅读,更多相关《Modbus协议中CRC校验和LRC校验.docx(10页珍藏版)》请在冰豆网上搜索。
Modbus协议中CRC校验和LRC校验
CRC的生成
循环冗余校验(CRC)域为两个字节,包含一个二进制16位值。
附加在报文后面的CRC的值由发送设备计算。
接收设备在接收报文时重新计算CRC的值,并将计算结果于实际接收到的CRC值相比较。
如果两个值不相等,则为错误。
CRC的计算,开始对一个16位寄存器预装全1.然后将报文中的连续的8位子节对其进行后续的计算。
只有字符中的8个数据位参与生成CRC的运算,起始位,停止位和校验位不参与CRC计算。
CRC的生成过程中,每个8–位字符与寄存器中的值异或。
然后结果向最低有效位(LSB)方向移动(Shift)1位,而最高有效位(MSB)位置充零。
然后提取并检查LSB:
如果LSB为1,则寄存器中的值与一个固定的预置值异或;如果LSB为0,则不进行异或操作。
这个过程将重复直到执行完8次移位。
完成最后一次(第8次)移位及相关操作后,下一个8位字节与寄存器的当前值异或,然后又同上面描述过的一样重复8次。
当所有报文中子节都运算之后得到的寄存器中的最终值,就是CRC.
生成CRC的过程为:
1.将一个16位寄存器装入十六进制FFFF(全1).将之称作CRC寄存器.
2.将报文的第一个8位字节与16位CRC寄存器的低字节异或,结果置于CRC寄存器.
3.将CRC寄存器右移1位(向LSB方向),MSB充零.提取并检测LSB.
4.(如果LSB为0):
重复步骤3(另一次移位).(如果LSB为1):
对CRC寄存器异或多项式值0xA001(1010000000000001).
5.重复步骤3和4,直到完成8次移位。
当做完此操作后,将完成对8位字节的完整操作。
6.对报文中的下一个字节重复步骤2到5,继续此操作直至所有报文被处理完毕。
7.CRC寄存器中的最终容为CRC值.
8.当放置CRC值于报文时,如下面描述的那样,高低字节必须交换。
MODBUS协议的CRC校验子程序代码
为方便读者使用MODBUS协议,将VC、VB、ASM51环境下MODBUS协议的CRC校验子程序代码一并给出,供读者参考。
//***CRCCalculationforMODBUSProtocolforVC***//
//数组snd为地址等传输字节,num为字节数,发为6收为5//
unsignedintmb_crc(BYTE*snd,intnum)
{
inti,j;
unsignedintc,crc=0xFFFF
for(i=0;i{c=snd[i]&0x00FF;crc^=c;for(j=0,j<8,j){if(crc&0x0001){crc>>=1;crc^=0xA001;}elsecrc>>=1}}return(crc);}unsignedshortintCrcCheck(constunsignedchar*buffer,constintbuffLen){unsignedshortintcrcValue=0;if(!buffer||buffLen<0){returncrcValue;}intCRCHi[]={0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40};intCRCLo[]={0x0,0xC0,0xC1,0x1,0xC3,0x3,0x2,0xC2,0xC6,0x6,0x7,0xC7,0x5,0xC5,0xC4,0x4,0xCC,0xC,0xD,0xCD,0xF,0xCF,0xCE,0xE,0xA,0xCA,0xCB,0xB,0xC9,0x9,0x8,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};inti,m;intdCRCHi=0xFF;intdCRCLo=0xFF;for(i=0;i{m=dCRCLo^buffer[i];dCRCLo=dCRCHi^CRCHi[m];dCRCHi=CRCLo[m];}crcValue=dCRCLo+(dCRCHi<<8);returncrcValue;} '//***CRCCalculationforMODBUSProtocolforVB***//Functionmb_crc(ByRefsnd()asBYTE,numasinteger)asLongcrc_l=crc_h=&HFFfori=1tonumcrc_l=crc_lXORsnd(i)forj=1to8ifcrc_lAND1thencrc_l=(crc_l-1)/2ifcrc_hand1thencrc_l=crc_l128crc_h=(crc_h-1)/2endifcrc_l=crc_lXOR&HA0crc_h=crc_hXOR&H01else:crc_l=crc_l/2ifcrc_hand1thencrcl_l=crc_l128crc_h=(crc_h-1)/2else:crc_h=crc_h/2endifendifnextjnextimb_crc=crc_lcrc_h*256EndFunction ;CRCCalculationforMODBUSProtocolforASM51;R1为发送(接收)字节的缓存首地址;R2为发送(接收)字节的字节数(不含CRC字节),;R3为CRC校验低位字节,;R4为CRC校验高位字节,CRC:MOVA,#0FFHMOVR4,AMOVR3,ACRC1:MOVA,R1XRLA,R3MOVR3,AMOVR2,#08HCRC8:CLRCMOVA,R4RRCAMOVR4,AMOVA,R3RRCAMOVR3,AJNCCRC10MOVA,R3XRLA,#01HMOVR3,AMOVA,R4XRLA,#0A0HMOVR4,ACRC10:DJNZR2,CRC8INCR1DJNZCRC1RETLRC的生成纵向冗余校验(LRC)为一个字节,含有8位二进制值。LRC由发送设备计算,并附加LRC到报文。接收设备在接收文时计算LRC,并将计算的结果与在LRC接收到的实际值相比较,如果两个值不相等,则结果为错。LRC的计算,对报文中的所有的连续8位字节相加,忽略任何进位,然后求出其二进制补码。LRC为一个8位域,那么每个会导致值大于255新的相加只是简单的将域的值在零”回绕”。因为没有第9位,进位被自动放弃。生成一个LRC的过程为:1.不包括起始”冒号”和结束CRLF的报文中的所有字节相加到一个8位域,故此进位被丢弃。2.从FF(全1)十六进制中减去域的最终值,产生1的补码(二进制反码)。3.加1产生二进制补码.将LRC置于报文当8位LRC(2个ASCII字符)在报文中传送时,高位字符首先发送,然后是低位字符。例如,如果LRC值为十六进制61(01100001):例:下面给出了执行生成LRC的C语言函数。函数带有两个参数:unsignedchar*auchMsg;指向含有用于生成LRC的二进制数据报文缓冲区的指针,unsignedshortusDataLen;报文缓冲区的字节数.LRC生成函数staticunsignedcharLRC(auchMsg,usDataLen)/*函数返回unsignedchar类型的LRC结果*/unsignedchar*auchMsg;/*要计算LRC的报文*/unsignedshortusDataLen;/*报文的字节数*/{unsignedcharuchLRC=0;/*LRC初始化*/while(usDataLen--)/*完成整个报文缓冲区*/uchLRC+=*auchMsg++;/*缓冲区字节相加,无进位*/return((unsignedchar)(-((char)uchLRC)));/*返回二进制补码*/}
c=snd[i]&0x00FF;
crc^=c;
for(j=0,j<8,j)
if(crc&0x0001)
crc>>=1;
crc^=0xA001;
}
else
crc>>=1
return(crc);
unsignedshortintCrcCheck(constunsignedchar*buffer,constintbuffLen)
unsignedshortintcrcValue=0;
if(!
buffer||buffLen<0)
returncrcValue;
intCRCHi[]={
0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,
0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,
0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,
0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,
0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,
0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,
0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,
0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,
0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,
0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,
0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,
0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,
0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x1,
0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,
0x81,0x40,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,
0x40,0x1,0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,
0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,0x81,0x40,0x1,
0xC0,0x80,0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x0,0xC1,
0x81,0x40,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40,0x1,0xC0,0x80,
0x41,0x1,0xC0,0x80,0x41,0x0,0xC1,0x81,0x40
};
intCRCLo[]={
0x0,0xC0,0xC1,0x1,0xC3,0x3,0x2,0xC2,0xC6,0x6,0x7,0xC7,0x5,
0xC5,0xC4,0x4,0xCC,0xC,0xD,0xCD,0xF,0xCF,0xCE,0xE,0xA,0xCA,
0xCB,0xB,0xC9,0x9,0x8,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
inti,m;
intdCRCHi=0xFF;
intdCRCLo=0xFF;
for(i=0;i{m=dCRCLo^buffer[i];dCRCLo=dCRCHi^CRCHi[m];dCRCHi=CRCLo[m];}crcValue=dCRCLo+(dCRCHi<<8);returncrcValue;} '//***CRCCalculationforMODBUSProtocolforVB***//Functionmb_crc(ByRefsnd()asBYTE,numasinteger)asLongcrc_l=crc_h=&HFFfori=1tonumcrc_l=crc_lXORsnd(i)forj=1to8ifcrc_lAND1thencrc_l=(crc_l-1)/2ifcrc_hand1thencrc_l=crc_l128crc_h=(crc_h-1)/2endifcrc_l=crc_lXOR&HA0crc_h=crc_hXOR&H01else:crc_l=crc_l/2ifcrc_hand1thencrcl_l=crc_l128crc_h=(crc_h-1)/2else:crc_h=crc_h/2endifendifnextjnextimb_crc=crc_lcrc_h*256EndFunction ;CRCCalculationforMODBUSProtocolforASM51;R1为发送(接收)字节的缓存首地址;R2为发送(接收)字节的字节数(不含CRC字节),;R3为CRC校验低位字节,;R4为CRC校验高位字节,CRC:MOVA,#0FFHMOVR4,AMOVR3,ACRC1:MOVA,R1XRLA,R3MOVR3,AMOVR2,#08HCRC8:CLRCMOVA,R4RRCAMOVR4,AMOVA,R3RRCAMOVR3,AJNCCRC10MOVA,R3XRLA,#01HMOVR3,AMOVA,R4XRLA,#0A0HMOVR4,ACRC10:DJNZR2,CRC8INCR1DJNZCRC1RETLRC的生成纵向冗余校验(LRC)为一个字节,含有8位二进制值。LRC由发送设备计算,并附加LRC到报文。接收设备在接收文时计算LRC,并将计算的结果与在LRC接收到的实际值相比较,如果两个值不相等,则结果为错。LRC的计算,对报文中的所有的连续8位字节相加,忽略任何进位,然后求出其二进制补码。LRC为一个8位域,那么每个会导致值大于255新的相加只是简单的将域的值在零”回绕”。因为没有第9位,进位被自动放弃。生成一个LRC的过程为:1.不包括起始”冒号”和结束CRLF的报文中的所有字节相加到一个8位域,故此进位被丢弃。2.从FF(全1)十六进制中减去域的最终值,产生1的补码(二进制反码)。3.加1产生二进制补码.将LRC置于报文当8位LRC(2个ASCII字符)在报文中传送时,高位字符首先发送,然后是低位字符。例如,如果LRC值为十六进制61(01100001):例:下面给出了执行生成LRC的C语言函数。函数带有两个参数:unsignedchar*auchMsg;指向含有用于生成LRC的二进制数据报文缓冲区的指针,unsignedshortusDataLen;报文缓冲区的字节数.LRC生成函数staticunsignedcharLRC(auchMsg,usDataLen)/*函数返回unsignedchar类型的LRC结果*/unsignedchar*auchMsg;/*要计算LRC的报文*/unsignedshortusDataLen;/*报文的字节数*/{unsignedcharuchLRC=0;/*LRC初始化*/while(usDataLen--)/*完成整个报文缓冲区*/uchLRC+=*auchMsg++;/*缓冲区字节相加,无进位*/return((unsignedchar)(-((char)uchLRC)));/*返回二进制补码*/}
m=dCRCLo^buffer[i];
dCRCLo=dCRCHi^CRCHi[m];
dCRCHi=CRCLo[m];
crcValue=dCRCLo+(dCRCHi<<8);
'//***CRCCalculationforMODBUSProtocolforVB***//
Functionmb_crc(ByRefsnd()asBYTE,numasinteger)asLong
crc_l=crc_h=&HFF
fori=1tonum
crc_l=crc_lXORsnd(i)
forj=1to8
ifcrc_lAND1then
crc_l=(crc_l-1)/2
ifcrc_hand1then
crc_l=crc_l128
crc_h=(crc_h-1)/2
endif
crc_l=crc_lXOR&HA0
crc_h=crc_hXOR&H01
else:
crc_l=crc_l/2
crcl_l=crc_l128
crc_h=crc_h/2
nextj
nexti
mb_crc=crc_lcrc_h*256
EndFunction
;CRCCalculationforMODBUSProtocolforASM51
;R1为发送(接收)字节的缓存首地址
;R2为发送(接收)字节的字节数(不含CRC字节),
;R3为CRC校验低位字节,
;R4为CRC校验高位字节,
CRC:
MOVA,#0FFH
MOVR4,A
MOVR3,A
CRC1:
MOVA,R1
XRLA,R3
MOVR2,#08H
CRC8:
CLRC
MOVA,R4
RRCA
MOVA,R3
JNCCRC10
XRLA,#01H
XRLA,#0A0H
CRC10:
DJNZR2,CRC8
INCR1
DJNZCRC1
RET
LRC的生成
纵向冗余校验(LRC)为一个字节,含有8位二进制值。
LRC由发送设备计算,并附加LRC到报文。
接收设备在接收文时计算LRC,并将计算的结果与在LRC接收到的实际值相比较,如果两个值不相等,则结果为错。
LRC的计算,对报文中的所有的连续8位字节相加,忽略任何进位,然后求出其二进制补码。
LRC为一个8位域,那么每个会导致值大于255新的相加只是简单的将域的值在零”回绕”。
因为没有第9位,进位被自动放弃。
生成一个LRC的过程为:
1.不包括起始”冒号”和结束CRLF的报文中的所有字节相加到一个8位域,故此进位被丢弃。
2.从FF(全1)十六进制中减去域的最终值,产生1的补码(二进制反码)。
3.加1产生二进制补码.
将LRC置于报文当8位LRC(2个ASCII字符)在报文中传送时,高位字符首先发送,然后是低位字符。
例如,如果LRC值为十六进制61(01100001):
例:
下面给出了执行生成LRC的C语言函数。
函数带有两个参数:
unsignedchar*auchMsg;指向含有用于生成LRC的二进制数据报文缓冲区的指针,
unsignedshortusDataLen;报文缓冲区的字节数.
LRC生成函数
staticunsignedcharLRC(auchMsg,usDataLen)/*函数返回unsignedchar类型的LRC结果*/
unsignedchar*auchMsg;/*要计算LRC的报文*/
unsignedshortusDataLen;/*报文的字节数*/
unsignedcharuchLRC=0;/*LRC初始化*/
while(usDataLen--)/*完成整个报文缓冲区*/
uchLRC+=*auchMsg++;/*缓冲区字节相加,无进位*/
return((unsignedchar)(-((char)uchLRC)));/*返回二进制补码*/
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1