1. 程式人生 > >CRC 校驗的原理及功用

CRC 校驗的原理及功用

1.CRC介紹:

迴圈冗餘碼校驗英文名稱為Cyclical Redundancy Check,簡稱CRC。它是利用除法及餘數的原理來作錯誤偵測(Error Detecting)的。實際應用時,傳送裝置計算出CRC值並隨資料一同傳送給接收裝置,接收裝置對收到的資料重新計算CRC並與收到的CRC相比較,若兩個CRC值不同,則說明資料通訊出現錯誤。 根據應用環境與習慣的不同,CRC又可分為以下幾種標準:   ①CRC-12碼;   ②CRC-16碼;   ③CRC-CCITT碼;   ④CRC-32碼。   CRC-12碼通常用來傳送6-bit字串。CRC-16及CRC-CCITT碼則用是來傳送8-bit字元,其中CRC-16為美國採用,而CRC-CCITT為歐洲國家所採用。CRC-32碼大都被採用在一種稱為Point-to-Point的同步傳輸中。 下面以最常用的CRC-16為例來說明其生成過程。   CRC-16碼由兩個位元組構成,在開始時CRC暫存器的每一位都預置為1,然後把CRC暫存器與8-bit的資料進行異或,之後對CRC暫存器從高到低進行移位,在最高位(MSB)的位置補零,而最低位(LSB,移位後已經被移出CRC暫存器)如果為1,則把暫存器與預定義的多項式碼進行異或,否則如果LSB為零,則無需進行異或。重複上述的由高至低的移位8次,第一個8-bit資料處理完畢,用此時CRC暫存器的值與下一個8-bit資料異或並進行如前一個數據似的8次移位。所有的字元處理完成後CRC暫存器內的值即為最終的CRC值。

2.CRC演算法實現過程:

CRC的檢驗碼的計算方法有兩種:一,直接校驗法;二,查表校驗法;

下面為CRC的計算過程:   1.設定CRC暫存器,並給其賦值FFFF(hex)。   2.將資料的第一個8-bit字元與16位CRC暫存器的低8位進行異或,並把結果存入CRC暫存器。   3.CRC暫存器向右移一位,MSB補零,移出並檢查LSB。   4.如果LSB為0,重複第三步;若LSB為1,CRC暫存器與多項式碼相異或。   5.重複第3與第4步直到8次移位全部完成。此時一個8-bit資料處理完畢。   6.重複第2至第5步直到所有資料全部處理完成。   7.最終CRC暫存器的內容即為CRC值。

3.CRC解析:

一、迴圈冗餘校驗碼(CRC) CRC校驗採用多項式編碼方法。被處理的資料塊可以看作是一個n階的二進位制多項式,由 。如一個8位二進位制數10110101可以表示為: 多項式乘除法運算過程與普通代數多項式的乘除法相同。多項式的加減法運算以2為模,加減時不進,錯位,和邏輯異或運算一致。 採用CRC校驗時,傳送方和接收方用同一個生成多項式g(x),並且g(x)的首位和最後一位的係數必須為1。CRC的處理方法是:傳送方以g(x)去除t(x),得到餘數作為CRC校驗碼。校驗時,以計算的校正結果是否為0為據,判斷資料幀是否出錯。 CRC校驗可以100%地檢測出所有奇數個隨機錯誤和長度小於等於k(k為g(x)的階數)的突發錯誤。所以CRC的生成多項式的階數越高,那麼誤判的概率就越小。CCITT建議:2048 kbit/s的PCM基群裝置採用CRC-4方案,使用的CRC校驗碼生成多項式g(x)= 。採用16位CRC校驗,可以保證在  bit碼元中只含有一位未被檢測出的錯誤 。在IBM的同步資料鏈路控制規程SDLC的幀校驗序列FCS中,使用CRC-16,其生成多項式g(x)= ;而在CCITT推薦的高階資料鏈路控制規程HDLC的幀校驗序列FCS中,使用CCITT-16,其生成多項式g(x)= 。CRC-32的生成多項式g(x)= 。CRC-32出錯的概率比CRC-16低 倍 。由於CRC-32的可靠性,把CRC-32用於重要資料傳輸十分合適,所以在通訊、計算機等領域運用十分廣泛。在一些UART通訊控制晶片(如MC6582、Intel8273和Z80-SIO)內,都採用了CRC校驗碼進行差錯控制;乙太網卡晶片、MPEG解碼晶片中,也採用CRC-32進行差錯控制。 二、CRC校驗碼的演算法分析 CRC校驗碼的編碼方法是用待發送的二進位制資料t(x)除以生成多項式g(x),將最後的餘數作為CRC校驗碼。其實現步驟如下: (1) 設待發送的資料塊是m位的二進位制多項式t(x),生成多項式為r階的g(x)。在資料塊的末尾新增r個0,資料塊的長度增加到m+r位,對應的二進位制多項式為 。 (2) 用生成多項式g(x)去除 ,求得餘數為階數為r-1的二進位制多項式y(x)。此二進位制多項式y(x)就是t(x)經過生成多項式g(x)編碼的CRC校驗碼。 (3) 用 以模2的方式減去y(x),得到二進位制多項式 。 就是包含了CRC校驗碼的待發送字串。 從CRC的編碼規則可以看出,CRC編碼實際上是將代傳送的m位二進位制多項式t(x)轉換成了可以被g(x)除盡的m+r位二進位制多項式 ,所以解碼時可以用接受到的資料去除g(x),如果餘數位零,則表示傳輸過程沒有錯誤;如果餘數不為零,則在傳輸過程中肯定存在錯誤。許多CRC的硬體解碼電路就是按這種方式進行檢錯的。同時 可以看做是由t(x)和CRC校驗碼的組合,所以解碼時將接收到的二進位制資料去掉尾部的r位資料,得到的就是原始資料。 為了更清楚的瞭解CRC校驗碼的編碼過程,下面用一個簡單的例子來說明CRC校驗碼的編碼過程。由於CRC-32、CRC-16、CCITT和CRC-4的編碼過程基本一致,只有位數和生成多項式不一樣。為了敘述簡單,用一個CRC-4編碼的例子來說明CRC的編碼過程。 設待發送的資料t(x)為12位的二進位制資料100100011100;CRC-4的生成多項式為g(x)= ,階數r為4,即10011。首先在t(x)的末尾新增4個0構成 ,資料塊就成了1001000111000000。然後用g(x)去除 ,不用管商是多少,只需要求得餘數y(x)。下表為給出了除法過程。 除數次數 被除數/ g(x)/結果     餘數 0  1 001000111000000 100111000000  1 0011  0 000100111000000 1  1 00111000000   1000000  1 0011   0 00001000000 2  1 000000 1100  1 0011  0 001100 從上面表中可以看出,CRC編碼實際上是一個迴圈移位的模2運算。對CRC-4,我們假設有一個5 bits的暫存器,通過反覆的移位和進行CRC的除法,那麼最終該暫存器中的值去掉最高一位就是我們所要求的餘數。所以可以將上述步驟用下面的流程描述: //reg是一個5 bits的暫存器

把reg中的值置0.  把原始的資料後新增r個0.  While (資料未處理完)  Begin  If (reg首位是1)  reg = reg XOR 0011.  把reg中的值左移一位,讀入一個新的資料並置於register的0 bit的位置。  End

reg的後四位就是我們所要求的餘數。 這種演算法簡單,容易實現,對任意長度生成多項式的G(x)都適用。在傳送的資料不長的情況下可以使用。但是如果傳送的資料塊很長的話,這種方法就不太適合了。它一次只能處理一位資料,效率太低。為了提高處理效率,可以一次處理4位、8位、16位、32位。由於處理器的結構基本上都支援8位資料的處理,所以一次處理8位比較合適。 為了對優化後的演算法有一種直觀的瞭解,先將上面的演算法換個角度理解一下。在上面例子中,可以將編碼過程看作如下過程:  由於最後只需要餘數,所以我們只看後四位。構造一個四位的暫存器reg,初值為0,資料依次移入reg0(reg的0位),同時reg3的資料移出reg。有上面的演算法可以知道,只有當移出的資料為1時,reg才和g(x)進行XOR運算;移出的資料為0時,reg不與g(x)進行XOR運算,相當與和0000進行XOR運算。就是說,reg和什麼樣的資料進行XOR移出的資料決定。由於只有一個bit,所以有 種選擇。上述演算法可以描述如下,

//reg是一個4 bits的暫存器 初始化t[]={0011,0000} 把reg中的值置0.  把原始的資料後新增r個0.  While (資料未處理完)  Begin  把reg中的值左移一位,讀入一個新的資料並置於register的0 bit的位置。 reg = reg XOR t[移出的位] End

上面演算法是以bit為單位進行處理的,可以將上述演算法擴充套件到8位,即以Byte為單位進行處理,即CRC-32。構造一個四個Byte的暫存器reg,初值為0x00000000,資料依次移入reg0(reg的0位元組,以下類似),同時reg3的資料移出reg。用上面的演算法類推可知,移出的資料位元組決定reg和什麼樣的資料進行XOR。由於有8個bit,所以有 種選擇。上述演算法可以描述如下:

//reg是一個4 Byte的暫存器 初始化t[]={…}//共有 =256項 把reg中的值置0.  把原始的資料後新增r/8個0位元組.  While (資料未處理完)  Begin  把reg中的值左移一個位元組,讀入一個新的位元組並置於reg的第0個byte的位置。 reg = reg XOR t[移出的位元組] End

演算法的依據和多項式除法性質有關。如果一個m位的多項式t(x)除以一個r階的生成多項式g(x), ,將每一位 (0=<k<m)提出來,在後面不足r個0後,單獨去除g(x),得到的餘式位 。則將 後得到的就是t(x)由生成多項式g(x)得到的餘式。對於CRC-32,可以將每個位元組在後面補上32個0後與生成多項式進行運算,得到餘式和此位元組唯一對應,這個餘式就是上面演算法種t[]中的值,由於一個位元組有8位,所以t[]共有 =256項。多項式運算性質可以參見參考文獻[1]。這種演算法每次處理一個位元組,通過查表法進行運算,大大提高了處理速度,故為大多數應用所採用。

三、CRC-32的程式實現。 為了提高編碼效率,在實際運用中大多采用查表法來完成CRC-32校驗,下面是產生CRC-32校驗嗎的子程式。 unsigned long  crc_32_tab[256]={ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,0x0edb8832,…, 0x5a05df1b, 0x2d02ef8d };//事先計算出的引數表,共有256項,未全部列出。 unsigned long GenerateCRC32(char xdata * DataBuf,unsigned long  len) { unsigned long oldcrc32; unsigned long crc32; unsigned long oldcrc; unsigned  int charcnt;         char c,t; oldcrc32 = 0x00000000; //初值為0     charcnt=0; while (len--) {                 t= (oldcrc32 >> 24) & 0xFF;   //要移出的位元組的值 oldcrc=crc_32_tab[t];         //根據移出的位元組的值查表                 c=DataBuf[charcnt];          //新移進來的位元組值                 oldcrc32= (oldcrc32 << 8) | c;   //將新移進來的位元組值添在暫存器末位元組中                 oldcrc32=oldcrc32^oldcrc;     //將暫存器與查出的值進行xor運算                 charcnt++; }         crc32=oldcrc32;         return crc32; } 引數表可以先在PC機上算出來,也可在程式初始化時完成。下面是用於計算引數表的c語言子程式,在Visual C++ 6.0下編譯通過。 #include <stdio.h> unsigned long int crc32_table[256]; unsigned long int ulPolynomial = 0x04c11db7; unsigned long int Reflect(unsigned long int ref, char ch) { unsigned long int value(0); // 交換bit0和bit7,bit1和bit6,類推 for(int i = 1; i < (ch + 1); i++) { if(ref & 1) value |= 1 << (ch - i);      ref >>= 1; } return value; } init_crc32_table() { unsigned long int crc,temp; // 256個值 for(int i = 0; i <= 0xFF; i++) {   temp=Reflect(i, 8); crc32_table[i]= temp<< 24; for (int j = 0; j < 8; j++){      unsigned long int t1,t2;  unsigned long int flag=crc32_table[i]&0x80000000;  t1=(crc32_table[i] << 1);  if(flag==0)    t2=0;  else    t2=ulPolynomial;  crc32_table[i] =t1^t2 ; } crc=crc32_table[i]; crc32_table[i] = Reflect(crc32_table[i], 32); } } 結束語     CRC校驗由於實現簡單,檢錯能力強,被廣泛使用在各種資料校驗應用中。佔用系統資源少,用軟硬體均能實現,是進行資料傳輸差錯檢測地一種很好的手段。