文章轉自 循環冗餘校驗(CRC)算法入門引導 - Ivan 的專欄 - 博客頻道 - CSDN.NET
web
http://blog.csdn.net/liyuanbhu/article/details/7882789算法
1、原理部分編程
CRC 算法的基本思想是將傳輸的數據當作一個位數很長的數,將這個數除以另外一個數,獲得的餘數做爲校驗數據附加到原數據後面。除法採用正常的多項式乘除法,而加減法都採用模2運算。模2運算就是結果除以2後取餘數,如3 mod 2 = 1,在計算機中就是異或運算:app
例如:ide
要傳輸的數據爲:1101011011函數
除數設爲:10011oop
在計算前先將原始數據後面填上4個0:11010110110000,之因此要補0,補0的個數就是獲得的餘數位數。大數據
採用了模2的加減法後,不須要考慮借位的問題。最後獲得的餘數就是CRC 校驗字。爲了進行CRC運算,也就是這種特殊的除法運算,必需要指定個被除數,在CRC算法中,這個被除數有一個專有名稱叫作「生成多項式」。文獻中提到的生成多項式常常會說到多項式的位寬(Width,簡記爲W),這個位寬不是多項式對應的二進制數的位數,而是位數減1。好比CRC8中用到的位寬爲8的生成多項式,其實對應得二進制數有九位:100110001。ui
2、編程實現編碼
假設咱們的生成多項式爲:100110001(簡記爲0x31),也就是CRC-8
則計算步驟以下:
(1) 將CRC寄存器(8-bits,比生成多項式少1bit)賦初值0
(2) 在待傳輸信息流後面加入8個0
(3) While (數據未處理完)
(4) Begin
(5) If (CRC寄存器首位是1)
(6) reg = reg XOR 0x31
(7) CRC寄存器左移一位,讀入一個新的數據於CRC寄存器的0 bit的位置。
(8) End
(9) CRC寄存器就是咱們所要求的餘數。
實際上,真正的CRC 計算一般與上面描述的還有些出入。這是由於這種最基本的CRC除法有個很明顯的缺陷,就是數據流的開頭添加一些0並不影響最後校驗字的結果。所以真正應用的CRC 算法基本都在原始的CRC算法的基礎上作了些小的改動。
所謂的改動,也就是增長了兩個概念,第一個是「餘數初始值」,第二個是「結果異或值」。
所謂的「餘數初始值」就是在計算CRC值的開始,給CRC寄存器一個初始值。「結果異或值」是在其他計算完成後將CRC寄存器的值在與這個值進行一下異或操做做爲最後的校驗值。
常見的三種CRC 標準用到個各個參數以下表。
|
CCITT |
CRC16 |
CRC32 |
校驗和位寬W |
16 |
16 |
32 |
生成多項式 |
x16+x12+x5+1 |
x16+x15+x2+1 |
x32+x26+x23+x22+x16+ x12+x11+x10+x8+x7+x5+ x4+x2+x1+1 |
除數(多項式) |
0x1021 |
0x8005 |
0x04C11DB7 |
餘數初始值 |
0xFFFF |
0x0000 |
0xFFFFFFFF |
結果異或值 |
0x0000 |
0x0000 |
0xFFFFFFFF |
加入這些變形後,常見的算法描述形式就成了這個樣子了:
(1) 設置CRC寄存器,並給其賦值爲「餘數初始值」。
while(數據未處理完)
begin(8_bit)
(2) 將數據的第一個8-bit字符與CRC寄存器進行異或,並把結果存入CRC寄存器。
(3) CRC寄存器向右移一位,MSB補零,移出並檢查LSB。
(4) 若是LSB爲0,重複第三步;若LSB爲1,CRC寄存器與0x31相異或。
(5) 重複第3與第4步直到8次移位所有完成。此時一個8-bit數據處理完畢。
end(8-bit)
(6) 重複第2至第5步直到全部數據所有處理完成。
(7) 最終CRC寄存器的內容與「結果異或值」進行或非操做後即爲CRC值。
示例性的C代碼以下所示,由於效率很低,項目中如對計算時間有要求應該避免採用這樣的代碼。這個代碼有一個crc的參數,能夠將上次計算的crc結果傳入函數中做爲此次計算的初始值,這對大數據塊的CRC計算是頗有用的,不須要一次將全部數據讀入內存,而是讀一部分算一次,全讀完後就計算完了。這對內存受限系統仍是頗有用的。
上面的算法對數據流逐位進行計算,效率很低。實際上仔細分析CRC計算的數學性質後咱們能夠多位多位計算,最經常使用的是一種按字節查表的快速算法。該算法基於這樣一個事實:計算本字節後的CRC碼,等於上一字節餘式CRC碼的低8位左移8位,加上上一字節CRC右移 8位和本字節之和後所求得的CRC碼。若是咱們把8位二進制序列數的CRC(共256個)所有計算出來,放在一個表裏,編碼時只要從表中查找對應的值進行處理便可。
按照這個方法,能夠有以下的代碼(這個代碼來自Micbael Barr的書「Programming Embedded Systems in C and C++」 ):
上面代碼中crcInit() 函數用來計算crcTable,所以在調用 crcCompute 前必須先調用 crcInit()。