1. 程式人生 > >CRC(迴圈冗餘校驗)演算法入門

CRC(迴圈冗餘校驗)演算法入門

寫給嵌入式程式設計師的迴圈冗餘校驗(CRC)演算法入門引導

前言

CRC校驗(迴圈冗餘校驗)是資料通訊中最常採用的校驗方式。在嵌入式軟體開發中,經常要用到CRC 演算法對各種資料進行校驗。因此,掌握基本的CRC演算法應是嵌入式程式設計師的基本技能。可是,我認識的嵌入式程式設計師中能真正掌握CRC演算法的人卻很少,平常在專案中見到的CRC的程式碼多數都是那種效率非常低下的實現方式。

其實,在網上有一篇介紹CRC 演算法的非常好的文章,作者是Ross Williams,題目叫:“A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS”。我常將這篇文章推薦給向我詢問CRC演算法的朋友,但不少朋友向我抱怨原文太長了,而且是英文的。希望我能寫篇短點的文章,因此就有了本文。不過,我的水平比不了Ross Williams,我的文章肯定也沒Ross Williams的寫的好。因此,閱讀英文沒有障礙的朋友還是去讀Ross Williams的原文吧。

本文的讀者群設定為軟體開發人員,尤其是從事嵌入式軟體開發的程式設計師,而不是專業從事數學或通訊領域研究的學者(我也沒有這個水平寫的這麼高深)。因此,本文的目標是介紹CRC演算法的基本原理和實現方式,用到的數學儘量控制在高中生可以理解的深度。

另外,鑑於大多數嵌入式程式設計師都是半路出家轉行過來的,不少人只會C語言。因此,文中的示例程式碼全部採用C語言來實現。作為一篇入門短文,文中給出的程式碼更注重於示範性,儘可能的保持易讀性。因此,文中的程式碼並不追求最高效的實現,但對於一般的應用卻也足夠快速了。

從奇偶校驗說起

所謂通訊過程的校驗是指在通訊資料後加上一些附加資訊,通過這些附加資訊來判斷接收到的資料是否和傳送出的資料相同。比如說RS232序列通訊可以設定奇偶校驗位,所謂奇偶校驗就是在傳送的每一個位元組後都加上一位,使得每個位元組中1的個數為奇數個或偶數個。比如我們要傳送的位元組是0x1a,二進位制表示為0001 1010。

採用奇校驗,則在資料後補上個0,資料變為0001 1010 0,資料中1的個數為奇數個(3個)

採用偶校驗,則在資料後補上個1,資料變為0001 1010 1,資料中1的個數為偶數個(4個)

接收方通過計算資料中1個數是否滿足奇偶性來確定資料是否有錯。

奇偶校驗的缺點也很明顯,首先,它對錯誤的檢測概率大約只有50%。也就是隻有一半的錯誤它能夠檢測出來。另外,每傳輸一個位元組都要附加一位校驗位,對傳輸效率的影響很大。因此,在高速資料通訊中很少採用奇偶校驗。奇偶校驗優點也很明顯,它很簡單,因此可以用硬體來實現,這樣可以減少軟體的負擔。因此,奇偶校驗也被廣泛的應用著。

奇偶校驗就先介紹到這來,之所以從奇偶校驗說起,是因為這種校驗方式最簡單,而且後面將會知道奇偶校驗其實就是CRC 校驗的一種(CRC-1)。

累加和校驗

另一種常見的校驗方式是累加和校驗。所謂累加和校驗實現方式有很多種,最常用的一種是在一次通訊資料包的最後加入一個位元組的校驗資料。這個位元組內容為前面資料包中全部資料的忽略進位的按位元組累加和。比如下面的例子:

我們要傳輸的資訊為: 6、23、4

加上校驗和後的資料包:6、23、4、33

這裡 33 為前三個位元組的校驗和。接收方收到全部資料後對前三個資料進行同樣的累加計算,如果累加和與最後一個位元組相同的話就認為傳輸的資料沒有錯誤。

累加和校驗由於實現起來非常簡單,也被廣泛的採用。但是這種校驗方式的檢錯能力也比較一般,對於單位元組的校驗和大概有1/256 的概率將原本是錯誤的通訊資料誤判為正確資料。之所以這裡介紹這種校驗,是因為CRC校驗在傳輸資料的形式上與累加和校驗是相同的,都可以表示為:通訊資料 校驗位元組(也可能是多個位元組)

初識 CRC 演算法

CRC 演算法的基本思想是將傳輸的資料當做一個位數很長的數。將這個數除以另一個數。得到的餘數作為校驗資料附加到原資料後面。還以上面例子中的資料為例:

6、23、4 可以看做一個2進位制數: 0000011000010111 00000010

假如被除數選9,二進位制表示為:1001

則除法運算可以表示為:

可以看到,最後的餘數為1。如果我們將這個餘數作為校驗和的話,傳輸的資料則是:6、23、4、1

CRC 演算法和這個過程有點類似,不過採用的不是上面例子中的通常的這種除法。在CRC演算法中,將二進位制資料流作為多項式的係數,然後進行的是多項式的乘除法。還是舉個例子吧。

比如說我們有兩個二進位制數,分別為:1101 和1011。

1101 與如下的多項式相聯絡:1x3+1x2+0x1+1x0=x3+x2+x0

1011與如下的多項式相聯絡:1x3+0x2+1x1+1x0=x3+x1+x0

兩個多項式的乘法:(x3+x2+x0)(x3+x1+x0)=x6+x5+x4+x3+x3+x3+x2+x1+x0

得到結果後,合併同類項時採用模2運算。也就是說乘除法採用正常的多項式乘除法,而加減法都採用模2運算。所謂模2運算就是結果除以2後取餘數。比如3 mod 2 = 1。因此,上面最終得到的多項式為:x6+x5+x4+x3+x2+x1+x0,對應的二進位制數:111111

加減法採用模2運算後其實就成了一種運算了,就是我們通常所說的異或運算:

0+0=0

0+1=1

1+0=1

1+1=0

0-0=0

1-0=1

0-1=1

1-1=0

上面說了半天多項式,其實就算是不引入多項式乘除法的概念也可以說明這些運算的特殊之處。只不過幾乎所有講解 CRC 演算法的文獻中都會提到多項式,因此這裡也簡單的寫了一點基本的概念。不過總用這種多項式表示也很羅嗦,下面的講解中將盡量採用更簡潔的寫法。

除法運算與上面給出的乘法概念類似,還是遇到加減的地方都用異或運算來代替。下面是一個例子:

要傳輸的資料為:1101011011

除數設為:10011

在計算前先將原始資料後面填上4個0:11010110110000,之所以要補0,後面再做解釋。

從這個例子可以看出,採用了模2的加減法後,不需要考慮借位的問題,所以除法變簡單了。最後得到的餘數就是CRC 校驗字。為了進行CRC運算,也就是這種特殊的除法運算,必須要指定個被除數,在CRC演算法中,這個被除數有一個專有名稱叫做“生成多項式”。生成多項式的選取是個很有難度的問題,如果選的不好,那麼檢出錯誤的概率就會低很多。好在這個問題已經被專家們研究了很長一段時間了,對於我們這些使用者來說,只要把現成的成果拿來用就行了。

最常用的幾種生成多項式如下:

CRC8=X8+X5+X4+X0

CRC-CCITT=X16+X12+X5+X0

CRC16=X16+X15+X2+X0

CRC12=X12+X11+X3+X2+X0

CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+X0

有一點要特別注意,文獻中提到的生成多項式經常會說到多項式的位寬(Width,簡記為W),這個位寬不是多項式對應的二進位制數的位數,而是位數減1。比如CRC8中用到的位寬為8的生成多項式,其實對應得二進位制數有九位:100110001。另外一點,多項式表示和二進位制表示都很繁瑣,交流起來不方便,因此,文獻中多用16進位制簡寫法來表示,因為生成多項式的最高位肯定為1,最高位的位置由位寬可知,故在簡記式中,將最高的1統一去掉了,如CRC32的生成多項式簡記為04C11DB7實際上表示的是104C11DB7。當然,這樣簡記除了方便外,在程式設計計算時也有它的用處。

對於上面的例子,位寬為4(W=4),按照CRC演算法的要求,計算前要在原始資料後填上W個0,也就是4個0。

位寬W=1的生成多項式(CRC1)有兩種,分別是X1和X1+X0,讀者可以自己證明10 對應的就是奇偶校驗中的奇校驗,而11對應則是偶校驗。因此,寫到這裡我們知道了奇偶校驗其實就是CRC校驗的一種特例,這也是我要以奇偶校驗作為開篇介紹的原因了。

CRC演算法的程式設計實現

說了這麼多總算到了核心部分了。從前面的介紹我們知道CRC校驗核心就是實現無借位的除法運算。下面還是通過一個例子來說明如何實現CRC校驗。

假設我們的生成多項式為: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暫存器,並給其賦值為“餘數初始值”。

(2)      將資料的第一個8-bit字元與CRC暫存器進行異或,並把結果存入CRC暫存器。

(3)      CRC暫存器向右移一位,MSB補零,移出並檢查LSB。

(4)      如果LSB為0,重複第三步;若LSB為1,CRC暫存器與0x31相異或。

(5)      重複第3與第4步直到8次移位全部完成。此時一個8-bit資料處理完畢。

(6)      重複第2至第5步直到所有資料全部處理完成。

(7)      最終CRC暫存器的內容與“結果異或值”進行或非操作後即為CRC值。

示例性的C程式碼如下所示,因為效率很低,專案中如對計算時間有要求應該避免採用這樣的程式碼。不過這個程式碼已經比網上常見的計算程式碼要好了,因為這個程式碼有一個crc的引數,可以將上次計算的crc結果傳入函式中作為這次計算的初始值,這對大資料塊的CRC計算是很有用的,不需要一次將所有資料讀入記憶體,而是讀一部分算一次,全讀完後就計算完了。這對記憶體受限系統還是很有用的。

  1. #define POLY 0x1021
  2. /**
  3. * Calculating CRC-16 in 'C'
  4. * @para addr, start of data
  5. * @para num, length of data
  6. * @para crc, incoming CRC
  7. */
  8. uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
  9. {
  10. int i;
  11. for (; num > 0; num--) /* Step through bytes in memory */
  12. {
  13. crc = crc ^ (*addr++ << 8); /* Fetch byte from memory, XOR into CRC top byte*/
  14. for (i = 0; i < 8; i++) /* Prepare to rotate 8 bits */
  15. {
  16. if (crc & 0x8000) /* b15 is set... */
  17. crc = (crc << 1) ^ POLY; /* rotate and XOR with polynomic */
  18. else /* b15 is clear... */
  19. crc <<= 1; /* just rotate */
  20. } /* Loop for 8 bits */
  21. crc &= 0xFFFF; /* Ensure CRC remains 16-bit value */
  22. } /* Loop until num=0 */
  23. return(crc); /* Return updated CRC */
  24. }

下面對這個函式給出個例子片段程式碼:

  1. unsigned char data1[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
  2. unsigned char data2[] = {'5', '6', '7', '8', '9'};
  3. unsigned short c1, c2;
  4. c1 = crc16(data1, 9, 0xffff);
  5. c2 = crc16(data1, 4, 0xffff);
  6. c2 = crc16(data2, 5, c2);
  7. printf("%04x\n", c1);
  8. printf("%04x\n", c2);

讀者可以驗算,c1、c2 的結果都為 29b1。上面程式碼中crc 的初始值之所以為0xffff,是因為CCITT標準要求的除數初始值就是0xffff。

上面的演算法對資料流逐位進行計算,效率很低。實際上仔細分析CRC計算的數學性質後我們可以多位多位計算,最常用的是一種按位元組查表的快速演算法。該演算法基於這樣一個事實:計算本位元組後的CRC碼,等於上一字節餘式CRC碼的低8位左移8位,加上上一位元組CRC右移 8位和本位元組之和後所求得的CRC碼。如果我們把8位二進位制序列數的CRC(共256個)全部計算出來,放在一個表裡,編碼時只要從表中查詢對應的值進行處理即可。

按照這個方法,可以有如下的程式碼(這個程式碼也不是我寫的,是我在Micbael Barr的書“Programming Embedded Systems in C and C++” 中找到的,同樣,我做了點小小的改動。):

  1. /*
  2. crc.h
  3. */
  4. #ifndef CRC_H_INCLUDED
  5. #define CRC_H_INCLUDED
  6. /*
  7. * The CRC parameters. Currently configured for CCITT.
  8. * Simply modify these to switch to another CRC Standard.
  9. */
  10. /*
  11. #define POLYNOMIAL 0x8005
  12. #define INITIAL_REMAINDER 0x0000
  13. #define FINAL_XOR_VALUE 0x0000
  14. */
  15. #define POLYNOMIAL 0x1021
  16. #define INITIAL_REMAINDER 0xFFFF
  17. #define FINAL_XOR_VALUE 0x0000
  18. /*
  19. #define POLYNOMIAL 0x1021
  20. #define POLYNOMIAL 0xA001
  21. #define INITIAL_REMAINDER 0xFFFF
  22. #define FINAL_XOR_VALUE 0x0000
  23. */
  24. /*
  25. * The width of the CRC calculation and result.
  26. * Modify the typedef for an 8 or 32-bit CRC standard.
  27. */
  28. typedef unsigned short width_t;
  29. #define WIDTH (8 * sizeof(width_t))
  30. #define TOPBIT (1 << (WIDTH - 1))
  31. /**
  32. * Initialize the CRC lookup table.
  33. * This table is used by crcCompute() to make CRC computation faster.
  34. */
  35. void crcInit(void);
  36. /**
  37. * Compute the CRC checksum of a binary message block.
  38. * @para message, 用來計算的資料
  39. * @para nBytes, 資料的長度
  40. * @note This function expects that crcInit() has been called
  41. * first to initialize the CRC lookup table.
  42. */
  43. width_t crcCompute(unsigned char * message, unsigned int nBytes);
  44. #endif // CRC_H_INCLUDED
  1. /*
  2. *crc.c
  3. */
  4. #include "crc.h"
  5. /*
  6. * An array containing the pre-computed intermediate result for each
  7. * possible byte of input. This is used to speed up the computation.
  8. */
  9. static width_t crcTable[256];
  10. /**
  11. * Initialize the CRC lookup table.
  12. * This table is used by crcCompute() to make CRC computation faster.
  13. */
  14. void crcInit(void)
  15. {
  16. width_t remainder;
  17. width_t dividend;
  18. int bit;
  19. /* Perform binary long division, a bit at a time. */
  20. for(dividend = 0; dividend < 256; dividend++)
  21. {
  22. /* Initialize the remainder. */
  23. remainder = dividend << (WIDTH - 8);
  24. /* Shift and XOR with the polynomial. */
  25. for(bit = 0; bit < 8; bit++)
  26. {
  27. /* Try to divide the current data bit. */
  28. if(remainder & TOPBIT)
  29. {
  30. remainder = (remainder << 1) ^ POLYNOMIAL;
  31. }
  32. else
  33. {
  34. remainder = remainder << 1;
  35. }
  36. }
  37. /* Save the result in the table. */
  38. crcTable[dividend] = remainder;
  39. }
  40. } /* crcInit() */
  41. /**
  42. * Compute the CRC checksum of a binary message block.
  43. * @para message, 用來計算的資料
  44. * @para nBytes, 資料的長度
  45. * @note This function expects that crcInit() has been called
  46. * first to initialize the CRC lookup table.
  47. */
  48. width_t crcCompute(unsigned char * message, unsigned int nBytes)
  49. {
  50. unsigned int offset;
  51. unsigned char byte;
  52. width_t remainder = INITIAL_REMAINDER;
  53. /* Divide the message by the polynomial, a byte at a time. */
  54. for( offset = 0; offset < nBytes; offset++)
  55. {
  56. byte = (remainder >> (WIDTH - 8)) ^ message[offset];
  57. remainder = crcTable[byte] ^ (remainder << 8);
  58. }
  59. /* The final remainder is the CRC result. */
  60. return (remainder ^ FINAL_XOR_VALUE);
  61. } /* crcCompute() */

上面程式碼中crcInit() 函式用來計算crcTable,因此在呼叫 crcCompute 前必須先呼叫 crcInit()。不過,對於嵌入式系統,RAM是很緊張的,最好將 crcTable 提前算好,作為常量資料存到程式儲存區而不佔用RAM空間。CRC 計算實際上還有很多內容可以介紹,不過對於一般的程式設計師來說,知道這些也就差不多了。餘下的部分以後有時間了我再寫篇文章來介紹吧。

最後,給出個 C++ 程式碼,實現了 CRC8、CRC16 和 CRC32 的計算。收集了常見的各種 CRC 係數。 程式碼可以從這裡下載:https://code.csdn.net/liyuanbhu/crc_compute/tree/master

  1. #ifndef CRCCOMPUTE_H
  2. #define CRCCOMPUTE_H
  3. #include <stdint.h>
  4. template <typename TYPE> class CRC
  5. {
  6. public:
  7. CRC();
  8. CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
  9. void build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
  10. /**
  11. * Compute the CRC checksum of a binary message block.
  12. * @para message, 用來計算的資料
  13. * @para nBytes, 資料的長度
  14. */
  15. TYPE crcCompute(char * message, unsigned int nBytes);
  16. TYPE crcCompute(char * message, unsigned int nBytes, bool reinit);
  17. protected:
  18. TYPE m_polynomial;
  19. TYPE m_initial_remainder;
  20. TYPE m_final_xor_value;
  21. TYPE m_remainder;
  22. TYPE crcTable[256];
  23. int m_width;
  24. int m_topbit;
  25. /**
  26. * Initialize the CRC lookup table.
  27. * This table is used by crcCompute() to make CRC computation faster.
  28. */
  29. void crcInit(void);
  30. };
  31. template <typename TYPE>
  32. CRC<TYPE>::CRC()
  33. {
  34. m_width = 8 * sizeof(TYPE);
  35. m_topbit = 1 << (m_width - 1);
  36. }
  37. template <typename TYPE>
  38. CRC<TYPE>::CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
  39. {
  40. m_width = 8 * sizeof(TYPE);
  41. m_topbit = 1 << (m_width - 1);
  42. m_polynomial = polynomial;
  43. m_initial_remainder = init_remainder;
  44. m_final_xor_value = final_xor_value;
  45. crcInit();
  46. }
  47. template <typename TYPE>
  48. void CRC<TYPE>::build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
  49. {
  50. m_polynomial = polynomial;
  51. m_initial_remainder = init_remainder;
  52. m_final_xor_value = final_xor_value;
  53. crcInit();
  54. }
  55. template <typename TYPE>
  56. TYPE CRC<TYPE>::crcCompute(char * message, unsigned int nBytes)
  57. {
  58. unsigned int offset;
  59. unsigned char byte;
  60. TYPE remainder = m_initial_remainder;
  61. /* Divide the message by the polynomial, a byte at a time. */
  62. for( offset = 0; offset < nBytes; offset++)
  63. {
  64. byte = (remainder >> (m_width - 8)) ^ message[offset];
  65. remainder = crcTable[byte] ^ (remainder << 8);
  66. }
  67. /* The final remainder is the CRC result. */
  68. return (remainder ^ m_final_xor_value);
  69. }
  70. template <typename TYPE>
  71. TYPE CRC<TYPE>::crcCompute(char * message, unsigned int nBytes, bool reinit)
  72. {
  73. unsigned int offset;
  74. unsigned char byte;
  75. if(reinit)
  76. {
  77. m_remainder = m_initial_remainder;
  78. }
  79. /* Divide the message by the polynomial, a byte at a time. */
  80. for( offset = 0; offset < nBytes; offset++)
  81. {
  82. byte = (m_remainder >> (m_width - 8)) ^ message[offset];
  83. m_remainder = crcTable[byte] ^ (m_remainder << 8);
  84. }
  85. /* The final remainder is the CRC result. */
  86. return (m_remainder ^ m_final_xor_value);
  87. }
  88. class CRC8 : public CRC<uint8_t>
  89. {
  90. public:
  91. enum CRC8_TYPE {eCRC8, eAUTOSAR, eCDMA2000, eDARC, eDVB_S2, eEBU, eAES, eGSM_A, eGSM_B, eI_CODE,
  92. eITU, eLTE, eMAXIM, eOPENSAFETY, eROHC, eSAE_J1850, eWCDMA};
  93. CRC8(CRC8_TYPE type);
  94. CRC8(uint8_t polynomial, uint8_t init_remainder, uint8_t final_xor_value)
  95. :CRC<uint8_t>(polynomial, init_remainder, final_xor_value){}
  96. };
  97. class CRC16 : public CRC<uint16_t>
  98. {
  99. public:
  100. enum CRC16_TYPE {eCCITT, eKERMIT, eCCITT_FALSE, eIBM, eARC, eLHA, eSPI_FUJITSU,
  101. eBUYPASS, eVERIFONE, eUMTS, eCDMA2000, eCMS, eDDS_110, eDECT_R,
  102. eDECT_X, eDNP, eEN_13757, eGENIBUS, eEPC, eDARC, eI_CODE, eGSM,
  103. eLJ1200, eMAXIM, eMCRF4XX, eOPENSAFETY_A, eOPENSAFETY_B, ePROFIBUS,
  104. eIEC_61158_2, eRIELLO, eT10_DIF, eTELEDISK, eTMS37157, eUSB,
  105. eCRC_A, eMODBUS, eX_25, eCRC_B, eISO_HDLC, eIBM_SDLC, eXMODEM,
  106. eZMODEM, eACORN, eLTE};
  107. CRC16(CRC16_TYPE type);
  108. CRC16(uint16_t polynomial, uint16_t init_remainder, uint16_t final_xor_value)
  109. :CRC<uint16_t>(polynomial, init_remainder, final_xor_value){}
  110. };
  111. class CRC32 : public CRC<uint32_t>
  112. {
  113. public:
  114. enum CRC32_TYPE {eADCCP, ePKZIP, eCRC32, eAAL5, eDECT_B, eB_CRC32, eBZIP2, eAUTOSAR,
  115. eCRC32C, eCRC32D, eMPEG2, ePOSIX, eCKSUM, eCRC32Q, eJAMCRC, eXFER};
  116. CRC32(CRC32_TYPE type);
  117. };
  118. #endif // CRCCOMPUTE_H
  1. #include "crcCompute.h"
  2. template <typename TYPE>
  3. void CRC<TYPE>::crcInit(void)
  4. {
  5. TYPE remainder;
  6. TYPE dividend;
  7. int bit;
  8. /* Perform binary long division, a bit at a time. */
  9. for(dividend = 0; dividend < 256; dividend++)
  10. {
  11. /* Initialize the remainder. */
  12. remainder = dividend << (m_width - 8);
  13. /* Shift and XOR with the polynomial. */
  14. for(bit = 0; bit < 8; bit++)
  15. {
  16. /* Try to divide the current data bit. */
  17. if(remainder & m_topbit)
  18. {
  19. remainder = (remainder << 1) ^ m_polynomial;
  20. }
  21. else
  22. {
  23. remainder = remainder << 1;
  24. }
  25. }
  26. /* Save the result in the table. */
  27. crcTable[dividend] = remainder;
  28. }
  29. }
  30. CRC8::CRC8(CRC8_TYPE type)
  31. {
  32. switch (type)
  33. {
  34. case eCRC8:
  35. m_polynomial = 0x07; //http://reveng.sourceforge.net/crc-catalogue/all.htm
  36. m_initial_remainder = 0x00;
  37. m_final_xor_value = 0x00;
  38. break;
  39. case eAUTOSAR:
  40. m_polynomial = 0x2f;
  41. m_initial_remainder = 0xff;
  42. m_final_xor_value = 0xff;
  43. break;
  44. case eCDMA2000:
  45. m_polynomial = 0x9b;
  46. m_initial_remainder = 0xFF;
  47. m_final_xor_value = 0x00;
  48. break;
  49. case eDARC:
  50. m_polynomial = 0x39;
  51. m_initial_remainder = 0x00;
  52. m_final_xor_value = 0x00;
  53. break;
  54. case eDVB_S2:
  55. m_polynomial = 0xd5;
  56. m_initial_remainder = 0x00;
  57. m_final_xor_value = 0x00;
  58. break;
  59. case eEBU:
  60. case eAES:
  61. m_polynomial = 0x1d;
  62. m_initial_remainder = 0xFF;
  63. m_final_xor_value = 0x00;
  64. break;
  65. case eGSM_A:
  66. m_polynomial = 0x1d;
  67. m_initial_remainder = 0x00;
  68. m_final_xor_value = 0x00;
  69. break;
  70. case eGSM_B:
  71. m_polynomial = 0x49;
  72. m_initial_remainder = 0x00;
  73. m_final_xor_value = 0xFF;
  74. break;
  75. case eI_CODE:
  76. m_polynomial = 0x1d;
  77. m_initial_remainder = 0xFD;
  78. m_final_xor_value = 0x00;
  79. break;
  80. case eITU:
  81. m_polynomial = 0x07;
  82. 相關推薦

    CRC迴圈演算法入門

    寫給嵌入式程式設計師的迴圈冗餘校驗(CRC)演算法入門引導前言CRC校驗(迴圈冗餘校驗)是資料通訊中最常採用的校驗方式。在嵌入式軟體開發中,經常要用到CRC 演算法對各種資料進行校驗。因此,掌握基本的CRC演算法應是嵌入式程式設計師的基本技能。可是,我認識的嵌入式程式設計師中

    迴圈基本演算法

    也稱為多項式編碼,是鏈路層廣泛使用的更具糾錯能力的方法 基本思想: 將位串看成是係數為 0 或 1 的多項式。一個 k 位幀看作是一個 k-1 次多項式的係數列表,該多項式共有 k 項,從 xk-1 到 XO。這樣的多項式認為是 k-1 階多項式。高次(最左邊〉位是 xk-1 項的係

    迴圈CRC的簡單應用實踐

    前言 一般來說,資料資訊在傳輸過程中有時會受到外界有意或無意的干擾行為,出現數據的篡改和破壞現象,造成資料接收端的不一致行為。於是乎,在資訊領域有了資料校驗的概念,今天本文主要來簡單聊聊其中的經典使用演算法CRC(迴圈冗餘校驗)以及其在Ozone中的應用實踐。 CRC和Ch

    迴圈CRC演算法入門引導

    前言 CRC校驗(迴圈冗餘校驗)是資料通訊中最常採用的校驗方式。在嵌入式軟體開發中,經常要用到CRC 演算法對各種資料進行校驗。因此,掌握基本的CRC演算法應是嵌入式程式設計師的基本技能。可是,我認識的嵌入式程式設計師中能真正掌握CRC演算法的人卻很少,平常在專案中見到的C

    迴圈 CRC 計算的C++ 程式碼

    最近經常有人發私信給我找我幫忙計算 CRC 校驗。其實我有一篇部落格 http://blog.csdn.net/liyuanbhu/article/details/7882789 裡面介紹了CRC 計算的原理,也給出了些程式碼,裡面的程式碼稍微改改就能計算常

    迴圈CRC應用總結(包括C++原始碼)

     最近在實習期間需要用到資料的校驗,所選為CRC16,那麼就在此總結一番吧。 現在此說明下什麼是CRC:迴圈冗餘碼校驗 英文名稱為Cyclical Redundancy Check,簡稱CRC,它是利用除法及餘數的原理來作錯誤偵測(Error Detecting)的。實際應

    迴圈檢錯方案CRC

    1、crc校驗原理 crc校驗的根本思想就是在要傳送的幀後面附加一個二進位制序列,生成一個新幀傳送給接收端。當然,這個附加的數不是隨意的,它要使生成的新幀能與傳送端和接收端共同選定的某個特定數整除,需要注意的是,這裡不是採用的二進位制除法,而是採用“模2除法”。到達接收端後,再把接收到的新幀除以(模2除)這個

    CRC---迴圈

    typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned short uInt16; uint crc; // CRC 碼 uint

    差錯檢測 — 迴圈CRC

      現實中通訊鏈路都不會是理想的,位元在傳輸的過程中可能會出現差錯,1可能變成0,0也可能變成1,這就叫做位元差錯。位元差錯是傳輸差錯的一種,因此為了保證資料傳輸的可靠性,在計算網路傳輸資料時,必須採用各種檢驗措施來處理位元差錯。目前在資料鏈路層廣泛使用了迴圈冗餘的CRC的檢錯技術。

    迴圈碼計算CRC

    CRC迴圈冗餘檢驗碼的計算題 1、若資訊碼字為11100011,生成多項式 G(X)=X5+X4+X+1,則計算出的 CRC 校驗碼為? x的最高次冪5則 資訊碼(被除數)補五個0為:111000

    STM32F4學習筆記11——CRC迴圈

    關於CRC校驗有以下幾個方面 1.模2除(也就是異或)。 2.多項式與二進位制關係(x的最高冪次對應二進位制數的最高位,以下各位對應多項式的各冪次,有此冪次項對應1,無此冪次項對應0,x的最高冪次為R,轉換成對應的二進位制數有R+1位。)。 CRC基本原理

    Qt---CRC迴圈

    #include "mainwindow.h" #include <QApplication> #include <QDebug> #define POLY 0x1021

    資料鏈路層差錯檢測:CRC迴圈檢驗

    1、迴圈冗餘檢驗(CRC): 在傳送端,先把資料劃分為祖,假定每組K個位元。現假定待傳送的資料M = 101001(k=6)。CRC運算就是在資料M後面新增提供差錯檢測的n位冗餘碼,然後構成一個幀傳送出去,一共傳送(k+n )位。在所要傳送的資料後新增冗餘碼,顯然增大了資

    CRC迴圈學習及總結

    一、什麼是CRC冗餘校驗? 迴圈冗餘校驗(Cyclic Redundancy Check, CRC)是一種根據網路資料包或電腦檔案等資料產生簡短固定位數校驗碼的一種雜湊函式,主要用來檢測或校驗資料傳輸

    C#:迴圈CRC-16-CCITT和CRC-32-IEEE 802.3

    /* ---------------------------------------------------------- 檔名稱:CRC16.cs 作者:秦建輝 MSN:[email protected] QQ:36748897 部落格:http://blog.csdn.net/jhqin 開發環

    CRC迴圈計算

    個人網站: 黑客攻防學習課程: 號外:新課上線啦~~ 電腦端學習入口: 手機端學習入口:請掃二維碼~ 如果本部落格對你的有所幫助,不妨小額打賞下博主,您的鼓勵是我不斷寫部落格的動力。關於金額多少隨意,心意到就好。博主萬分感

    CRC迴圈

    Ⅰ.CRC校驗        CRC(Cyclic Redundancy Check):即迴圈冗餘檢驗碼,是資料通訊領域中最常用的一種差錯校驗碼,其特徵是資訊欄位和校驗欄位的長度可以任意選定。 Ⅱ.

    計算機網路 CRC迴圈

    4.使用迴圈冗餘校驗碼CRC, 接收方如何檢查收到的資訊有無錯誤(一個簡單通俗的模型) 首先接收方和傳送方約定一個“生成多項式”g(x); 傳送方傳送的是T(x),接收方接收到的是R(x),若T(x)和R(X)相等,則傳輸的過程中沒有出現錯誤。 傳送方要傳輸的資訊info包

    迴圈碼例題[轉帖]

    【例】假設使用的生成多項式是G(x)=x^3+x+1。4位的原始報文為1010,求編碼後的報文。 解: 1、將生成多項式G(x)=x^3+x+1轉換成對應的二進位制除數1011。 2、此題生成多項式有4位(R+1),要把原始報文C(x)左移3(R)位變成1010000 3、用生成多項式對應的二進位制數對左移4

    CAN匯流排中迴圈碼的原理

    在CAN系統中為保證報文傳輸的正確性,需要對通訊過程進行差錯控制。目前常用的方法是反饋重發,即一旦收到接收端發出的出錯資訊,傳送端便自動重發,此時的差錯控制只需要檢錯功能。常用的檢錯碼有兩類:奇偶校驗碼和迴圈冗餘校驗碼。奇偶校驗碼是一種最常見的檢錯碼,其實現方法簡單,但檢錯能