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校驗類似於上面的實現。