CRC校验原理及步骤(转载只是为了查阅方便,若侵权立删)

什么是CRC校验?

CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

CRC校验原理:

其根本思想就是先在要发送的帧后面附加一个数(这个就是用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(注意,这里不是直接采用二进制除法,而是采用一种称之为“模2除法”)。到达接收端后,再把接收到的新帧除以(同样采用“模2除法”)这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数,做了“去余”处理(也就已经能整除了),所以结果应该是没有余数。如果有余数,则表明该帧在传输过程中出现了差错。

模2除法:

模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。

例:

CRC校验步骤:

CRC校验中有两个关键点,一是预先确定一个发送送端和接收端都用来作为除数的二进制比特串(或多项式),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1;二是把原始帧与上面计算出的除数进行模2除法运算,计算出CRC码。

具体步骤:

1. 选择合适的除数

2. 看选定除数的二进制位数,然后再要发送的数据帧上面加上这个位数-1位的0,然后用新生成的帧以模2除法的方式除上面的除数,得到的余数就是该帧的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。

3. 将计算出来的CRC校验码附加在原数据帧后面,构建成一个新的数据帧进行发送;最后接收端在以模2除法方式除以前面选择的除数,如果没有余数,则说明数据帧在传输的过程中没有出错。

CRC校验码计算示例:

现假设选择的CRC生成多项式为G(X) = X4 + X3 + 1,要求出二进制序列10110011的CRC校验码。下面是具体的计算过程:

①将多项式转化为二进制序列,由G(X) = X4 + X3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001

②多项式的位数位5,则在数据帧的后面加上5-1位0,数据帧变为101100110000,然后使用模2除法除以除数11001,得到余数。

③将计算出来的CRC校验码添加在原始帧的后面,真正的数据帧为101100110100,再把这个数据帧发送到接收端。

④接收端收到数据帧后,用上面选定的除数,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。

--------------------- 本文来自 D_leo 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/d_leo/article/details/73572373?utm_source=copy

————————————————————————————————————————————————————————

CRC校验码原理、实例、手动计算

目录
一、CRC16实现代码
二、CRC32编码字符表
三、CRC校验码的手动计算示例
四、CRC校验原理
五、CRC的生成多项式
参考

一、CRC16实现代码

思路:取一个字符(8bit),逐位检查该字符,如果为1,crc^crc_mul;同时,如果原本crc最高位是1,那么crc^crc_mul后左移1位,否则只是左移一位。计算完一个字符后,装入下一个字符。

复制代码

#include<stdio.h>

#define crc_mul 0x1021  //生成多项式

unsigned int cal_crc(unsigned char *ptr, unsigned char len)
{
    unsigned char i;
    unsigned int crc=0;
    while(len-- != 0)
    {
        for(i=0x80; i!=0; i>>=1)
        {
            if((crc&0x8000)!=0)
            {
               crc<<=1;
               crc^=(crc_mul);
            }else{
               crc<<=1;
            }
            if((*ptr&i)!=0)
            {
               crc ^= (crc_mul);
            }
        }
        ptr ++;
    }
    return (crc);
}

int main()
{
    unsigned char i[8] = {0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};
    unsigned int crc;
    crc=cal_crc(i,8);
    return 0;
} 
/*结果:7123dbc0*/

复制代码

其实,世界上一共就256个字符,每装载一个就运算一遍,实在是浪费CPU,不如直接把每个字符的CRC都算出来存入数组。因此,就有了CRC编码字符表。 

二、CRC32编码字符表

复制代码

#include<stdio.h>
unsigned int CRC32_table[256] = {0};
void init_CRC32_table()
{
  for (int i = 0; i != 256; i++)
  {
    unsigned int CRC = i;
    for (int j = 0; j != 8; j++)
    {
      if (CRC & 1)
        CRC = (CRC >> 1) ^ 0xEDB88320;
      else
        CRC >>= 1;
    }
    CRC32_table[i] = CRC;
  }
}
unsigned int GetCRC32(unsigned char* buf, unsigned int len)
{
  unsigned int CRC32_data = 0xFFFFFFFF;
  for (unsigned int i = 0; i != len; ++i)
  {
    unsigned int t = (CRC32_data ^ buf[i]) & 0xFF;
    CRC32_data = ((CRC32_data >> 8) & 0xFFFFFF) ^ CRC32_table[t];
  }
  return ~CRC32_data;
}

int main()
{
    unsigned char i[8] = {0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};
    init_CRC32_table();
    printf("BUFFER i's CRC32: 0x%x\n", GetCRC32(i,8));
    printf("CRC32 TABLE:\n");
    for(int i=0;i<256;i++)
    {
         printf("0x%8x\t",CRC32_table[i]);
         if((i+1)%8 == 0)
             printf("\n");
    }
} 
/*结果如下:

BUFFER i's CRC32: 0xc29c07b9
CRC32 TABLE:
0x 0 0x77073096 0xee0e612c 0x990951ba 0x 76dc419 0x706af48f 0xe963a535 0x9e6495a3
0x edb8832 0x79dcb8a4 0xe0d5e91e 0x97d2d988 0x 9b64c2b 0x7eb17cbd 0xe7b82d07 0x90bf1d91
0x1db71064 0x6ab020f2 0xf3b97148 0x84be41de 0x1adad47d 0x6ddde4eb 0xf4d4b551 0x83d385c7
0x136c9856 0x646ba8c0 0xfd62f97a 0x8a65c9ec 0x14015c4f 0x63066cd9 0xfa0f3d63 0x8d080df5
0x3b6e20c8 0x4c69105e 0xd56041e4 0xa2677172 0x3c03e4d1 0x4b04d447 0xd20d85fd 0xa50ab56b
0x35b5a8fa 0x42b2986c 0xdbbbc9d6 0xacbcf940 0x32d86ce3 0x45df5c75 0xdcd60dcf 0xabd13d59
0x26d930ac 0x51de003a 0xc8d75180 0xbfd06116 0x21b4f4b5 0x56b3c423 0xcfba9599 0xb8bda50f
0x2802b89e 0x5f058808 0xc60cd9b2 0xb10be924 0x2f6f7c87 0x58684c11 0xc1611dab 0xb6662d3d
0x76dc4190 0x 1db7106 0x98d220bc 0xefd5102a 0x71b18589 0x 6b6b51f 0x9fbfe4a5 0xe8b8d433
0x7807c9a2 0x f00f934 0x9609a88e 0xe10e9818 0x7f6a0dbb 0x 86d3d2d 0x91646c97 0xe6635c01
0x6b6b51f4 0x1c6c6162 0x856530d8 0xf262004e 0x6c0695ed 0x1b01a57b 0x8208f4c1 0xf50fc457
0x65b0d9c6 0x12b7e950 0x8bbeb8ea 0xfcb9887c 0x62dd1ddf 0x15da2d49 0x8cd37cf3 0xfbd44c65
0x4db26158 0x3ab551ce 0xa3bc0074 0xd4bb30e2 0x4adfa541 0x3dd895d7 0xa4d1c46d 0xd3d6f4fb
0x4369e96a 0x346ed9fc 0xad678846 0xda60b8d0 0x44042d73 0x33031de5 0xaa0a4c5f 0xdd0d7cc9
0x5005713c 0x270241aa 0xbe0b1010 0xc90c2086 0x5768b525 0x206f85b3 0xb966d409 0xce61e49f
0x5edef90e 0x29d9c998 0xb0d09822 0xc7d7a8b4 0x59b33d17 0x2eb40d81 0xb7bd5c3b 0xc0ba6cad
0xedb88320 0x9abfb3b6 0x 3b6e20c 0x74b1d29a 0xead54739 0x9dd277af 0x 4db2615 0x73dc1683
0xe3630b12 0x94643b84 0x d6d6a3e 0x7a6a5aa8 0xe40ecf0b 0x9309ff9d 0x a00ae27 0x7d079eb1
0xf00f9344 0x8708a3d2 0x1e01f268 0x6906c2fe 0xf762575d 0x806567cb 0x196c3671 0x6e6b06e7
0xfed41b76 0x89d32be0 0x10da7a5a 0x67dd4acc 0xf9b9df6f 0x8ebeeff9 0x17b7be43 0x60b08ed5
0xd6d6a3e8 0xa1d1937e 0x38d8c2c4 0x4fdff252 0xd1bb67f1 0xa6bc5767 0x3fb506dd 0x48b2364b
0xd80d2bda 0xaf0a1b4c 0x36034af6 0x41047a60 0xdf60efc3 0xa867df55 0x316e8eef 0x4669be79
0xcb61b38c 0xbc66831a 0x256fd2a0 0x5268e236 0xcc0c7795 0xbb0b4703 0x220216b9 0x5505262f
0xc5ba3bbe 0xb2bd0b28 0x2bb45a92 0x5cb36a04 0xc2d7ffa7 0xb5d0cf31 0x2cd99e8b 0x5bdeae1d
0x9b64c2b0 0xec63f226 0x756aa39c 0x 26d930a 0x9c0906a9 0xeb0e363f 0x72076785 0x 5005713
0x95bf4a82 0xe2b87a14 0x7bb12bae 0x cb61b38 0x92d28e9b 0xe5d5be0d 0x7cdcefb7 0x bdbdf21
0x86d3d2d4 0xf1d4e242 0x68ddb3f8 0x1fda836e 0x81be16cd 0xf6b9265b 0x6fb077e1 0x18b74777
0x88085ae6 0xff0f6a70 0x66063bca 0x11010b5c 0x8f659eff 0xf862ae69 0x616bffd3 0x166ccf45
0xa00ae278 0xd70dd2ee 0x4e048354 0x3903b3c2 0xa7672661 0xd06016f7 0x4969474d 0x3e6e77db
0xaed16a4a 0xd9d65adc 0x40df0b66 0x37d83bf0 0xa9bcae53 0xdebb9ec5 0x47b2cf7f 0x30b5ffe9
0xbdbdf21c 0xcabac28a 0x53b39330 0x24b4a3a6 0xbad03605 0xcdd70693 0x54de5729 0x23d967bf
0xb3667a2e 0xc4614ab8 0x5d681b02 0x2a6f2b94 0xb40bbe37 0xc30c8ea1 0x5a05df1b 0x2d02ef8d

*/

复制代码

三、CRC校验码的手动计算示例

生成多项式:G(X)=X4+X3+1,要求出二进制序列10110011的CRC校验码。

(1)G(X)=X4+X3+1,二进制比特串为11001;(有X的几次方,对应的2的几次方的位就是1)

(2)因为校验码4位,所以10110011后面再加4个0,得到101100110000,用“模2除法”(其实就是亦或^)即可得出结果;

 

                                            图5-10 CRC校验码计算示例

 (3)CRC^101100110000得到101100110100。发送到接收端;

 (4)接收端收到101100110100后除以11001(以“模2除法”方式去除),余数为0则无差错;

四、CRC校验原理

在k位信息码后再拼接r位的校验码,报文编码长度为n位,因此,这种编码又叫(n,k)码。

定理:对于一个给定的(nk)码,可以证明,存在一个最高次幂为n=k+r的多项式G(x)存在且仅存在一个R次多项式G(x),使得

其中:

m(x) :k次信息多项式,

r(x) :r-1次校验多项式,

g(x):生成多项式:

发送方通过指定的G(x)产生r位的CRC校验码,接收方则通过该G(x)来验证收到的报文码的CRC校验码是否为0。

    假设发送信息用信息多项式C(X)表示,将C(x)左移r位,则可表示成C(x)*2r,这样C(x)的右边就会空出r位校验码的位置,做除法(模2除),得到的余数R就是校验码。发送的CRC编码是,验证接收到的报文编码是否至正确,依然是做模2除:

五、CRC的生成多项式

    生成多项式的选取应满足以下条件:

    a、生成多项式的最高位和最低位必须为1。

    b、当被传送信息(CRC码)任何一位发生错误时,被生成多项式做模2除后,应该使余数不为0。

    c、不同位发生错误时,应该使余数不同。

    d、对余数继续做模2除,应使余数循环。

主要的生成多项式G(x)有以下几种:

名称

生成多项式

数值式

简记式

标准引用

CRC-16

x16+x15+x2+1

0x1’8005

8005

IBM SDLC

CRC-CCITT

x16+x12+x5+1

0X1’1021

0x1021

ISO HDLC,ITU X.25,V.34/V.41/V.42,PPP-FCS

CRC-32

注*

0X1’04C11DB7

0x04C11DB7

ZIP,RAR,IEEE 802 LAN/FDDI,IEEE1394,PPP-FCS

注*  x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1

    下表中的生成多项式G(x)也常见的:

名称

生成多项式

数值式

简记式

标准引用

CRC-4

x4+x+1

0x1’3

0x3

ITU G.704

CRC-8

x8+x5+x4+1

0x1’31

0x31

CRC-8

x8+x2+x1+1

0x1’07

0x07

CRC-8

x8+x6+x4+x3+x2+x1

0x1’5E

0x5E

CRC-12

x12+x11+x3+x2+x+1

0x1’80F

0x80F

CRC-32c

注**

0X1’1EDC6F41

0x1EDC6F41

SCTP

注** x32+x28+x27+x26+x25+x23+x22+x20+x19+x18+x14+x13+x11+x10+x9+x8+x6+1

 参考:

http://blog.chinaunix.net/uid-2630593-id-2138511.html

http://blog.sina.com.cn/s/blog_6f7a12790100n1vs.html

http://wenku.baidu.com/link?url=4W06KdqshIHVtvIxJcvujrwibubyVLVYFbEsRkFCA4ZVBe9g1VjiLVwRDjjmu_0HiLV-YUK65QZ9_6BqDuFhUeQxW8hNsfkXMM2H6cNjYH7

————————————————————————————————————————————————————————

最详细易懂的CRC-16校验原理(附源程序)

from:http://www.openhw.org/chudonganjin/blog/12-08/230184_515e6.html 

最详细易懂的CRC-16校验原理(附源程序)

1、循环校验码(CRC码):

是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。

2、生成CRC码的基本原理:

任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。

标准CRC生成多项式如下表:

   名称          生成多项式              简记式*   标准引用

   CRC-4         x4+x+1                  3         ITU G.704

   CRC-8         x8+x5+x4+1              0x31                   

   CRC-8         x8+x2+x1+1              0x07                   

   CRC-8         x8+x6+x4+x3+x2+x1       0x5E

   CRC-12        x12+x11+x3+x+1          80F

   CRC-16        x16+x15+x2+1            8005      IBM SDLC

CRC16-CCITT  x16+x12+x5+1   1021   ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS

   CRC-32      x32+x26+x23+...+x2+x+1 04C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS

   CRC-32c     x32+x28+x27+...+x8+x6+1 1EDC6F41     SCTP

3、CRC-16校验码的使用:

    现选择最常用的CRC-16校验,说明它的使用方法。

根据Modbus协议,常规485通讯的信息发送形式如下:

   地址  功能码   数据信息  校验码

   1byte   1byte   nbyte    2byte  

CRC校验是前面几段数据内容的校验值,为一个16位数据,发送时,低8位在前,高8为最后。

例如:信息字段代码为: 1011001,校验字段为:1010。

发送方:发出的传输字段为:  1 0 1 1 0 0 1 1 0 10

                          信息字段       校验字段

接收方:使用相同的计算方法计算出信息字段的校验码,对比接收到的实际校验码,如果相等及信息正确,不相等则信息错误;或者将接受到的所有信息除多项式,如果能够除尽,则信息正确。

4、CRC-16校验码计算方法:

常用查表法和计算法。计算方法一般都是:
(1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器;
(2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低
       8位相异或,把结果放于CRC寄存器,高八位数据不变;
(3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位;
(4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多

    项式A001(1010 0000 0000 0001)进行异或;
(5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
(6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
(7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低
       字节进行交换;
(8)、最后得到的CRC寄存器内容即为:CRC码。

以上计算步骤中的多项式A001是8005按位颠倒后的结果。

查表法是将移位异或的计算结果做成了一个表,就是将0~256放入一个长度为16位的寄存器中的低八位,高八位填充0,然后将该寄存器与多项式0XA001按照上述3、4步骤,直到八位全部移出,最后寄存器中的值就是表格中的数据,高八位、低八位分别单独一个表。

  【

0x8005=1000 0000 0000 0101B


0xA001=1010 0000 0000 0001B
对比两个二进制高低位正好是完全相反的,CRC校验分为正向校验与反向校验。正向校验高位在左,反向校验低位在左,比如正向CRC校验的数据为0xAF5D=1010 1111 0101 1101B与0x8005异或时应该是0xAF5D^0x8005,而要使用0xA001与数据进行校验也应该使0xAF5D高低位换顺序为0xBAF5=1011 1010 1111 0101B。正向校验使用左移位,反向校验使用右移位,其实原理是一样的,得看校验的数据高低位顺序】

5、提供两个经典的程序示例(皆验证通过)

(1)     C查表法版本:

     特点:速度快,语句少,但表格占用一定的程序空间。

 *pucFrame 为待校验数据首地址,usLen为待校验数据长度。返回值为校验结果。

USHORT usMBCRC16( UCHAR * pucFrame, USHORT usLen )

{

    UCHAR ucCRCHi = 0xFF;

    UCHAR ucCRCLo = 0xFF;

    int iIndex;

    while( usLen-- )

    {

        iIndex = ucCRCLo ^ *( pucFrame++ );

        ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );

        ucCRCHi = aucCRCLo[iIndex];

    }

    return ( USHORT )( ucCRCHi << 8 | ucCRCLo );

}

static const UCHAR aucCRCHi[] = {

    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

};

static const UCHAR aucCRCLo[] = {

    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

};

(2)     汇编计算法版本:

    特点:需要计算n*8次(n为信息字节数),运行速度慢,占用程序时间,但节省空间资源。

TEMP             EQU    40H
CHKSUMBYL   EQU    46H                  ;校验和低字节
CHKSUMBYH   EQU    47H                  ;校验和高字节
DATALENGTH  EQU    4FH                  ;待校验的数据串长度
            

 ORG   0000H
 MOV   TEMP,#1EH
 MOV   TEMP+1,#6
 MOV   TEMP+2,#20H
 MOV   TEMP+3,#0
 MOV   TEMP+4,#0
 MOV   TEMP+5,#2
 LCALL MAKE_CHKSUM
 SJMP  $
 ;--------------------------------------------------------------------------
;运行:  1E 06 20 00 00 02 01 A4  ,16进制,设备地址,命令,存储器地址高,存储器地址低,参数高,参数低,校验低,校验高。
;---------------------------------------------------------------------------
MAKE_CHKSUM:         ;RTU 模式,CRC - 16 校验,用软件模拟仿真检查无误
          MOV   R0,#TEMP
        MOV   CHKSUMBYL,#0FFH  ;1.预置 16 位寄存器为十六进制 FFFF(即全为 1),低字节
        MOV   CHKSUMBYH,#0FFH  ;  预置 16 位寄存器为十六进制 FFFF(即全为 1),高字节
        MOV   DATALENGTH,#6         ;待校验的数据串长度
CHKSUM_LP1:
        MOV   A,@R0           ;2.把第一个 8 位数据与 16 位 CRC 寄存器的低位相异或,
        XRL   A,CHKSUMBYL
        MOV   CHKSUMBYL,A           ;并把结果放于CRC 寄存器
        MOV   R7,#8
CHKSUM_LP2:
         MOV   A,CHKSUMBYH
         CLR   C
         RRC   A                   ;把寄存器的内容右移一位(朝低位),先移动高字节
         MOV   CHKSUMBYH,A
         MOV   A,CHKSUMBYL
         RRC   A                     ;再移动低字节
         MOV   CHKSUMBYL,A
         JNC   CHKSUM_JP  ;4.检查最低位(移出位),如果最低位为 0 ,重复第 3 步(再次移位)
         MOV   A,CHKSUMBYL
         XRL   A,#01H             ;如果最低位为 1,CRC 寄存器与多项式 A001 进行异或
         MOV   CHKSUMBYL,A
         MOV   A,CHKSUMBYH
         XRL   A,#0A0H
         MOV   CHKSUMBYH,A
CHKSUM_JP:
         DJNZ  R7,CHKSUM_LP2   ;重复步骤 3、4,右移 8 次,8 位数据全部进行了处理
         INC   R0
         DJNZ  DATALENGTH,CHKSUM_LP1 ;重复步骤2-5,进行下一个 8 位数据的处理
         RET
           
         END

首先介绍一个不错的CRC校验的网站,http://www.easics.com/webtools/crctool  现在估计所有的工程应用均来自该网站生成的代码。使用方便。

但是该网站的代码不易于CRC的学习和研究,但是保证是对的,工程实践证明。现在将我的研究成果和大家分享一下:用于任意CRC的校验。

    网站上的校验方式最大提供CRC32 和任意数据位(最大511)的校验。当然一般的情况下应该是够用了。我所做的设计可以扩展到任意数据的校验,当然是并行数据的校验,串行数据的校验应用可以参照网上的一些资料。很简单,不再赘述。以CRC32为例

首先建立函数,=====设计的的关键

//--------------------------------------------------------------------------
function [31:0] next_c32;   

input [31:0] crc;     
input B;        
begin
    next_c32 = {crc[30:0],1'b0} ^ ({32{(crc[31] ^ B)}} &32'h04c11db7);//下划线的部分为本征多项式
end

endfunction

/*这是校验和左移一位求校验和的计算公式*/

相同的如果CRC8

//--------------------------------------------------------------------------
function [7:0] next_c8;   

input [7:0] crc;     
input B;        
begin
    next_c8 = {crc[6:0],1'b0} ^ ({8{(crc[7] ^ B)}} & 8'h03);//下划线的部分为本征多项式
end

endfunction

其他的是一样的。

其次 如果我们要求CRC32_D(M)M  >= 32

function [31:0] next_c32_ge; //M+1 is the data maximum with
input [M:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_ge = crc;
 for(i=0; i<=M; i="i"+1) begin
      next_c32_ge = next_c32(next_c32_ge,data[M-i]);
 end
end
endfunction

假设我们求CRC32_D64  那么M=63

function [31:0] next_c32_D64; //M+1 is the data maximum with
input [63:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_D64 = crc;
 for(i=0; i<=63; i="i"+1) begin
      next_c32_D64 = next_c32(next_c32_D64,data[63-i]);
 end
end
endfunction

假设我们求CRC32_D128  那么M=127

function [31:0] next_c32_D128; 

input [127:0] data;
input [31:0] crc;
integer  i;
begin
 next_c32_D128 = crc;
 for(i=0; i<=127; i="i"+1) begin
      next_c32_D128= next_c32(next_c32_D128,data[127-i]);
 end
end
endfunction

再次如果我们要求CRC32_D(M) M<=32

function [31:0] next_c32_le;
input [31:0] data;
input [31:0] inp;
input [4:0] be;
integer  i;
begin
 next_c32_le = data;
 for(i=0; i<=31-be; i="i"+1) begin
      next_c32_le = next_c32(next_c32_le,inp[31-be-i]);
 end
end
endfunction

我们首先校验完毕所有的有效数据位下面的函数是对CRC的空闲位的修正。

function [K-1:0] next_cK_1_any_LEK_1;
input [N-1:0] data;
input [K-1:0] crc;
begin
     next_cK_1_any_LEK_1 = next_c32_le({data,{(K-N){1'b0}}},{crc[K-1:N],{(K-N){1'b0}}},(K-N))^{crc<
end 
endfunction

//以CRC32D16  K =32   N =16 这个函数就变成

function [31:0] next_C32_D16;
input [15:0] data;
input [31:0] crc;
begin
 next_C32_D16 = next_c32_le({data,{16{1'b0}}},{crc[31:16],{16{1'b0}}},16)^{crc<<16}; 
end 
endfunction

经过modelsim和Qii软件仿真无误。本来想做成动态数据长度校验的函数,本人也作了一些尝试,在CRC--N           N = 2^m时都是没有问题的 比如CRC8  CRC16 CRC32 CRC64 等等,但是若是不是这些数值比如CRC12 CRC10的Qii会抱错(因为部分函数的输入部分必须为常数),但是Modelsim不会抱错而且仿真和实际的结果一致。可以用来做验证。 这边仅仅举了CRC32 的例子,其他的也都类似。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity CODE_74_NEW is
Port (     clk : in  STD_LOGIC;
           data_in: in  STD_LOGIC_VECTOR (3 downto 0);
     --cnt_out: out std_logic_vector (2 downto 0);
     --dtemp_out: out std_logic_vector (3 downto 0);
           data_crc : out  STD_LOGIC);
     
end CODE_74_NEW;
architecture Behavioral of CODE_74_NEW is
  constant multi_coef:std_logic_vector (3 downto 0):="1101";--生成多项式系数,MSB一定为1,g(x)=x^3+x^2+1
  
begin
process(clk)
  variable crcvar,dtemp,sdata:std_logic_vector(3 downto 0);--除法运算被除数变量
  variable cnt:std_logic_vector (2 downto 0):="000";--运算次数控制
  
begin
if clk'event and clk='1' then
cnt:=cnt+1;
--cnt_out<=cnt;
--dtemp_out<=dtemp;
   
    if cnt<=4 then --前四个时钟,串行输出四位信息码
    if  cnt=1 then --初始化操作
      dtemp:=data_in;--装载原数据,用于运算校验码
      sdata:=data_in;--装载原数据,保存
    end if;  
    
          data_crc<=sdata(3);--当计数器小于4时,每来一个时钟串行输出一位信息码
          sdata:=sdata(2 downto 0) & '0'; --左移   
    
    --以下为校验码运算
    if dtemp(3)='1' then  --当前运算的四位码,如果最高位为1则可进行模二除法
     crcvar:=dtemp(3 downto 0) xor multi_coef;--异或运算模二除法
     dtemp:=crcvar(2 downto 0) & '0';--运算后补零
     else dtemp:=dtemp(2 downto 0) & '0';--当前运算的四位码,如果最高位为0则只进行移位补零
    end if;
    
  elsif  cnt>4  then --后三个时钟串行输?位校验码
      data_crc<=dtemp(3);--输出,移位 
    dtemp:=dtemp(2 downto 0) & '0';
      if cnt=7 then  --第7个时钟清零
       cnt:=(others=>'0');
    end if;
  end if;
end if;
end process;
end Behavioral;

猜你喜欢

转载自blog.csdn.net/qq_36389327/article/details/82966636