簡介
循環冗餘校驗(Cyclic Redundancy Check, CRC)是一種根據網絡數據包或計算機文件等數據產生簡短固定位數校驗碼的一種信道編碼技術,主要用來檢測或校驗數據傳輸或者保存後可能出現的錯誤。它是利用除法及餘數的原理來作錯誤偵測的。
CRC參數模型
參數名稱 | 參數意義 |
---|---|
Name | CRC校驗的名稱,存在許多用於特定場合的CRC校驗模型,比如“CRC-16/MODBUS” |
Width | 代表生成項的長度 |
Poly | 多項式碼 |
Init | CRC校驗碼的初始值嗎,數據在進行上述二項式運算之前,需要先將要計算的數據與初始值進行異或,然後再與多項式進行計算,其作用是可以保留數據頭部的初始零(如果與0xFFFF異或,則頭部省略的零變成1進行運算) |
RefIn | 輸入數據是否反轉,通常與數據的接受方式有關,比如MODBUS是先接受低位數據,所以就需要對輸入數據進行反轉,可以在接收端“負負得正” |
XorOut | 最後的CRC校驗碼與XorOUt進行異或後再輸出 |
RefOut | 輸出的CRC校驗碼是否反轉,一般於RefIn配對 |
- 不同的二項式、初始值、結果異或值、反轉原則都會造成最終的結果不一致,所以存在許多模型參數不同的CRC校驗,但是隻要發送方和接收方約定相同,就可以成功校驗。
- 出現數據反轉的原因是由於一些協議傳輸數據是從低位開始的,比如:因爲Modbus在傳輸數據的時候是低位優先的,也就是它是從0位出去的,然後接收端是從0位接收的。所以RefIn和RefOut一般是成對出現的。
多項式碼
性質
- 生成多項式的最高位和最低位必須爲1。
- 當被傳送信息(CRC碼)任何一位發生錯誤時,被生成多項式做模2除後,應該使餘數不爲0。
- 不同位發生錯誤時,應該使餘數不同。
- 對餘數繼續做模2除,應使餘數循環。
特點
- 多項式碼的首位1可以忽略
* 因爲在實現往往是檢測數據的置1數據位,然後跳過,對剩下的取餘。既跳過了數據的置1數據位,也跳過了多項式碼的首位1.
實例
- x16 + x15 + x2 + 1對應的多項式碼是1’0x8005(1000 0000 0000 0101B),其首位的1可以忽略,其反向校驗的值爲1’0xA001(1010 0000 0000 0001B)。
實現方式
從理解原理到實際實現,是有很多技巧可以使用的。(在Qt中實現)
- 直接計算法
unsigned short crc_check(QByteArray data) { /* CRC16參數模型 */ // 模型名稱:CRC-16/SERIALPORT // 初始值:0xFFFF // 校驗方向:反向 // 多項式檢驗碼:0x8005(反向0xA001):x16 + x12 + x2 + 1 // 輸入反向:否 // 輸出反向:否 // 實現算法:直接算法 unsigned short temp = 0x0000; int i,j; /* 處理過程不要與"模二除法"混淆,應該把每個數據獨立出來看待,否則會解釋不通temp ^= (unsigned char)data.at(i) */ // 反向校驗 for (i = 0;i < data.size(); i++) { // 第一次處理是與CRC初始值進行異或,後面是數據對前面一個數據單獨異或得到的餘數進行異或 temp ^= ((unsigned char)data.at(i) << 8); for (j = 0; j < 8; j++) { if (temp & 0x8000) temp = (temp << 1) ^ 0x8005; else temp = temp << 1; } } return temp; }
- 查表法
- CRC校驗的數據往往是以字節形式存儲,一個字節有8個bit,其範圍爲0x00 ~ 0xFF,既0 - 255,大小是固定的,而且並不是很大, 所以我們可以實現構造表,可以直接得到字節對應的CRC校驗碼。
- 其實質就是將這一段代碼給封裝起來了,使用表的形式存儲。
for (j = 0; j < 8; j++) { if (temp & 0x01) temp = (temp >> 1) ^ 0xA001; else temp = temp >> 1; }
- 輸入反轉鏡像算法
unsigned short crc16_check(QByteArray data) { /* CRC16參數模型 */ // 模型名稱:CRC-16/MODBUS // 初始值:0xFFFF // 校驗方向:反向 // 多項式檢驗碼:0x8005(反向0xA001):x16 + x12 + x2 + 1 // 輸入反向:是 // 輸出反向:是 // 實現算法:鏡像算法(使用右移替代左移,使得數據不用反向再來處理 unsigned short temp = 0xFFFF; int i,j; /* 處理過程不要與"CRC除法"混淆,應該把每個數據獨立出來看待,否則會解釋不通temp ^= (unsigned char)data.at(i) */ // 反向校驗 for (i = 0;i < data.size(); i++) { // 第一次處理是與CRC初始值進行異或,後面是數據對前面一個數據單獨異或得到的餘數進行異或 temp ^= (unsigned char)data.at(i); for (j = 0; j < 8; j++) { if (temp & 0x01) temp = (temp >> 1) ^ 0xA001; else temp = temp >> 1; } } return temp; }
- 可以通過下圖[1]來理解
temp ^= (unsigned char)data.at(i)
,圖中數據長度爲5,如果替換成8就是我們實現中的代碼流程。應該將一個數據的"模二除法”獨立出來看待,第一個數據“模二除法”後的餘數再與第二個數據異或。同時可以理解爲使用到了異或的組合率:A XOR B XOR C XOR D = A XOR (B XOR C XOR D),這裏的A可以理解爲第二個字符11011,而(B XOR C XOR D)可以理解爲多項式碼在第一個字符操作期間,在第二個字符區域的最終異或結果。
- 可以通過下圖[1]來理解
常見CRC校驗模型
模型名稱 | 多項式公式 | 寬度 | 多項式 | 初始值 | 結果異或值 | 輸入反轉 | 輸出反轉 |
---|---|---|---|---|---|---|---|
CRC-8 | x8 + x2 + x + 1 | 8 | 07 | 00 | 00 | false | false |
CRC-16/USB | x16 + x15 + x2 + 1 | 16 | 8005 | FFFF | FFFF | true | true |
CRC-16/MODBUS | x16 + x15 + x2 + 1 | 16 | 8005 | FFFF | 0000 | true | true |
CRC-32 | x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 | 32 | 04C11DB7 | FFFFFFFF | FFFFFFFF | true | true |
參考資料
- https://segmentfault.com/a/1190000018094567?utm_source=tag-newest
- http://blog.sina.com.cn/s/blog_17e2ca0810102z3dz.html
- http://blog.sina.com.cn/s/blog_17e2ca0810102z3e0.html
- CRC校驗測試網站
- https://baike.baidu.com/item/CRC/1453359?fr=aladdin