1. 程式人生 > >CRC16校驗碼C語言實現

CRC16校驗碼C語言實現

一、目的

 闡述CRC16的原理,並以C語言程式碼實現。

二、 校驗碼的作用

 校驗碼用於校驗資料的有效性/正確性。

校驗碼用原資料生成,並伴隨原資料一起傳送/儲存,使用者拿到傳送/儲存的資料序列後,取出原資料部分,根據校驗碼生成規則生成校驗碼,與拿到的校驗碼進行比較即可判斷資料是否有效/正確。

三、 CRC校驗碼生成過程

 任意一個數據序列都可以用二進位制表示,如整數123可以用二進位制表示為1111011B。對二進位制數模2除以一個N位的二進位制數,餘數應該是N-1位。而模2除相當於找到為1的最高位,作異或運算。

舉個例子來看取餘的過程:

123對25取餘,亦即1111011B對11001取餘,可以先將1111011B右移(5-1)位,用來儲存餘數,得

11110110000B

11001B作異或,得餘數為:

111110000B

   11001B作異或,餘數為:

1100000B對

11001B作異或,餘數為:

0100

剩餘這4位就是最終結果。

四、C實現

我們模仿上述過程,假定一個N位的數a要對M位生成多項式序列做CRC校驗,對應的C語言程式碼應該是:

BitsG_t CRCM(Bits_t data, BitsG_t crcGen)
{
	int i;
	data <<= (M - 1);
	for(i = N - 1; i >= 0; --i)
	{
		Bits_t bit = ((Bits_t)1) << i;
		if(bit & data) //找到為1的位才做異或運算
		{
			data ^= ((Bits_t)crcGen << (i - (M - 1)));
		}
	}
	return data;
}

當然,我們求CRC一般都不是一個Bits_t這種型別的數,而更可能的是一個位元組陣列。

我們注意到,其實上述資料處理是有冗餘的,比如,找到最的最高位跟生成多項式序列的最高位都是1,異或結果肯定是1,也就是說,M位生成多項式序列,其實有M-1位參與異或運算即可。

所以,在許多的CRC生成程式碼裡,都省略了最高位,比如CRC16裡通常採用的0x1021,事實上代表著0x11021。

對於這種省略了最高位的生成多項式序列,就可以很直觀地放在一個數據型別了,比如CRC16的生成多項式序列,省略最高位,可以放在uint16_t裡,CRC32可以放在uint32_t裡了。

我們上面有提到一點,就是求CRC的資料通常是一個位元組陣列,那麼對於位元組陣列,我們該怎麼求CRC呢?

注意到,源資料中參與判斷的是否做異或運算(改變位的值)的只有最高位,那麼儲存判別式所需要的記憶體僅僅需要1位,任何計算機儲存資料型別都是可以的。

然後,做異或運算需要的位數是M-1,故計算過程實際只需要M-1位的數。

另外,多個數的異或操作,其順序是可以調換的,也就是說,對於位元組流,可以為下一個位元組保留位置,直到需要讀取下一個位元組的位用以判斷時,才對其進行異或。

對一個位元組進行CRC的C程式碼如下所示(以CRC16為例):

/**
	@param data 要進行CRC校驗的資料
	@param crcGenLow CRC校驗碼的生成序列的低16位(要生成16位,需要17位生成序列,最高位固定為1,省略)
	@param remainder 上一個位元組操作生成的校驗碼的,其高8位未與data進行異或
	@return 對data進行CRC生成的碼
*/
uint16_t CRC16_Byte(uint8_t data, uint16_t crcGenLow, uint16_t remainder)
{
	int i;
	remainder ^= ((uint16_t)data) << 8;
	for(i = 0; i < 8; ++i)
	{
		bool bXOR = 0x8000 & remainder;
		remainder <<= 1;
		if(bXOR)
		{
			remainder ^= crcGenLow;
		}
	}
	return remainder;
}

remainder對於第一個位元組來說,可以設定為0,函式內的第一個異或操作相當於將第一個位元組設定到了高8位進行CRC校驗,也就是說,對多位元組進行CRC16校驗,函式

BitsG_t CRCM(Bits_t data, BitsG_t crcGen)

可以寫為:

uint16_t CRC16(const uint8_t * pData, int nData, uint16_t crcGenLow)
{
	uint16_t remainder = 0;
	int i;
	for(i = 0; i < nData; ++i)
	{
		remainder = CRC16_Byte(pData[i], crcGenLow, remainder);
	}
	return remainder;
}

至此,CRC16校驗碼的C語言實現已完成,對於其他位數的CRC校驗類似於上面的實現。