1. 程式人生 > >CRC校驗實現原理以及程式實現研究

CRC校驗實現原理以及程式實現研究

在學習mdobus過程中,曾遇到過CRC校驗,之前一直不是很明白其原理,現在利用一點閒暇時間學習下。

不同別的校驗方式,想弄明白CRC校驗的原理以及程式實現過程還真得有點耐心,琢磨一下數學公式。

1.      什麼是CRC校驗

一句話總結:將需要傳遞的資料塊看成一個資訊多項式M(x),收發雙方約定一個生成多項式G(x),最高階數為r,利用模2除法計算M(x)左移r位後與G(x)兩者的餘數就是CRC校驗資料R(x)(或Thebasic idea of CRC algorithms is simply to treat the message as an enormousbinary number, to divide it by another fixed binary number, and to make theremainder from this division the checksum)。

這樣將M(x)左移r位後與CRC校驗位拼接起來就是傳送資訊T(x)。

用數學公式來表達就是T(x) = M(x)• + R(x) 其中M(x)•= G(x)•Q(x)+ R(x)

可以看出,對於傳送多項式T(x),一定是可以整除G(x)的,因為補了餘數嘛。

是不是夠明白了。

2.      計算步驟

(1). Choose a width W, and a poly G (of width W).

(2). Append W zero bits to the message. Call this M'.

(3). Divide M' by G using CRC MOD2 arithmetic. The remainder is thechecksum.

3.      什麼是模2運算

一般資料的加減法是基於10進位制計算,有進位和借位,與之不同的是MOD2 運算時是不考慮進位和借位的,這樣本位計算對前後位都沒有影響,這樣MOD2運算位加減法就是“相同為0,相異為1”,而乘除法可以用加減法去表示,所以MOD2運算就等同於異或運算。

4.      CRC校驗可以檢測什麼樣的錯誤?

正常情況下,傳送多項式T(x)mod G(x)=0,如果傳輸過程中傳送錯誤,傳送多項式變為T(x)+ E,T(x)mod G(x)=E,如果E是G(x)的倍數,這樣的錯誤是檢測不出來的,其他的錯誤都可以檢測出來。這樣就要找一個G(x)使其與系統噪聲的相似程度儘量低。那麼如何尋找一個合適的生產多項式使傳輸錯誤儘可能多的被檢測出來就很重要了。

5.      常用生成多項式的選擇

這裡只需記住三個,主要是CRC16

CRC-16(美國):     G(x) =x16+x15+x2+1

CRC-CCITT(歐洲): G(x) =x16+x12+x5+1

CRC-32 (32,26,23,22,16,12,11,10,8,7,5,4,2,1,0) 主要用於乙太網校驗。

取16或32bit主要是為了適用於微型計算機的字長,方便計算。

6.      計算方法

CRC單位元組計算方法就不多說了,按照定義即可,下面說說資料塊的CRC生成過程。

CRC校驗有三種計算方法,一是按位計算,二是按位元組計算,三是按半位元組計算。

按位計算方法,又叫直接計演算法。直接給出,雖然這個演算法因為效率低實用性不高,但是對於CRC計算的理解還是很有幫助的。直接計演算法就是實現CRC模2除法,並不是普通的模10除法。

1.Loadthe register with zero bits.(如果計算CRC-M,則選擇一個暫存器長度為M)

2.Augmentthe message by appending W zero bits to the end of it. (將資訊多項式右移M位,補M個0)

While(more message bits)

Begin

Shiftthe register left by one bit, reading the next bit of the

augmentedmessage into register bit position 0.

If(a 1 bit popped out of the register during step 3)

Register= Register XOR Poly.

(如果有1被移除就與生產多項式異或一次)

End

The register now containsthe remainder.

(最終暫存器中包含的餘數就是CRC計算值)

可以使用以下C語言程式去解釋:CRC-4,計算一個位元組

#define CRC_WIDTH   4

#define CRC_POLY   3  

//load data

    data = 0x35;           //載入資訊資料

//append W zeros to the data

    data <<= CRC_WIDTH;    //資料右移4

//initial the regs

    regs = 0;              //移位暫存器

//processing

    for(shift_bit=DATA_WIDTH+CRC_WIDTH; shift_bit>0;shift_bit--){

    // shift

        regs = (regs<<1)| ((data>>(shift_bit-1))&0x1); //將新資料的最高位移入暫存器

    // xor

        if(regs>>CRC_WIDTH)     regs = regs ^ CRC_POLY; //如果暫存器最高位為1,則進行異或計算,計算完成後regs中就是CRC4的值

}

查表法

然而在實際應用中多使用查表法,推導過程很複雜,數學不好的話建議直接記住結論,也就是:本位元組的CRC碼等於上一位元組的餘式的CRC碼的低 8位左移8位後,再加上上一位元組CRC右移8(也就是取高8)和本位元組之和(異或)所求的CRC,可以表示為

unsigned short do_crc_table(unsignedchar *ptr,int len)
{
    unsigned short int crc;
   unsigned char da;
    crc=0;
   while(len--!=0) 
    {
       da=(unsignedshort)crc>>8;   
        crc<<=8;             
        crc^=crc_ta[da^*ptr];  
        ptr++;
      }
   return(crc);
}

以上演算法實現了按位元組進行計算校驗值。在使用的時候,把計算出來的校驗值放在最後兩個位元組裡,將其傳送出去,接收端對所有的資料進行相同的校驗,如校驗值為0我們則認為其資料沒有出錯。這個是按高位到低位的傳送順序時使用的校驗方法。在實際應用中,還有一種按低位到高位的傳送方法(比如串列埠資料就是先發低位後高位),這樣 ,就要進行反相的校驗,但如果把資料反相,顯然加大計算量,故可以使用相應的查表演算法(數學推導)來進行計算。可以由以下演算法實現(摘自linux

u16crc16(u16 crc, u8 const *buffer, size_t len){

       while (len--)

              crc = (crc >> 8) ^crc16_table[(crc ^ *buffer++) & 0xff];

retrun crc;

}