1. 程式人生 > >CRC校驗原理及步驟(轉載只是為了查閱方便,若侵權立刪)

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;