原理
原理我就不詳細介紹了,維基百科什么的有很多,大概意思就是這么一套操作過后可以極大概率地檢測出數據錯誤。我又怎么會告訴你們我也沒有完全看懂。
實現
直接計算法
/**
* CRC32 校驗函數
* @param pdata 數據指針
* @param len 數據長度
* @param crc 初值或上段數據校驗結果
* @param poly 多項式
*/
uint32_t CRC32(const uint8_t* pdata, int len, uint32_t crc, uint32_t poly)
{
for (int i = 0; i < len; i++, pdata++)
{
crc ^= ((uint32_t)*pdata << 24);
for (int bits = 0; bits < 8; bits++)
{
if (crc & 0x80000000)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
}
return crc;
}
稍微解釋一下代碼,大概步驟是:
- 把新的數據 xor 到之前數據的最高 byte
- 判斷最高 bit,如果是 0 就左移,是 1 就左移后 xor poly
- 處理完最高 8 個 bits 結束
網上有些例程是右移處理,其實本質是一樣的,只要生成 CRC 的程序和校驗它的程序是一致的就可以。
為了證明我不是隨便找了一段沒驗證過的代碼就貼出來,我這里給出一個 CRC Online Calculator,我貼出的代碼能夠得出和它一致的結果,各位自己調試代碼的時候也可以有個參考。
使用的時候有幾點注意:
- 選擇 CRC-32
- 由于我給出的代碼沒有進行位翻轉操作,所以選擇 Custom,然后兩個 reflected 的鉤去掉
- Final Xor Value 填 0
- 剩下的你應該懂的
雖然這段代碼簡單明白,但是網上類似的東西也不少,發個貼子沒點干貨似乎不太好意思。那么我就多給點料。
查表法
了解多一些的朋友會知道,雖然直接計算的方法代碼特別簡單,但是循環次數太多效率不夠高。根據 xor 運算的結合律特性,可以使用查表法提高運行效率。
static uint32_t gsTable[256];
void CRC32_TableInit(uint32_t poly)
{
for(uint32_t i = 0; i < 256; i++)
{
uint32_t crc = i << 24;
for (int bits = 0; bits < 8; bits++)
{
if (crc & 0x80000000)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
gsTable[i] = crc;
}
}
uint32_t CRC32_Table(const uint8_t* pdata, int len, uint32_t crc)
{
uint8_t index;
for (int i = 0; i < len; i++, pdata++)
{
index = *pdata ^ (crc >> 24);
crc = (crc << 8) ^ gsTable[index];
}
return crc;
}
對比上面的代碼,其實就是把 8 bits 的循環抽了出來。從直接計算的代碼我們看出,這八次循環的計算效果,其實完全取決于新數據 xor 之后 crc 最高 byte 的值。那么我們如果把這一個 byte 所有可能的值對應的計算結果先存起來,就得到了一個 256 數據的表。
使用的時候就拿 xor 之后的最高 byte 去查表,然后把 crc 直接左移 8 個 bits,再進行一次 xor 就妥了。
有些朋友會疑惑,這樣一番操作是不是真的能得到同樣的結果。我們可以觀察一下表的值,發現它的第 0 個值必然是 0,而第 1 個值,必然等于 poly。為什么呢?因為如果最高 byte 是 0 的話,數據除了會左移 8 個 bits 以外,剩下的 bits 都不會改變(xor 0 值也不變)。而最高 byte 如果是 1 的話,那么它會在第 8 次左移之后 xor 一次 poly。可以看出至少在這兩種情況的時候,兩段代碼的計算效果是等價的,而后面的更多種情況就只能自己體會一下了。
查表法是挺不錯,但是網上其實也能找得到。所以呢,就再上一份干貨,這是我實際踩過的坑。
STM32 CRC32
下面的代碼,與 STM32 中自帶的 CRC32 模塊的計算效果是等效的。
#define CRC_STM32_POLY 0x04c11db7
#define CRC_STM32_INIT_VALUE 0xFFFFFFFF
uint32_t STM32_CRC32(const uint32_t* pdata, int len, uint32_t crc, uint32_t poly)
{
for (int i = 0; i < len; i++, pdata++)
{
crc ^= *pdata;
for (int bits = 0; bits < 32; bits++)
{
if ((crc & 0x80000000) != 0)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
}
return crc;
}
那么它和我們上面一般的 CRC 運算有什么不同呢?細心的朋友可能發現了,它的計算是 32 bits 一輪的,而由于其是小端存儲,所以其實是從第四個 byte 的最高 bit 開始計算的。和從第一個 byte 開始計算的 CRC 結果是不一樣的,而且輸入數據 bytes 個數必須補零到 4 的整數倍。
如果你以為它和一開始介紹的算法一樣,就會被它坑到 T^T。而它的算法是內置的,沒得修改,所以只要你想用,就必須按它的來。
好了,干貨上完,感謝閱讀。
Jalon 原創,轉載署名,商用聯系本人。