CRC远没有网上所说那么简单。
首先我们有一个数据流,也就是你需要校验的数据,可以是N BIT,一般我们常用的4,8,16,32,128 BIT,这里我取几个8的倍数,是因为我们的硬件以二进制为基础,所以在存取数据比较容易,无须充填位数来满足寄存器的要求。你用13,17,29 BIT也可以。
有一点你要记住的是,你选用几个BIT,那么在CRC编码就需要移位几次,8就是移位8次,13就是移位13次,128就是移位128次。
我们看看目前为止的CRC的标准或者行规有:
CRC8 = X8+X5+X4+1
CRC16 = X16+X15+X5+1
CRC12 = X12+X11+X3+X2+1
CRC32 = X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1
CRC-CCITT=X16+X12+X5+1
需要说明的:CRC后所带的数字就是CRC的位数,它与你的数据流是无关的。
CRC8,8位CRC校验。
CRC16,16位CRC校验。
CRC12,12位CRC校验。
CRC32,32位CRC校验。
CRC-CCITT,16位CRC校验。
我们看到5个多项式,它的意义其实就是给出一个与数据流进行异或运算的初始值,
当然你可以更改这个值。比如:
CRC8 = X8+X5+X4+1 》》》 CRC8 = X7+X3+X2+1
CRC16 = X16+X15+X5+1 》》》CRC16 = X15+X5+1
只要你做出的东西是相互连接,不用与其他的机器对接,推荐保密部门使用该方法。
如果修改上述的多项式,那么别人在分析你传送来的数据时,该全部是错码。
补一下课:
异或操作:
RESULT
0 0 0
0 1 1
1 0 1
1 1 0
leasor 发表于 2007-4-5 23:38
2楼: 可靠棵
CRC8 = X8+X5+X4+1
X8 表示第7位需要异或运算
X5 表示第4位需要异或运算
X4 表示第3位需要异或运算
1 表示第0位需要异或运算
如图,首先将CRC所有位清0。
假设我们的数据流为8位,数据流 == 00000011,并且从0位(LSB)开始送入CRC中,则有:
1)1000 1 100
1)1100 1 010
0)0110 0 101
0)1011 1 110
0)0101 1 111
0)1010 0 011
0)1101 1 101
0)1110 0 010
我们看看这个数:
1110 0010 == 0XE2 --- 结果是错的
0100 0111 == 0X47 --- CRC
leasor 发表于 2007-4-5 23:44
3楼: AAA
CRC16 = X16+X15+X5+1
X16 表示第15位需要异或运算
X15 表示第14位需要异或运算
X5 表示第4位需要异或运算
1 表示第0位需要异或运算
如图,首先将CRC所有位清0。
假设我们的数据流为8位,数据流 == 00000011,并且从0位(LSB)开始送入CRC中,则有:
1)10000 1000000000 1
1)01000 0100000000 0
0)00100 0010000000 0
0)00010 0001000000 0
0)00001 0000100000 0
0)00000 1000010000 0
0)00000 0100001000 0
0)00000 0010000100 0
CRC
0001 0000 1000 0000 == 0X1080
leasor 发表于 2007-4-5 23:44
4楼: DDDD
CRC12 = X12+X11+X3+X2+1
CRC32 = X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1
以上述两个例子,自行推倒。
xwj 发表于 2007-4-5 23:46
5楼: 呵呵,楼上先给我讲讲用^移位8(16)次模2除法是怎么回事好吗?
为什么和十进制、十六进制的除法算出来的模是不同的呢???
呵呵,上次别人发贴偶仔细一算,确实越算越糊涂了:-)
leasor 发表于 2007-4-5 23:48
6楼: DDDDD
CRC-CCITT=X16+X12+X5+1
比较特殊,所以再说说
CRC-CCITT 不同于 CRC-16在于它是个反相的CRC-16,所谓反相的意义指从第7位(MSB)开始移入CRC移位寄存器,
如图,首先将CRC所有位清0。
假设我们的数据流为8位,数据流 == 00000011,并且从7位(MSB)开始送入CRC中,则有:
0)00000 0000000 0000
0)00000 0000000 0000
0)00000 0000000 0000
0)00000 0000000 0000
0)00000 0000000 0000
0)00000 0000000 0000
1)10000 1000000 1000
1)11000 1100000 1100
CRC
0011 0000 0110 0011 == 0X3063
leasor 发表于 2007-4-5 23:48
7楼: JJJ
请教高手:
全字节和半字节查表CRC的工作原理的不同在那里?谢谢!
leasor 发表于 2007-4-5 23:57
8楼: 别急5楼,我来了
我觉得没有任何问题哦!
十进制数除数/ 16 == 十进制的商,将除数和商换成二进制,比对结果
你要问的是不是左右移位的问题?
右移1位的结果:11001010 》》1100101,原来的数/2
左移1位的结果:11001010 》》110010100,原来的数*2
leasor 发表于 2007-4-6 00:02
9楼: see you later
如果原来的数不能被2整除的话,好像不能适用移位的方法。
如果涉及好二进制小数的话,有一个误区:
(二进制数)11.11
(十进制数)3.75
这里0.1 == 0.5 ,0.01 == 0.25
希望对你有帮助!我下了,明天见
xwj 发表于 2007-4-6 00:13
10楼: 不是的,比如0x0001的CRC是1021,但用计算器什么算都不是这个数啊
比如按这个多项式:CRC-CCITT=X16+X12+X5+1
0x0001 的CRC=0x1021
0x0003 的CRC=0x3063
用什么杨的数学公式能得到这个值呢???
yewuyi 发表于 2007-4-6 08:30
11楼: 呵呵,LZ只是负责转贴
签名:
lixun00 发表于 2007-4-6 09:30
12楼: 如下,
unsigned int cal_crc(unsigned char *pt, unsigned char len)
{
unsigned char i;
unsigned int crc=0;
unsigned char *ptr = pt;
while(len--!=0)
{
for(i=0x80; i!=0; i/=2)
{
if((crc&0x8000)!=0)
{
crc*=2; crc^=0x1021;
} /* 余式CRC 乘以2 再求CRC */
else
crc*=2;
if((*ptr&i)!=0)
crc^=0x1021; /* 再加上本位的CRC */
}
ptr++;
}
return(crc);
}
leasor 发表于 2007-4-7 11:44
13楼: dddd
让我研究研究答复你~~~~~~~~~
lixun00 发表于 2007-4-7 13:35
14楼: 这里讲的很清楚,不清楚的可以看下高等代数
http://bbs.21ic.com/upfiles/img/20074/20074713353824.pdf
hotpower 发表于 2007-4-7 14:24
15楼: 倒塌了~~~不会CRC算法,只知道结果~~~~
汽车电子失踪了~~~
给我的网站被黑了~~~赶紧备份,因为我自己都没保留~~~
还好,这黑客还有良心~~~
这个程序是我研究CRC多年的总结笔记吧,实际内含加密和解密.
把CRC应用引伸了一节~~~
"计算"实际是加密过程.
"还原"实际是解密过程.
CRC/PEC网上通用计数器
hotpower 发表于 2007-4-7 14:46
16楼: 楼主给的我真算不出来,只会算xwj给出的结果
xwj 发表于 2007-4-8 23:34
17楼: 研究出来了:
对于但字节,CRC结果不是模,而是0x11021-左移16位的模
既:
CRC(n)=0x11021-(n<<16)%0x11021;
或
CRC(n)=0x11021-(n*0x10000)%0x11021;
这样,对于有乘加指令的DSP就可以不用查表而 一步算出答案 了
对于多字节的结果不对,还得继续研究下合并规律的简化...
xwj 发表于 2007-4-8 23:40
18楼: PS: hotpower的程序可以简化了,不用一步步移位了
呵呵
xwj 发表于 2007-4-9 00:11
19楼: 呵呵,都怪资料不准确,害我去由运算结果反推算法,真是晕死了
研究这个主要是因为看到(找到)的程序大家不管是用什么CPU:电脑、单片机、DSP,写的CRC程序都是用的循环移位异或的方式,也就是模2除法。
这个模2除法是针对没有除法器的CPU搞出来的用加减异或指令完成的除法算法啊,只有没有除法器的CPU才会这样算啊!那个效率有多么的低是可想而知的!
而我们实际使用的CPU大多数是有乘法器和除法器的,就算没有用查表也会快很多很多倍,而我看到所有的CRC算法大家都是循环移位异或的方式来算,甚至于电脑上的程序都这样算!
这。。。真是食而不化了...
呵呵,偶以后看到这样写CRC程序的一律BS之!
:-)
资料上说CRC的算法是:
16 位的CRC 码产生的规则是先将要发送的二进制序列数左移16 位(既乘以216 )后,
再除以一个多项式,最后所得到的余数既是CRC 码,如式(2-1)式所示,其中B(X)表
示n 位的二进制序列数,G(X)为多项式,Q(X)为整数,R(X)是余数(既CRC 码)。
(X)·2^16 R(X)
---------- = Q(X) + ----------
G(X) G(X)
那CRC的结果应该就是:
CRC(X) = R(X) = ((X)<<16) % G(X);
而实际测试的结果却是:
CRC(X) = R(X) = G(X) - ((X)<<16) % G(X);
应该是:R(X)是余数(CRC 码为 G(X)-R(X),这样整个位串再合并上CRC 码就刚好能被多项式G(X)整除)。
对于CRC-CCITT的多项式:X16+X12+X5+1
就是
CRC(n)=0x11021-(n<<16)%0x11021;
或
CRC(n)=0x11021-(n*0x10000)%0x11021;
唉,CRC的资料只说“R(X)是余数(既CRC 码)”,自己没去仔细看E文资料,就信以为真了,结果全然不对啊,最好竟然去由运算结果反推算法,真是晕死了...
:-(
xwj 发表于 2007-4-9 15:10
21楼: 直接和字节间的关系如下,好像没法进一步化简了:-(
CRC(ABCD)={ CRC(AB)<<8 ^ CRC( (CRC(AB)/256 ^ CD ) ) } & 0xffff;
高字节CRC CRC:(高字节CRC低8位^低字节)
只取低16位,高位截除
--由于有溢出,截除的高位不知道,所以用多项式验证整个16位数得不到正确的结果了
好像没法进一步化简了,
只好按字节执行循环运算了 :-(
xwj 发表于 2007-4-9 15:52
22楼: 整理了下多字节CRC步骤
多字节CRC步骤
字节3
字节2
字节1
字节0
说明
AB
CD
源数据
1
AB
00
00
高8位左移16位
2
CRC(AB)高
CRC(AB)低
高8位取模运算
3
CD
00
00
低8位左移16位
4
CD = CRC(AB)高 ^ CD
CD对应CRC(AB)高
5
CRC(CD)高
CRC(CD)低
低8位取模运算
6
CRC(ABCD)高
CRC(ABCD)低
高低CRC结果合并
7
CRC(ABCD) &= 0xffff;
结果只取低16位
下一字节运算 依次循环步骤3~7,直到位串结束。
--由于结果只取低16位,高位截除,而多字节运算是必然有溢出,截除的高位不知道,所以用多项式验证整个16位数得不到正确的结果了
//程序如下,全局int变量crc中保留了前一次的CRC运算结果:
uint table_crc(uchar *ptr,uchar len) // 字节查表法求CRC,查表操作就是一次CRC运算
{
uchar da;
while(len--!=0)
{
da=(uchar) (crc/256); // 以8 位二进制数暂存CRC 的高8 位
crc<<=8; // 左移8 位
crc^=crc_ta[da^*ptr]; // 高字节和当前数据XOR 再查表
ptr++;
}
return(crc);
}