前言:1、想直接用CRC的可以直接看程序,想了解原理,或者写程序的原理的。可以看按顺序看整篇 2、模二除法没有深入研究,所以模二除法待验证,也不去验证了,我花了一个月的业余时间写。3、本文只进行了16位的验证。个人建议会用就行。
目录
什么是CRC
CRC(Cyclic Redundancy Check),即循环冗余校核,是一种根据网络数据包或电脑文件等数据产生简短固定位数校核码的快速算法,主要用来检测或校核数据传输或者保存后可能出现的错误。CRC利用除法及余数的原理,实现错误侦测的功能,具有原理清晰、实现简单等优点。
个人理解:就是CRC校验就是保证传输的内容是不是错了,是不是收到干扰。
发送方:如传输的0x32.校验一次记为校验1,校验码一并传输;即传输为0x32 校验1。
最高位在传输的时候收到干扰变成了1,接收到的为0xB2 校验1;那么接下来接收方就判定产生错误
接收方:再次校验接收回来的数据0x32,记为校验2。如果接收的数据0x32正确,校验2与校验1一样,则传输正确,如果不一样,要么是校验1传输出问题要么是0x32出问题。此时判定不需要此帧数据
CRC的计算原理
异或计算和直观图:先看这个
模二计算方法:再理解这个
除了以上的计算,还有就是用数据串右移多项式的位宽进行(记为数据串1)除去多项式,除到数据串1的最后一位。
如上图:现假设选择的 CRC生成多项式为 G( X) = X^4 + X^3 + 1,要求出二进制序列 10110011的 CRC校验码。下面是具体的计算过程 :
①将多项式转化为二进制序列,由 G( X) = X^4 + X^3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001
②多项式的位数位5,则在数据帧的后面加上4位0,数据帧变为 101100110000,然后使用模2除法除以除数 11001,得到余数
③将计算出来的CRC校验码添加在原始帧的后面,真正的数据帧为101100110100,再把这个数据帧发送到接收端。
④接收端收到数据帧后,用上面选定的除数,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。
此处引用一个博主:CRC校验原理及步骤_erci_fc2336的博客-CSDN博客_crc校验
程序原理:
数据串都放进8位型数组u8 data[100],确定好多项式,并且去除最高位。
为啥去除最高位?因为方便运算,并且在程序处理的时候已经考虑最高位的处理。结合多项式表比如多项式为CRC16xmodem,二进制为1 0001 0000 0010 0001,省去最高位为0x1021。
省去的最高位在程序中补偿:因为是从数据串的首位异或二进制为1 0001 0000 0010 0001,如果数据串首位为1就异或1 0001 0000 0010 0001,此时数据串的首位1被异或清零相当于移除掉数据串首位,并且异或去掉多项式首位的效果;否则首位为0直接移除掉。
位处理型:按照异或计算和直观图视频里的原理
1、提取出数据串的位数与多项式省去最高位后一样(8 、16、32),记为datax,位数记为L
2、多项式省去最高位后记为ployint;
3、判断首位是否为1,为1就右移一位datax,从数据串中补上L=L+1的位,进行异或。不为1直接左移,再补上L=L+1。
4直到L为数据串的位长度,后再补ployint位宽长度的0;假设数据串为10字节,ployint为0x1021位宽是16,在10*8个L长度之后开始补0,直到L补到10*8+16结束所有运算,此时的异或结果就是校验值。
位处理程序补充:但是程序我写的是CRC16Xmodem多项式。
void InvertUint8(unsigned char *DesBuf, unsigned char *SrcBuf)//字节取反
{
int i;
unsigned char temp = 0;
for(i = 0; i < 8; i++)
{
if(SrcBuf[0] & (1 << i))
{
temp |= 1<<(7-i);
}
}
DesBuf[0] = temp;
}
void InvertUint16(unsigned short *DesBuf, unsigned short *SrcBuf)//16位取反
{
int i;
unsigned short temp = 0;
for(i = 0; i < 16; i++)
{
if(SrcBuf[0] & (1 << i))
{
temp |= 1<<(15 - i);
}
}
DesBuf[0] = temp;
}
unsigned int wei_chuli(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned short wCRCin = 0x0000;
unsigned short wCPoly = 0x1021;
unsigned short wChar = puchMsg[0]<<8|puchMsg[1]; //如果改成其他校验模式,改这三个的类型和值就行了。
int i=0;
unsigned char t=0;//记处理当前字节的位
unsigned char temp=0;//记补位补0还是1
unsigned char now_zijie_num=1;//记当前第几字节
//for(i=0;i<usDataLen;i++)InvertUint8(puchMsg+i,puchMsg+i); //如果需要输入反转,应把数组每个字节不动位置,每个字节的位进行反转
puchMsg+=1; //指针指向第二个字节
wCRCin^=wChar;//开始输入异或
while (now_zijie_num<=usDataLen)//直到字节数都处理完,当前字节数大于usDataLen
{
printf("开始处理第%d字符: \r\n",now_zijie_num);
for(i = 0x80,t=0; i >0; i=i>>1,t++) //用来提取当前字节的后第二位字节的位
{ printf("处理第%d字符%d位: \r\n",now_zijie_num,t);
if(wCRCin & 0x8000) //判断当前字节最高位
{
// printf("处理前 %x \r\n",wCRCin);
// printf("异或值 %x \r\n",wCPoly);
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //只要没到倒数第二个字节,就正常补位,到了只能补零。相当于数据串整体移位多项式位宽。
{
wCRCin=(wCRCin << 1)+(temp=*(puchMsg+1)&i?1:0);//公式内的左移是单目运算,实际的wCRCin已经移位。并且补上前字节的后第二位字节的位
printf("当前在%d字节\r\n",now_zijie_num);
printf("补后第二字节补后第二字节%x字节的%d位补了%d",*(puchMsg+1),t,temp);
}
else {wCRCin=(wCRCin << 1); //处理完数据传只补零
printf("当前在%d字节\r\n",now_zijie_num);
printf("只补0后");
}
printf("处理前 %x \r\n",wCRCin);
printf("异或值 %x \r\n",wCPoly);
wCRCin = wCRCin^ wCPoly;
printf("处理结果%x \r\n",wCRCin);
}
else
{
wCRCin = wCRCin << 1;
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //只要没到倒数第二个字节,就正常补位,到了只能补零。
{ //
wCRCin=wCRCin+(temp=*(puchMsg+1)&i?1:0);
printf("当前在%d字节\r\n",now_zijie_num);
printf("纯位移,补后第二字节%x字节的%d位补了%d",*(puchMsg+1),t,temp);
}
else { printf("当前在%d字节\r\n",now_zijie_num);
printf("纯位移,只补0");}
printf("单纯移位之后%x \r\n",wCRCin);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //如果数据没完,每次补完8位,指针++
puchMsg++;
now_zijie_num++;
}
//InvertUint16(&wCRCin, &wCRCin);//如果输出需要反转
return (wCRCin) ; //输出需要异或可以在这里处理。
}
字节处理型:
PS:以下的加号都是异或
把按字节排列的数据流表示成数学多项式,设数据流为BYTE[n]BYTE[n-1]BYTE[n-2]、、、BYTE[1]BYTE[0],表示成数学表达式为BYTE[n]×256^n+BYTE[n-1]×256^(n-1)
+...+BYTE[1]*256+BYTE[0],在这里+表示为异或运算。设生成多项式为G17(17bit),CRC码为CRC16。
则,CRC16=(BYTE[n]×256^n+BYTE[n-1]×256^(n-1)+...+BYTE[1]×256+BYTE[0])×256^2/G17,即数据流左移16位,再除以生成多项式G17。
先变换BYTE[n-1]、BYTE[n-1]扩大后的形式,
CRC16
=BYTE[n]×256^n×256^2/G17+BYTE[n-1]256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=(Z[n]+Y[n]/G17)×256^n+BYTE[n-1]×256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{Y[n]×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{(YH8[n]×256+YHL[n])×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
这样就推导出,BYTE[n-1]字节的CRC校验码为{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17},即上一字节CRC校验码Y[n]的高8位(YH8[n])与本字节BYTE[n-1]异或,
该结果单独计算CRC校验码(即单字节的16位CRC校验码,对单字节可建立表格,预先生成对应的16位CRC校验码),所得的CRC校验码与上一字节CRC校验码Y[n]的低8位(YL8[n])
乘以256(即左移8位)异或。然后依次逐个字节求出CRC,直到BYTE[0]。
字节型算法的一般描述为:本字节的CRC码,等于上一字节CRC码的低8位左移8位,与上一字节CRC右移8位同本字节异或后所得的CRC码异或。
以上是引用的内容;但是我觉得紫色的没有完整,或者没有准确描述。
个人分析:BYTE[n-1]字节的CRC校验码为{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17}
可以再分解为{【YHL[n]+(YH8[n]+BYTE[n-1])×256】x256/G17}
结论:所以可以轻松发现:BYTE[n-1]字节的CRC校验码为上一个BYTE[n]字节校验码的高8位,异或BYTE[n-1]字节左移8,再异或上一个校验码的低八。也就是说上个字节校验码的高位异或BYTE[n-1]字节再进行异或处理,又得到了BYTE[n-1]校验值。
>>补充:关于z[n]的出现,可以考虑到模二除法,BYTE[n]=商*多项式+余数【PS余数就是该字节的CRC校验值】。所以z[n]=商*多项式,还留着z[n]是为了保证等式的完整性。
为什么会找以上理论呢?因为每个人都只发程序文章,而程序为什么这样写,没有人提到,理论和程序联系存在部分断层。接下来就是程序补充了。
字节型处理程序补充:使用的是CRC16-Xmodem算法,可以看下面的多项式表。
unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned short wCRCin = 0x0000;
unsigned short wCPoly = 0x1021;
unsigned char wChar = 0;
int i,t=0;
while (usDataLen--)
{
wChar = *(puchMsg++);
t++;
printf("第%d个数: %x \r\n",t,wChar);
/*********************
为什么要用每个字节的校验码异或下一个字节,这就需要刚才的字节型处理理论依据了
*********************************/
wCRCin ^= (wChar << 8);/******* 这个哦******/
if(t==3)
printf("%x \r\n",wCRCin);
for(i = 0; i < 8; i++)
{ printf("处理第%d字符%d位: \r\n",t,i);
if(wCRCin & 0x8000)
{
printf("处理前 %x \r\n",wCRCin);
printf("异或值 %x \r\n",wCPoly);
wCRCin = (wCRCin << 1) ^ wCPoly;
printf("处理结果%x \r\n",wCRCin);
}
else
{
wCRCin = wCRCin << 1;
printf("单纯移位之后%x \r\n",wCRCin);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
}
return (wCRCin) ;
}
根据以上程序可以提供一个直接表观察数据处理:如二进制处理测试数据表1,可以不用看除了多项式表以下的都不用看,因为需要增加学习量了,有兴趣可以看,研究就可以动手写一下,试一下看一下如下下下面的。
此处引用了百度:CRC校验字节型算法查表法解读 - 百度文库
多项式表
此表有引用:
C# 实现CRC校验(循环冗余校验)CRC-8位/16位/32位等参数模型_猿长大人的博客-CSDN博客_c# crc校验
二进制处理测试数据表1
描述此表的目的:·观察数据变化,单字节处理和多字节处理的变化。
之前思考一个问题,就是单字节处理与多项式处理8次,但是双字节也会处理8次,而且后面第二个字节也会参与处理,所以观察单字节处理完结果和双字节处理第一个字节完是否一样?
表左边使用的是0x32,表右边是0x32 0x03;单字节处理和双字节处理。
观察第七位也就是第一个字节最后一位处理完成。
第1个数: 0011 0010 处理第1字符0位: 单纯 0110 0100 0000 0000
处理第1字符1位: 单纯 1100 1000 0000 0000
处理第1字符2位: 处理前1100 1000 0000 0000 异或值0001 0000 0010 0001 处结果1000 0000 0010 0001
处理第1字符3位: 处理前1000 0000 0010 0001 异或值0001 0000 0010 0001 处结果0001 0000 0110 0011
处理第1字符4位: 单纯 0010 0000 1100 0110
处理第1字符5位: 单纯 0100 0001 1000 1100
处理第1字符6位: 单纯 1000 0011 0001 1000
处理第1字符7位: 处理前1000 0011 0001 1000 异或值0001 0000 0010 0001 处理值0001 0110 0001 0001 校验值为0001 0110 0001 0001 可以观察这里
| 第1个数: 0011 0010 0000 0011 处理第1字符0位: 纯移0110 0100 0000 0110 0000 0000 0000 0000
处理第1字符1位: 纯移1100 1000 0000 1100 0000 0000 0000 0000
处理第1字符2位: 处理前1100 1000 0000 1100 0000 0000 0000 0000 异或值0001 0000 0010 00010000 0000 0000 0000 处结果1000 0000 0011 1001 0000 0000 0000 0000
处理第1字符3位: 处理前1000 0000 0011 1001 0000 0000 0000 0000 异或值0001 0000 0010 0001 0000 0000 0000 0000 处结果0001 0000 0101 0011 0000 0000 0000 0000
处理第1字符4位: 纯移0010 0000 1010 0110 0000 0000 0000 0000
处理第1字符5位: 纯移0100 0001 0100 1100 0000 0000 0000 0000
处理第1字符6位: 纯移1000 0010 1001 1000 0000 0000 0000 0000
处理第1字符7位: 处理前1000 0010 1001 1000 0000 0000 0000 0000 异或值0001 0000 0010 0001 0000 0000 0000 0000 处结果0001 0101 0001 0001 0000 0000 0000 0000 校验值为0001 010 0001 0001
处理第2字符8位: 纯移0010 1010 0010 0010 0000 0000 0000 0000
处理第2字符9位: 纯移0101 0100 0100 0100 0000 0000 0000 0000
处理第2字符10位: 纯移1010 1000 1000 1000 0000 0000 0000 0000
处理第2字符11位: 处理前1010 1000 1000 1000 0000 0000 0000 0000 异或值0001 0000 0010 0001 0000 0000 0000 0000 处结果0100 0001 0011 0001 0000 0000 0000 0000
处理第2字符12位: 纯移1000 0010 0110 0010 0000 0000 0000 0000
处理第2字符13位: 处理前1000 0010 0110 0010 0000 0000 0000 0000 异或值0001 0000 0010 0001 0000 0000 0000 0000 处结果0001 0100 1110 0101 0000 0000 0000 0000
处理第2字符14位: 纯移0010 1001 1100 1010 0000 0000 0000 0000
处理第2字符15位: 纯移101 0011 1001 0100 0000 0000 0000 0000
最终结果101 0011 1001 0100 0000 0000 0000 0000 |
如上表的测试是用VC2010程序测试出来的,附带程序。
主程序:
int main()
{ int z=0;
unsigned char a[10]={0xe2, 0x03 ,0x97 ,0x01}; //e2 03 97 01
unsigned char a1[10]={0x32, 0x03 ,0x08 ,0x81};//32 03 08 81
//z=CRC16_XMODEM(a1, 2);
//printf("校验值: %x",z);
//z=CRC16_XMODEM_test(0x3203, 1);
//CRC_16_desplay_2jinzhi(a1,1); //表左边用的
CRC_16_desplay_2jinzhi_2(0x3203, 1);//表右边用的
printf("TEST_校验值: %x",z);
}
表左用的程序
int CRC_16_desplay_2jinzhi(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned short wCRCin = 0x0000;
unsigned short wCPoly = 0x1021;
unsigned char wChar = 0;
int i,t=0;
char buff[50]={0};
//itoa(x,buff,2);
while (usDataLen--)
{
wChar = *(puchMsg++);
t++;
itoa(wChar,buff,2);
printf("第%d个数: %s \r\n",t,buff);
wCRCin ^= (wChar << 8);
if(t==3){
itoa(wCRCin,buff,2);
printf("%s \r\n",buff);
}
for(i = 0; i < 8; i++)
{ printf("处理第%d字符%d位: \r\n",t,i);
if(wCRCin & 0x8000)
{
itoa(wCRCin,buff,2);
printf("处理前。。。。%s \r\n",buff);
itoa(wCPoly,buff,2);
printf("异或值。。。 000%s \r\n",buff);
wCRCin = (wCRCin << 1) ^ wCPoly;
itoa(wCRCin,buff,2);
printf("处理结果。 %s \r\n",buff);
}
else
{
wCRCin = wCRCin << 1;
itoa(wCRCin,buff,2);
printf("单纯移位之后 %s \r\n",buff);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
}
itoa(wCRCin,buff,2);
printf("最终结果%s \r\n\r\n",buff);
return (wCRCin) ;
}
表右用的程序
int CRC_16_desplay_2jinzhi_2(unsigned short puchMsg, unsigned int usDataLen)
{
unsigned int wCRCin = 0x00000000;
unsigned int wCPoly = 0x10210000;
unsigned int wChar = 0;
int i,t=0;
char buff[50]={0};
//itoa(x,buff,2);
while (usDataLen--)
{
wChar =puchMsg;
t++;
itoa(wChar,buff,2);
printf("第%d个数: %s \r\n",t,buff);
wCRCin ^= (wChar << 16);
if(t==3){
itoa(wCRCin,buff,2);
printf("%s \r\n",buff);
}
for(i = 0; i < 16; i++)
{ printf("处理第%d字符%d位: \r\n",t,i);
if(wCRCin & 0x80000000)
{
itoa(wCRCin,buff,2);
printf("处理前%s \r\n",buff);
itoa(wCPoly,buff,2);
printf("异或值000%s \r\n",buff);
wCRCin = (wCRCin << 1) ^ wCPoly;
itoa(wCRCin,buff,2);
printf("处结果%s \r\n",buff);
}
else
{
wCRCin = wCRCin << 1;
itoa(wCRCin,buff,2);
printf("纯移%s \r\n",buff);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
}
itoa(wCRCin,buff,2);
printf("最终结果%s\r\n\r\n",buff);
return (wCRCin) ;
}
字节处理型型和位处理型对比验证
可以先通过网上的校验计算做对比:CRC(循环冗余校验)在线计算_ip33.com
本人使用VC2010学习版做的验证程序如下,也可以用在单片机上。
#include<stdio.h>
#include<stdlib.h>
unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen);
unsigned short CRC16_XMODEM_test(unsigned short puchMsg, unsigned int usDataLen);
int CRC_16_desplay_2jinzhi(unsigned char *puchMsg, unsigned int usDataLen);
int CRC_16_desplay_2jinzhi_2(unsigned short puchMsg, unsigned int usDataLen);
unsigned int wei_chuli(unsigned char *puchMsg, unsigned int usDataLen);
int main()
{ int z=0,z1=0;
unsigned char a[10]={0xe2, 0x03 ,0x97 ,0x01}; //e2 03 97 01
unsigned char a1[10]={0x32, 0x03 ,0x08 ,0x81,0x32,0x99,0x01,0x12};//32 03 08 81 32 99 01 12
z1=CRC16_XMODEM(a1, 4);
//z=CRC16_XMODEM_test(0x3203, 1);
//CRC_16_desplay_2jinzhi(a1,1);
//CRC_16_desplay_2jinzhi_2(0x3203, 1);
z=wei_chuli(a1,4 );
printf("校验值: %x",z1);
printf("TEST_校验值: %x",z);
}
unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned short wCRCin = 0x0000;
unsigned short wCPoly = 0x1021;
unsigned char wChar = 0;
int i,t=0;
while (usDataLen--)
{
wChar = *(puchMsg++);
t++;
printf("第%d个数: %x \r\n",t,wChar);
wCRCin ^= (wChar << 8);
if(t==3)
printf("%x \r\n",wCRCin);
for(i = 0; i < 8; i++)
{ printf("处理第%d字符%d位: \r\n",t,i);
if(wCRCin & 0x8000)
{
printf("处理前 %x \r\n",wCRCin);
printf("异或值 %x \r\n",wCPoly);
wCRCin = (wCRCin << 1) ^ wCPoly;
printf("处理结果%x \r\n",wCRCin);
}
else
{
wCRCin = wCRCin << 1;
printf("单纯移位之后%x \r\n",wCRCin);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
}
return (wCRCin) ;
}
unsigned int wei_chuli(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned short wCRCin = 0x0000;
unsigned short wCPoly = 0x1021;
unsigned short wChar = puchMsg[0]<<8|puchMsg[1]; //如果改成其他校验模式,改这三个的类型和值就行了。
int i=0;
unsigned char t=0;//记处理当前字节的位
unsigned char temp=0;//记补位补0还是1
unsigned char now_zijie_num=1;//记当前第几字节
//for(i=0;i<usDataLen;i++)InvertUint8(puchMsg+i,puchMsg+i); //如果需要输入反转,应把数组每个字节不动位置,每个字节的位进行反转
puchMsg+=1; //指针指向第二个字节
wCRCin^=wChar;//开始输入异或
while (now_zijie_num<=usDataLen)//直到字节数都处理完,当前字节数大于usDataLen
{
printf("开始处理第%d字符: \r\n",now_zijie_num);
for(i = 0x80,t=0; i >0; i=i>>1,t++) //用来提取当前字节的后第二位字节的位
{ printf("处理第%d字符%d位: \r\n",now_zijie_num,t);
if(wCRCin & 0x8000) //判断当前字节最高位
{
// printf("处理前 %x \r\n",wCRCin);
// printf("异或值 %x \r\n",wCPoly);
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //只要没到倒数第二个字节,就正常补位,到了只能补零。相当于数据串整体移位多项式位宽。
{
wCRCin=(wCRCin << 1)+(temp=*(puchMsg+1)&i?1:0);//公式内的左移是单目运算,实际的wCRCin已经移位。并且补上前字节的后第二位字节的位
printf("当前在%d字节\r\n",now_zijie_num);
printf("补后第二字节补后第二字节%x字节的%d位补了%d",*(puchMsg+1),t,temp);
}
else {wCRCin=(wCRCin << 1); //处理完数据传只补零
printf("当前在%d字节\r\n",now_zijie_num);
printf("只补0后");
}
printf("处理前 %x \r\n",wCRCin);
printf("异或值 %x \r\n",wCPoly);
wCRCin = wCRCin^ wCPoly;
printf("处理结果%x \r\n",wCRCin);
}
else
{
wCRCin = wCRCin << 1;
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //只要没到倒数第二个字节,就正常补位,到了只能补零。
{ //
wCRCin=wCRCin+(temp=*(puchMsg+1)&i?1:0);
printf("当前在%d字节\r\n",now_zijie_num);
printf("纯位移,补后第二字节%x字节的%d位补了%d",*(puchMsg+1),t,temp);
}
else { printf("当前在%d字节\r\n",now_zijie_num);
printf("纯位移,只补0");}
printf("单纯移位之后%x \r\n",wCRCin);
}
printf(" \r\n",wCRCin);
printf(" \r\n",wCRCin);
}
if(now_zijie_num<=usDataLen-sizeof(wCPoly)) //如果数据没完,每次补完8位,指针++
puchMsg++;
now_zijie_num++;
}
//InvertUint16(&wCRCin, &wCRCin);//如果输出需要反转
return (wCRCin) ; //输出需要异或可以在这里处理。
}
得到比较表:
左边为z1=CRC16_XMODEM(a1, 4);字节型处理
右边为z=wei_chuli(a1,4 );位型处理,位处理可以进行手算进行对比
第1个数: 32 处理第1字符0位: 单纯移位之后6400
处理第1字符1位: 单纯移位之后c800
处理第1字符2位: 处理前 c800 异或值 1021 处理结果8021
处理第1字符3位: 处理前 8021 异或值 1021 处理结果1063
处理第1字符4位: 单纯移位之后20c6
处理第1字符5位: 单纯移位之后418c
处理第1字符6位: 单纯移位之后8318
处理第1字符7位: 处理前 8318 异或值 1021 处理结果1611
第2个数: 3 处理第2字符0位: 单纯移位之后2a22
处理第2字符1位: 单纯移位之后5444
处理第2字符2位: 单纯移位之后a888
处理第2字符3位: 处理前 a888 异或值 1021 处理结果4131
处理第2字符4位: 单纯移位之后8262
处理第2字符5位: 处理前 8262 异或值 1021 处理结果14e5
处理第2字符6位: 单纯移位之后29ca
处理第2字符7位: 单纯移位之后5394
第3个数: 8 5b94 处理第3字符0位: 单纯移位之后b728
处理第3字符1位: 处理前 b728 异或值 1021 处理结果7e71
处理第3字符2位: 单纯移位之后fce2
处理第3字符3位: 处理前 fce2 异或值 1021 处理结果e9e5
处理第3字符4位: 处理前 e9e5 异或值 1021 处理结果c3eb
处理第3字符5位: 处理前 c3eb 异或值 1021 处理结果97f7
处理第3字符6位: 处理前 97f7 异或值 1021 处理结果3fcf
处理第3字符7位: 单纯移位之后7f9e
第4个数: 81 处理第4字符0位: 处理前 fe9e 异或值 1021 处理结果ed1d
处理第4字符1位: 处理前 ed1d 异或值 1021 处理结果ca1b
处理第4字符2位: 处理前 ca1b 异或值 1021 处理结果8417
处理第4字符3位: 处理前 8417 异或值 1021 处理结果180f
处理第4字符4位: 单纯移位之后301e
处理第4字符5位: 单纯移位之后603c
处理第4字符6位: 单纯移位之后c078
处理第4字符7位: 处理前 c078 异或值 1021 处理结果90d1
校验值: 90d1
| 开始处理第1字符: 处理第1字符0位: 当前在1字节 纯位移,补后第二字节8字节的0位补了0单纯移位之后6406
处理第1字符1位: 当前在1字节 纯位移,补后第二字节8字节的1位补了0单纯移位之后c80c
处理第1字符2位: 当前在1字节 补后第二字节补后第二字节8字节的2位补了0处理前 9018 异或值 1021 处理结果8039
处理第1字符3位: 当前在1字节 补后第二字节补后第二字节8字节的3位补了0处理前 72 异或值 1021 处理结果1053
处理第1字符4位: 当前在1字节 纯位移,补后第二字节8字节的4位补了1单纯移位之后20a7
处理第1字符5位: 当前在1字节 纯位移,补后第二字节8字节的5位补了0单纯移位之后414e
处理第1字符6位: 当前在1字节 纯位移,补后第二字节8字节的6位补了0单纯移位之后829c
处理第1字符7位: 当前在1字节 补后第二字节补后第二字节8字节的7位补了0处理前 538 异或值 1021 处理结果1519
开始处理第2字符: 处理第2字符0位: 当前在2字节 纯位移,补后第二字节81字节的0位补了1单纯移位之后2a33
处理第2字符1位: 当前在2字节 纯位移,补后第二字节81字节的1位补了0单纯移位之后5466
处理第2字符2位: 当前在2字节 纯位移,补后第二字节81字节的2位补了0单纯移位之后a8cc
处理第2字符3位: 当前在2字节 补后第二字节补后第二字节81字节的3位补了0处理前 5198 异或值 1021 处理结果41b9
处理第2字符4位: 当前在2字节 纯位移,补后第二字节81字节的4位补了0单纯移位之后8372
处理第2字符5位: 当前在2字节 补后第二字节补后第二字节81字节的5位补了0处理前 6e4 异或值 1021 处理结果16c5
处理第2字符6位: 当前在2字节 纯位移,补后第二字节81字节的6位补了0单纯移位之后2d8a
处理第2字符7位: 当前在2字节 纯位移,补后第二字节81字节的7位补了1单纯移位之后5b15
开始处理第3字符: 处理第3字符0位: 当前在3字节 纯位移,只补0单纯移位之后b62a
处理第3字符1位: 当前在3字节 只补0后处理前 6c54 异或值 1021 处理结果7c75
处理第3字符2位: 当前在3字节 纯位移,只补0单纯移位之后f8ea
处理第3字符3位: 当前在3字节 只补0后处理前 f1d4 异或值 1021 处理结果e1f5
处理第3字符4位: 当前在3字节 只补0后处理前 c3ea 异或值 1021 处理结果d3cb
处理第3字符5位: 当前在3字节 只补0后处理前 a796 异或值 1021 处理结果b7b7
处理第3字符6位: 当前在3字节 只补0后处理前 6f6e 异或值 1021 处理结果7f4f
处理第3字符7位: 当前在3字节 纯位移,只补0单纯移位之后fe9e
开始处理第4字符: 处理第4字符0位: 当前在4字节 只补0后处理前 fd3c 异或值 1021 处理结果ed1d
处理第4字符1位: 当前在4字节 只补0后处理前 da3a 异或值 1021 处理结果ca1b
处理第4字符2位: 当前在4字节 只补0后处理前 9436 异或值 1021 处理结果8417
处理第4字符3位: 当前在4字节 只补0后处理前 82e 异或值 1021 处理结果180f
处理第4字符4位: 当前在4字节 纯位移,只补0单纯移位之后301e
处理第4字符5位: 当前在4字节 纯位移,只补0单纯移位之后603c
处理第4字符6位: 当前在4字节 纯位移,只补0单纯移位之后c078
处理第4字符7位: 当前在4字节 只补0后处理前 80f0 异或值 1021 处理结果90d1
|
这是手算表:手算时多项式0x1021在前面最高位要加1即1 0001 0000 0010 0001
数据串为0x32 0x03 0x08 0x81 并且添加多项式位宽个0,即加16个0.
手算的校验结果为0x90d1