CRC16 的生成及校驗原理


參考:https://blog.csdn.net/niepangu/article/details/45499383

 

計算CRC的過程,就是用一個特殊的“除法”,來得到余數,這個余數就是CRC。 
它不是真正的算術上的除法!過程和算術除法過程一樣,只是加減運算變成了XOR(異或)運算! 
 
算術上的除法: 
120÷9=13 余 3,120是被除數,9是除數,13是商,3是余數。念作120除以9,或者9除120,或
者9去除120!(除法的過程就不寫了) 
這個除法計算機當然會做,但是做起來很麻煩,因為減法有借位,很耗時間和指令! 
所以,計算CRC也是除法,但是用XOR來代替減法,這就簡單多了!

CRC的除法: 
120÷9=14 余 6,商、余數和算術除法不一定相同!!因為除法用的是XOR,而不是真正的減法。 
以二進制模擬這個計算過程: 

120 二進制:1111000 、除數9 二進制:1001 、商 14 二進制:1110 余數6 二進制:110 

從高位1111開始,每次進行一次XOR 的到的值后,去掉最高位加入下一位,每加一次進行一次XOR運算。

  1111

^1001

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

  0110

 第一次XOR后得到0110,去掉最高位0,加入下一位0, 得1100 ,這樣最高位是1,所以下個商是1 ,用^1001【很明顯保留的位數與1001 保持一致】

  1100

^1001

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

   0101

第二次XOR ,去掉最高位,加入下一位0,得1010 ,這樣最高位是1,所以下個商是1 ,用^1001

  1010

^ 1001

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

  0011

第三次XOR,去掉最高位,加入下一位0,得0110 ,這樣最高位是1,所以下個商是0 ,用^0000

  0110

^ 0000

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

  0110

最后一次XOR后得到0110,最高位的0可以消掉了,得到余數為110,即6 
注意,余數不是0110,而是110,因為最前面那個0已經被XOR后消掉了!

 

 

可見,除法(XOR)的目的是逐步消掉最高位的1或0! 
由於過程是XOR的,所以商是沒有意義的,我們不要。我們要的是余數。 
 
余數110是1111000的CRC嗎?不是! 
余數110是1111(即十進制15)的CRC!!! 
為什么?因為CRC是和數據一起傳送的,所以數據后面要加上CRC。 
數據1111加上CRC110后,變成1111110,再傳送。接收機收到1111110后,除以除數1001,余數為
000,正確;如果余數不為0,則說明傳送的數據有誤!這樣完成CRC校驗。 
即發送端要發送1111,先在1111后加000,變成1111000,再除以1001得到余數110,這個110
就是CRC,將110加到數據后面,變成1111110,發送出去。 
接收端收到1111110,用它除以1001,計算得余數為000,就說明收到的數據正確。 
所以原始數據后面要先擴展出3位0,以容納CRC值! 
會發現,在上面的除法過程中,這3位0,能保證所有的4個數據位在除法時都能夠被處理到!不然做
一次除法就到結果了,那是不對的。這個概念后面要用到。 

 

二、  生成項 
上面例子中,生成項是1001,共4位比特,最高位的1,實際上在除法的每次XOR時,都要消掉,所
以這個1可不做參考,后3位001才是最重要的!001有3位,所以得到的余數也是3位,因為最后一次除
法XOR時,最高位消掉了。所以CRC就是3位比特的。 
CRC是3比特,表示它的寬度W=3。也就是說,原始數據后面要加上W=3比特的0進行擴展! 
生成項的最低位也必須是1,這是規定的。 
生成項1001,就等效於g(x)=x2+1 
生成項也可以倒過來寫,即顛倒過來,寫成1001,這里倒過來的值是一樣的。 
 
再如CRC32的生成項是: 
1 0000 0100 1100 0001 0001 1101 1011 0111  (33個比特) 
即g(x)= x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1 
顛倒過來,就可以寫成1110 1101 1011 1000 1000 0011 0010 0000 1 
一般生成項簡寫時不寫最高位的1,故生成項是0x04C11DB7,顛倒后的生成項是0xEDB88320 
CRC32的生成項是33比特,最高位是消掉的,即CRC值是32比特(4個字節),即寬度W=32,就是說,
在計算前,原始數據后面要先擴展W=32個比特0,即4個0x00字節。 
 
注意:我看到網上CRC32的POLY有0x04C10DB7這個值的,它和正規的POLY值不同,需要注意! 
 
顛倒過來,即是鏡像,為什么要顛倒,后述。 

拿運 CRC CCITT-16 為例子,行下面的代碼,CRC_acc = 0xFFFF;CRC_input[0] = 0x63; 返回校驗值CRC.

unsigned short UpdateCRC(unsigned short CRC_acc, unsigned char *CRC_input, unsigned int len)
{
	unsigned char i,k = 0; 
	#define POLY_  0x1021
	while (len--)
	{
		CRC_acc = CRC_acc ^ (CRC_input[k++] << 8);
		printf("CRC_input:%d   : %s\n", CRC_input[0], itoa(CRC_input[0], binbuf, 2));
		printf("CRC_input:%x :%s\n", CRC_acc, itoa(CRC_acc, binbuf, 2));
		printf("POLY_    :%x :%s\n\n", POLY_, itoa(POLY_, binbuf, 2));
		printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
		for (i = 0; i < 8; i++)
		{
			if ((CRC_acc & 0x8000) == 0x8000)
			{
				printf("chushi   :%x    :%s\n", 0, itoa(CRC_acc, binbuf, 2));
				CRC_acc = CRC_acc << 1;
				printf("you yi   :%x    :%s\n", 0, itoa(CRC_acc, binbuf, 2));
				CRC_acc ^= POLY_;
				printf("POLY_1   :%x    :%s\n\n", 0, itoa(CRC_acc, binbuf, 2));
			}
			else
			{
				printf("chushi   :%x    :%s\n", 0, itoa(CRC_acc, binbuf, 2));
				CRC_acc = CRC_acc << 1;
				printf("POLY_0   :%x    :%s\n\n", 0, itoa(CRC_acc, binbuf, 2));
			}
		}
	}
	return CRC_acc;
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM