CRC源码.docx

上传人:b****0 文档编号:17927524 上传时间:2023-08-05 格式:DOCX 页数:20 大小:25.61KB
下载 相关 举报
CRC源码.docx_第1页
第1页 / 共20页
CRC源码.docx_第2页
第2页 / 共20页
CRC源码.docx_第3页
第3页 / 共20页
CRC源码.docx_第4页
第4页 / 共20页
CRC源码.docx_第5页
第5页 / 共20页
CRC源码.docx_第6页
第6页 / 共20页
CRC源码.docx_第7页
第7页 / 共20页
CRC源码.docx_第8页
第8页 / 共20页
CRC源码.docx_第9页
第9页 / 共20页
CRC源码.docx_第10页
第10页 / 共20页
CRC源码.docx_第11页
第11页 / 共20页
CRC源码.docx_第12页
第12页 / 共20页
CRC源码.docx_第13页
第13页 / 共20页
CRC源码.docx_第14页
第14页 / 共20页
CRC源码.docx_第15页
第15页 / 共20页
CRC源码.docx_第16页
第16页 / 共20页
CRC源码.docx_第17页
第17页 / 共20页
CRC源码.docx_第18页
第18页 / 共20页
CRC源码.docx_第19页
第19页 / 共20页
CRC源码.docx_第20页
第20页 / 共20页
亲,该文档总共20页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

CRC源码.docx

《CRC源码.docx》由会员分享,可在线阅读,更多相关《CRC源码.docx(20页珍藏版)》请在冰点文库上搜索。

CRC源码.docx

CRC源码

CRC-16/CRC-32程序代码

不久前写一程序时要用到CRC-16,但找来找去只在UDDF里找到一个Delphi的CRC-32程序代码,而且是用查表法,虽然说查表法速度快,但256项32位数据我怀疑可能会有输入错误,让人不是那么放心,而我又不知道这个表是怎么算出来的。

后来我又在一本两年前的笔记本里找到一段关于CRC的内容,也不知是从哪里抄来的,还好里面有一段程序代码,是CRC-16的,这段程序正是产生CRC表的,可是这区区几行的程序(基本上与下面的BuilderTable16函数相同)看得我一头雾水,直到这两天才弄明白,并据此推出CRC-32的算法,现将全部程序列在下面,并作一些说明以助于理解,不但要知其然,还要知其所以然嘛:

补充:

为了使这段程序更加实用,我将其整理为几个单元,分别用于Delphi和C++Builder。

包括对数据流TMemoryStream和字符串的处理。

可以在此下载:

3.7KB。

Aug.18-01

//注意:

因最高位一定为“1”,故略去

constunsignedshortcnCRC_16=0x8005;

//CRC-16=X16+X15+X2+X0

constunsignedshortcnCRC_CCITT=0x1021;

//CRC-CCITT=X16+X12+X5+X0,据说这个16位CRC多项式比上一个要好

constunsignedlongcnCRC_32=0x04C10DB7;

//CRC-32=X32+X26+X23+X22+X16+X11+X10+X8+X7+X5+X4+X2+X1+X0

unsignedlongTable_CRC[256];//CRC表

//构造16位CRC表

voidBuildTable16(unsignedshortaPoly)

{

unsignedshorti,j;

unsignedshortnData;

unsignedshortnAccum;

for(i=0;i<256;i++)

{

nData=(unsignedshort)(i<<8);

nAccum=0;

for(j=0;j<8;j++)

{

if((nData^nAccum)&0x8000)

nAccum=(nAccum<<1)^aPoly;

else

nAccum<<=1;

nData<<=1;

}

Table_CRC[i]=(unsignedlong)nAccum;

}

}

//计算16位CRC值,CRC-16或CRC-CCITT

unsignedshortCRC_16(unsignedchar*aData,unsignedlongaSize)

{

unsignedlongi;

unsignedshortnAccum=0;

BuildTable16(cnCRC_16);//orcnCRC_CCITT

for(i=0;i<aSize;i++)

nAccum=(nAccum<<8)^(unsignedshort)Table_CRC[(nAccum>>8)^*aData++];

returnnAccum;

}

//构造32位CRC表

voidBuildTable32(unsignedlongaPoly)

{

unsignedlongi,j;

unsignedlongnData;

unsignedlongnAccum;

for(i=0;i<256;i++)

{

nData=(unsignedlong)(i<<24);

nAccum=0;

for(j=0;j<8;j++)

{

if((nData^nAccum)&0x80000000)

nAccum=(nAccum<<1)^aPoly;

else

nAccum<<=1;

nData<<=1;

}

Table_CRC[i]=nAccum;

}

}

//计算32位CRC-32值

unsignedlongCRC_32(unsignedchar*aData,unsignedlongaSize)

{

unsignedlongi;

unsignedlongnAccum=0;

BuildTable32(cnCRC_32);

for(i=0;i<aSize;i++)

nAccum=(nAccum<<8)^Table_CRC[(nAccum>>24)^*aData++];

returnnAccum;

}

说明:

CRC的计算原理如下(一个字节的简单例子)

110110000000000000000000<-一个字节数据,左移16b

^10001000000100001<-CRC-CCITT多项式,17b

--------------------------

10100000001000010<-中间余数

^10001000000100001

-------------------------

10100000110001100

^10001000000100001

-----------------------

10100011010110100

^10001000000100001

---------------------

10101101001010100

^10001000000100001

-------------------

0100101001110101<-16bCRC

仿此,可推出两个字节数据计算如下:

d为数据,p为项式,a为余数

dddddddddddddddd0000000000000000<-数据D(D1,D0,0,0)

^ppppppppppppppppp<-多项式P

-----------------------------------

...

aaaaaaaaaaaaaaaa0<-第一次的余数A'(A'1,A'0)

^ppppppppppppppppp

--------------------------

...

aaaaaaaaaaaaaaaa<-结果A(A1,A0)

由此与一字节的情况比较,将两个字节分开计算如下:

先算高字节:

dddddddd000000000000000000000000<-D1,0,0,0

^ppppppppppppppppp<-P

-----------------------------------

...

aaaaaaaaaaaaaaaa<-高字节部分余数PHA1,PHA0

此处的部分余数与前面两字节算法中的第一次余数有如下关系,即A'1=PHA1^D0,A'0=PHA0:

aaaaaaaaaaaaaaaa<-PHA1,PHA0

^dddddddd<-D0

-----------------

aaaaaaaaaaaaaaaa<-A'1,A'0

低字节的计算:

aaaaaaaa0000000000000000<-A'1,0,0

^ppppppppppppppppp<-P

--------------------------

...

aaaaaaaaaaaaaaaa<-低字节部分余数PLA1,PLA0

^aaaaaaaa<-A'0,即PHA0

-----------------

aaaaaaaaaaaaaaaa<-最后的CRC(A1,A0)

总结以上内容可得规律如下:

设部分余数函数

PA=f(d)

其中d为一个字节的数据(注意,除非n=0,否则就不是原始数据,见下文)

第n次的部分余数

PA(n)=(PA(n-1)<<8)^f(d)

其中的

d=(PA(n-1)>>8)^D(n)

其中的D(n)才是一个字节的原始数据。

公式如下:

PA(n)=(PA(n-1)<<8)^f((PA(n-1)>>8)^D(n))

可以注意到函数f(d)的参数d为一个字节,对一个确定的多项式P,f(d)的返回值

是与d一一对应的,总数为256项,将这些数据预先算出保存在表里,f(d)就转换为一

个查表的过程,速度也就可以大幅提高,这也就是查表法计算CRC的原理,在CRC_16和

CRC_32两个函数的循环中的语句便是上面那个公式。

再来看CRC表是如何计算出来的,即函数f(d)的实现方法。

分析前面一个字节数据的

计算过程可发现,d对结果的影响只表现为对P的移位异或,看计算过程中的三个8位

的列中只有低两个字节的最后结果是余数,而数据所在的高8位列最后都被消去了,因其

中的运算均为异或,不产生进位或借位,故每一位数据只影响本列的结果,即d并不直接

影响结果。

再将前例变化一下重列如下:

11011000

--------------------------

10001000000100001//P

^10001000000100001//P

^00000000000000000//0

^10001000000100001//P

^00000000000000000//0

^10001000000100001//P

^00000000000000000//0

^10001000000100001//P

-------------------

0100101001110101

现在的问题就是如何根据d来对P移位异或了,从上面的例子看,也可以理解为每步

移位,但根据d决定中间余数是否与P异或。

从前面原来的例子可以看出,决定的条

件是中间余数的最高位为0,因为P的最高位一定为1,即当中间余数与d相应位异或

的最高位为1时,中间余数移位就要和P异或,否则只需移位即可。

具体做法见程序中

的BuildTable16和BuildTable32两个函数,其方法如下例(上例的变形,注意其中

空格的移动表现了d的影响如何被排除在结果之外):

d--------a--------

10000000000000000<-HSB=1

0000000000000000<-a<<=1

0001000000100001<-P,CRC-CCITT不含最高位的1

-----------------

10001000000100001

0010000001000010

0001000000100001

-----------------

00011000001100011<-HSB=0

0110000011000110

-----------------

10110000011000110<-HSB=1

1100000110001100

0001000000100001

-----------------

11101000110101101<-HSB=0

1010001101011010

-----------------

01010001101011010<-HSB=1

0100011010110100

010*********

-----------------

00101011010010101<-HSB=0

010*********

-----------------

01010110100101010<-HSB=1

010*********

0001000000100001

-----------------

0100101001110101<-CRC

结合这些,前面的程序就好理解了。

至于32位CRC的计算与16相似,就不多加说明,请参考源程序。

CRC算法原理及C语言实现 -来自(我爱单片机)

       摘要 

       本文从理论上推导出CRC算法实现原理,给出三种分别适应不同计算机或微控制器硬件环境的C语言程序。

读者更能根据本算法原理,用不同的语言编写出独特风格更加实用的CRC计算程序。

       关键词  CRC  算法  C语言

       1 引言

            

       循环冗余码CRC检验技术广泛应用于测控及通信领域。

CRC计算可以靠专用的硬件来实现,但是对于低成本的微控制器系统,在没有硬件支持下实现CRC检验,关键的问题就是如何通过软件来完成CRC计算,也就是CRC算法的问题。

           

       这里将提供三种算法,它们稍有不同,一种适用于程序空间十分苛刻但CRC计算速度要求不高的微控制器系统,另一种适用于程序空间较大且CRC计算速度要求较高的计算机或微控制器系统,最后一种是适用于程序空间不太大,且CRC计算速度又不可以太慢的微控制器系统。

       2 CRC简介

       CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。

在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。

       16位的CRC码产生的规则是先将要发送的二进制序列数左移16位(既乘以

       )后,再除以一个多项式,最后所得到的余数既是CRC码,如式(2-1)式所示,其中B(X)表示n位的二进制序列数,G(X)为多项式,Q(X)为整数,R(X)是余数(既CRC码)。

                               (2-1)

       求CRC码所采用模2加减运算法则,既是不带进位和借位的按位加减,这种加减运算实际上就是逻辑上的异或运算,加法和减法等价,乘法和除法运算与普通代数式的乘除法运算是一样,符合同样的规律。

生成CRC码的多项式如下,其中CRC-16和CRC-CCITT产生16位的CRC码,而CRC-32则产生的是32位的CRC码。

本文不讨论32位的CRC算法,有兴趣的朋友可以根据本文的思路自己去推导计算方法。

       CRC-16:

(美国二进制同步系统中采用)  

       CRC-CCITT:

(由欧洲CCITT推荐)       

       CRC-32:

   

       接收方将接收到的二进制序列数(包括信息码和CRC码)除以多项式,如果余数为0,则说明传输中无错误发生,否则说明传输有误,关于其原理这里不再多述。

用软件计算CRC码时,接收方可以将接收到的信息码求CRC码,比较结果和接收到的CRC码是否相同。

       3 按位计算CRC

       对于一个二进制序列数可以表示为式(3-1):

                                   (3-1)

       求此二进制序列数的CRC码时,先乘以后(既左移16位),再除以多项式G(X),所得的余数既是所要求的CRC码。

如式(3-2)所示:

                 (3-2)

       可以设:

                                               (3-3)

       其中为整数,为16位二进制余数。

将式(3-3)代入式(3-2)得:

             (3-4)

       再设:

                                  (3-5)

       其中为整数,为16位二进制余数,将式(3-5)代入式(3-4),如上类推,最后得到:

          (3-6)

       根据CRC的定义,很显然,十六位二进制数既是我们要求的CRC码。

       式(3-5)是编程计算CRC的关键,它说明计算本位后的CRC码等于上一位CRC码乘以2后除以多项式,所得的余数再加上本位值除以多项式所得的余数。

由此不难理解下面求CRC码的C语言程序。

*ptr指向发送缓冲区的首字节,len是要发送的总字节数,0x1021与多项式有关。

       unsignedintcal_crc(unsignedchar*ptr,unsignedcharlen){

         unsignedchari;

         unsignedintcrc=0;

         while(len--!

=0){

           for(i=0x80;i!

=0;i/=2){

             if((crc&0x8000)!

=0){crc*=2;crc^=0x1021;}  /*余式CRC乘以2再求CRC */

               elsecrc*=2;

       if((*ptr&i)!

=0)crc^=0x1021;               /*再加上本位的CRC*/

           }

           ptr++;

         }

         return(crc);

       }

         

       按位计算CRC虽然代码简单,所占用的内存比较少,但其最大的缺点就是一位一位地计算会占用很多的处理器处理时间,尤其在高速通讯的场合,这个缺点更是不可容忍。

因此下面再介绍一种按字节查表快速计算CRC的方法。

       4 按字节计算CRC

       不难理解,对于一个二进制序列数可以按字节表示为式(4-1),其中为一个字节(共8位)。

                      (4-1)

       求此二进制序列数的CRC码时,先乘以后(既左移16位),再除以多项式G(X),所得的余数既是所要求的CRC码。

如式(4-2)所示:

                     (4-2)

       可以设:

                                           (4-3)

       其中为整数,为16位二进制余数。

将式(4-3)代入式(4-2)得:

                           (4-4)

       因为:

      

                                                           (4-5)

       其中是的高八位,是的低八位。

将式(4-5)代入式(4-4),经整理后得:

                                                                                

             (4-6)

       再设:

                (4-7)

       其中为整数,为16位二进制余数。

将式(4-7)代入式(4-6),如上类推,最后得:

                       (4-8)

       很显然,十六位二进制数既是我们要求的CRC码。

       式(4-7)是编写按字节计算CRC程序的关键,它说明计算本字节后的CRC码等于上一字节余式CRC码的低8位左移8位后,再加上上一字节CRC右移8位(也既取高8位)和本字节之和后所求得的CRC码,如果我们把8位二进制序列数的CRC全部计算出来,放如一个表里,采用查表法,可以大大提高计算速度。

由此不难理解下面按字节求CRC码的C语言程序。

*ptr指向发送缓冲区的首字节,len是要发送的总字节数,CRC余式表是按0x11021多项式求出的。

       unsignedintcal_crc(unsignedchar*ptr, unsignedcharlen){

         unsignedintcrc;

         unsignedcharda;

         unsignedintcrc_ta[256]={              /*CRC余式表*/

           0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,

       0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,

           0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,

           0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,

           0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,

           0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,

           0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 解决方案

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2