加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 一、CRC概念
    • 二、CRC名稱的定義
    • 三、常見的CRC算法
    •  
    • 四、CRC校驗(yàn)算法前置知識(shí)
    •  
    • 五、CRC原理
    • 六、循環(huán)冗余的計(jì)算
    •  
    • 七、代碼實(shí)現(xiàn)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

一文講透CRC校驗(yàn)碼-附贈(zèng)C語(yǔ)言實(shí)例

2021/06/30
515
閱讀需 16 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

一口君最近工作用到CRC校驗(yàn),順便整理本篇文章和大家一起研究。

一、CRC概念

1. 什么是CRC?

CRC(Cyclic Redundancy Checksum)是一種糾錯(cuò)技術(shù),代表循環(huán)冗余校驗(yàn)和。

數(shù)據(jù)通信領(lǐng)域中最常用的一種差錯(cuò)校驗(yàn)碼,其信息字段和校驗(yàn)字段長(zhǎng)度可以任意指定,但要求通信雙方定義的CRC標(biāo)準(zhǔn)一致。主要用來檢測(cè)或校驗(yàn)數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯(cuò)誤。它的使用方式可以說明如下圖所示:

在數(shù)據(jù)傳輸過程中,無論傳輸系統(tǒng)的設(shè)計(jì)再怎么完美,差錯(cuò)總會(huì)存在,這種差錯(cuò)可能會(huì)導(dǎo)致在鏈路上傳輸?shù)囊粋€(gè)或者多個(gè)幀被破壞(出現(xiàn)比特差錯(cuò),0變?yōu)?,或者1變?yōu)?),從而接受方接收到錯(cuò)誤的數(shù)據(jù)。

為盡量提高接受方收到數(shù)據(jù)的正確率,在接收方接收數(shù)據(jù)之前需要對(duì)數(shù)據(jù)進(jìn)行差錯(cuò)檢測(cè),當(dāng)且僅當(dāng)檢測(cè)的結(jié)果為正確時(shí)接收方才真正收下數(shù)據(jù)。檢測(cè)的方式有多種,常見的有奇偶校驗(yàn)、因特網(wǎng)校驗(yàn)和循環(huán)冗余校驗(yàn)等。

2. 使用方法概述

循環(huán)冗余校驗(yàn)是一種用于校驗(yàn)通信鏈路上數(shù)字傳輸準(zhǔn)確性的計(jì)算方法(通過某種數(shù)學(xué)運(yùn)算來建立數(shù)據(jù)位和校驗(yàn)位的約定關(guān)系的 )。

 

發(fā)送方計(jì)算機(jī)使用某公式計(jì)算出被傳送數(shù)據(jù)所含信息的一個(gè)值,并將此值 附在被傳送數(shù)據(jù)后,接收方計(jì)算機(jī)則對(duì)同一數(shù)據(jù)進(jìn)行 相同的計(jì)算,應(yīng)該得到相同的結(jié)果。

如果這兩個(gè) CRC結(jié)果不一致,則說明發(fā)送中出現(xiàn)了差錯(cuò),接收方計(jì)算機(jī)可要求發(fā)送方計(jì)算機(jī)重新發(fā)送該數(shù)據(jù)。

3. 應(yīng)用廣泛

在諸多檢錯(cuò)手段中,CRC是最著名的一種。CRC的全稱是循環(huán)冗余校驗(yàn),其特點(diǎn)是:檢錯(cuò)能力強(qiáng),開銷小,易于用編碼器及檢測(cè)電路實(shí)現(xiàn)。從其檢錯(cuò)能力來看,它所不能發(fā)現(xiàn)的錯(cuò)誤的幾率僅為0.0047%以下。

從性能上和開銷上考慮,均遠(yuǎn)遠(yuǎn)優(yōu)于奇偶校驗(yàn)及算術(shù)和校驗(yàn)等方式。

因而,在數(shù)據(jù)存儲(chǔ)和數(shù)據(jù)通訊領(lǐng)域,CRC無處不在:著名的通訊協(xié)議X.25的FCS(幀檢錯(cuò)序列)采用的是CRC-CCITT,WinRAR、NERO、ARJ、LHA等壓縮工具軟件采用的是CRC32,磁盤驅(qū)動(dòng)器的讀寫采用了CRC16,通用的圖像存儲(chǔ)格式GIF、TIFF等也都用CRC作為檢錯(cuò)手段。

二、CRC名稱的定義

這里需要知道幾個(gè)組成部分或者說計(jì)算概念:多項(xiàng)式公式、多項(xiàng)式簡(jiǎn)記式、數(shù)據(jù)寬度、初始值、結(jié)果異或值、輸入值反轉(zhuǎn)、輸出值反轉(zhuǎn)、參數(shù)模型。

1、多項(xiàng)式公式

對(duì)于CRC標(biāo)準(zhǔn)除數(shù),一般使用多項(xiàng)式(或二項(xiàng)式)公式表示,如下圖中除數(shù)11011(poly值為0x1b)的二項(xiàng)式為G(X)=X4+X3+X+1,X的指數(shù)就代表了該bit位上的數(shù)據(jù)為1,(最低位為0)。

這里特別注意一下位數(shù)問題,除數(shù)的位數(shù)為二項(xiàng)式最高次冪+1(4+1=5),這個(gè)很重要。

 

2、多項(xiàng)式簡(jiǎn)記式

通過對(duì)CRC的基本了解我們知道,多項(xiàng)式的首尾必定為1,而這個(gè)1的位置在下一步計(jì)算一定為0,所以就把前面這個(gè)1給省略掉了,出現(xiàn)了一個(gè)叫簡(jiǎn)記式的東西,如上例中除數(shù)11011的簡(jiǎn)記式為1011,很多看過CRC高級(jí)語(yǔ)言源碼的人會(huì)知道,對(duì)于CRC_16標(biāo)準(zhǔn)下G(X)=X16+X15+X2+1(16#18005)的poly值實(shí)際上是8005,這里使用的就是簡(jiǎn)記式。后面會(huì)對(duì)這個(gè)用法做一個(gè)說明。

3、數(shù)據(jù)寬度

數(shù)據(jù)寬度指的就是CRC校驗(yàn)碼的長(zhǎng)度(二進(jìn)制位數(shù)),知道了CRC的運(yùn)算概念和多項(xiàng)式,就可以理解這個(gè)概念了,CRC長(zhǎng)度始終要比除數(shù)位數(shù)少1,與簡(jiǎn)記式長(zhǎng)度是一致的。

以上三個(gè)數(shù)據(jù)就是我們經(jīng)常能夠用到的基本數(shù)據(jù)

4、初始值與結(jié)果異或值

在一些標(biāo)準(zhǔn)中,規(guī)定了初始值,則數(shù)據(jù)在進(jìn)行上述二項(xiàng)式運(yùn)算之前,需要先將要計(jì)算的數(shù)據(jù)與初始值的最低字節(jié)進(jìn)行異或,然后再與多項(xiàng)式進(jìn)行計(jì)算。

而在結(jié)果異或值不為零的情況下,則需要將計(jì)算得到的CRC結(jié)果值再與結(jié)果異或值進(jìn)行一次異或計(jì)算,得到的最終值才是我們需要的CRC校驗(yàn)碼。

這里可以看出,初始值與結(jié)果值的位數(shù)要求與數(shù)據(jù)寬度一致。

5、輸入值反轉(zhuǎn)與輸出值反轉(zhuǎn)

輸入值反轉(zhuǎn)的意思是在計(jì)算之前先將二項(xiàng)式反轉(zhuǎn),然后再用得到的新值和數(shù)據(jù)進(jìn)行計(jì)算。如對(duì)于G(X)=X16+X15+X2+1(16#18005),其正向值為1 1000 0000 0000 0101,反轉(zhuǎn)值則為1010 0000 0000 0001 1

輸出值反轉(zhuǎn)則是將最終得到的CRC結(jié)果反轉(zhuǎn)。

通常,輸入值反轉(zhuǎn)后的結(jié)果值也會(huì)是反轉(zhuǎn)的,所以這兩個(gè)選項(xiàng)一般是同向的,我們只有在在線CRC計(jì)算器中會(huì)看到自由選擇正反轉(zhuǎn)的情況存在。

三、常見的CRC算法

雖然CRC可以任意定義二項(xiàng)式、數(shù)據(jù)長(zhǎng)度等,但沒有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)的話,就會(huì)讓整個(gè)計(jì)算變得非常的麻煩。但實(shí)際上,不同的廠家經(jīng)常采用不同的標(biāo)準(zhǔn)算法,這里列出了一些國(guó)際常用的模型表:

名稱 多項(xiàng)式 表示法 應(yīng)用舉例
CRC-8 X8+X2+X+1 0X107  
CRC-12 X12+X11+X3+X2+X+1 0X180F telecom systems
CRC-16 X16+X15+X2+1 0X18005 Bisync, Modbus, USB, ANSI X3.28, SIA DC-07, many others; also known as CRC-16 and CRC-16-ANSI
CRC-CCITT X16+X12+X5+1 0X11021 ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS
CRC-32 X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+1 0x104C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS
CRC-32C X32+X28+X27+X26+X25+X23+X22+X20+X19+X18+X14+X13+X11+X10+X9+X8+X6+1 0x11EDC6F41 iSCSI, SCTP, G.hn payload, SSE4.2, Btrfs, ext4, Ceph

 

四、CRC校驗(yàn)算法前置知識(shí)

在學(xué)習(xí)CRC校驗(yàn)算法之前,先復(fù)習(xí)一下CRC會(huì)涉及的主要幾個(gè)主要的算法。

1. 異或

異或,就是不同為1,相同為0,運(yùn)算符號(hào)是^。

0^0 = 0
0^1 = 1
1^1 = 0
1^0 = 1

異或運(yùn)算存在如下幾個(gè)規(guī)律,需要了解。

0^x = x 即0 異或任何數(shù)等于任何數(shù)
1^x = ~x 即1異或任何數(shù)等于任何數(shù)取反
x^x = 0 即任何數(shù)與自己異或,結(jié)果為0
a ^ b = b ^ a 交換律
a ^ (b ^ c) = (a ^ b) ^c 結(jié)合律

 

2. 模2加法

模2加法相對(duì)于普通的算術(shù)加法,主要的區(qū)別在模2加法,不做進(jìn)位處理。具體結(jié)果如下。0+0 = 0 0+1 = 1 1+1 = 0 1+0 = 1 我們發(fā)現(xiàn)模2加法的計(jì)算結(jié)果,同異或運(yùn)算結(jié)果一模一樣。進(jìn)一步推演,我們會(huì)發(fā)現(xiàn),異或運(yùn)算的5個(gè)規(guī)律,同樣適合于模2加法。這里,就不在一一列舉了。

 

3. 模2減法

模2減法相對(duì)于普通的算術(shù)減法,主要的區(qū)別在模2減法,不做借位處理。具體結(jié)果如下。0-0 = 0 0-1 = 1 1-1 = 0 1-0 = 1 我們發(fā)現(xiàn)模2減法的計(jì)算結(jié)果,同模2加法,以及異或的運(yùn)算結(jié)果一模一樣。進(jìn)一步推演,我們會(huì)發(fā)現(xiàn),異或運(yùn)算的5個(gè)規(guī)律,同樣適合于模2減法。這里,就不在一一列舉了。

 

4. 模2除法

模2除法相對(duì)于普通的算術(shù)除法,主要的區(qū)別在模2除法,它既不向上位借位,也不比較除數(shù)和被除數(shù)的相同位數(shù)值的大小,只要以相同位數(shù)進(jìn)行相除即可。

 

五、CRC原理

CRC原理:在K位信息碼(目標(biāo)發(fā)送數(shù)據(jù))后再拼接R位校驗(yàn)碼,使整個(gè)編碼長(zhǎng)度為N位,因此這種編碼也叫(N,K)碼。

通俗的說,就是在需要發(fā)送的信息后面附加一個(gè)數(shù)(即校驗(yàn)碼),生成一個(gè)新的發(fā)送數(shù)據(jù)發(fā)送給接收端。這個(gè)數(shù)據(jù)要求能夠使生成的新數(shù)據(jù)被一個(gè)特定的數(shù)整除。這里的整除需要引入模 2除法的概念。

那么,CRC校驗(yàn)的具體做法就是

(1)選定一個(gè)標(biāo)準(zhǔn)除數(shù)(K位二進(jìn)制數(shù)據(jù)串)

(2)在要發(fā)送的數(shù)據(jù)(m位)后面加上K-1位0,然后將這個(gè)新數(shù)(M+K-1位)以模2除法的方式除以上面這個(gè)標(biāo)準(zhǔn)除數(shù),所得到的余數(shù)也就是該數(shù)據(jù)的CRC校驗(yàn)碼(注:余數(shù)必須比除數(shù)少且只少一位,不夠就補(bǔ)0)

(3)將這個(gè)校驗(yàn)碼附在原m位數(shù)據(jù)后面,構(gòu)成新的M+K-1位數(shù)據(jù),發(fā)送給接收端。

(4)接收端將接收到的數(shù)據(jù)除以標(biāo)準(zhǔn)除數(shù),如果余數(shù)為0則認(rèn)為數(shù)據(jù)正確。

注意:CRC校驗(yàn)中有兩個(gè)關(guān)鍵點(diǎn):

一是要預(yù)先確定一個(gè)發(fā)送端和接收端都用來作為除數(shù)的二進(jìn)制比特串(或多項(xiàng)式);

二是把原始幀與上面選定的除進(jìn)行二進(jìn)制除法運(yùn)算,計(jì)算出FCS。

前者可以隨機(jī)選擇,也可按國(guó)際上通行的標(biāo)準(zhǔn)選擇,但最高位和最低位必須均為“1”

六、循環(huán)冗余的計(jì)算

實(shí)例:

由于CRC-32、CRC-16、CCITT和CRC-4的編碼過程基本一致,只有位數(shù)和生成多項(xiàng)式不一樣,下面就舉例,來說明CRC校驗(yàn)碼生成過程。

對(duì)于數(shù)據(jù)1110 0101(16#E5),以指定除數(shù)11011求它的CRC校驗(yàn)碼,其過程如下:

 

使用上面計(jì)算的校驗(yàn)和和消息數(shù)據(jù),可以創(chuàng)建要傳輸?shù)拇a字。

有時(shí)候,我們需要填充checksum到制定的位置,這就涉及到字節(jié)序問題,建議用memcpy()進(jìn)行拷貝。

 

七、代碼實(shí)現(xiàn)

實(shí)現(xiàn)算法參考網(wǎng)絡(luò)相關(guān)代碼,進(jìn)行整理并驗(yàn)證,可直接使用。crc.c

/*  
 *一口Linux
 *2021.6.21
 *version: 1.0.0
*/
#include "crc.h"
#include 

typedef enum {
 REF_4BIT = 4,
 REF_5BIT = 5,
 REF_6BIT = 6,
 REF_7BIT = 7,
 REF_8BIT = 8,
 REF_16BIT = 16,
 REF_32BIT = 32
}REFLECTED_MODE;

uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode)
{
 data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16);
 data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8);
 data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
 data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2);
 data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1);

 switch (mode)
 {
 case REF_32BIT:
  return data;
 case REF_16BIT:
  return (data >> 16) & 0xffff;
 case REF_8BIT:
  return (data >> 24) & 0xff;
 case REF_7BIT:
  return (data >> 25) & 0x7f;
 case REF_6BIT:
  return (data >> 26) & 0x7f;
 case REF_5BIT:
  return (data >> 27) & 0x1f;
 case REF_4BIT:
  return (data >> 28) & 0x0f;
 }
 return 0;
}

uint8_t CheckCrc4(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint8_t i;
 uint8_t crc;

 if (refIn == true)
 {
  crc = init;
  poly = ReflectedData(poly, REF_4BIT);

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x01)
    {
     crc >>= 1;
     crc ^= poly;
    }
    else
    {
     crc >>= 1;
    }
   }
  }

  return crc ^ xorOut;
 }
 else
 {
  crc = init << 4;
  poly <<= 4;

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x80)
    {
     crc <<= 1;
     crc ^= poly;
    }
    else
    {
     crc <<= 1;
    }
   }
  }

  return (crc >> 4) ^ xorOut;
 }
}

uint8_t CheckCrc5(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint8_t i;
 uint8_t crc;

 if (refIn == true)
 {
  crc = init;
  poly = ReflectedData(poly, REF_5BIT);

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x01)
    {
     crc >>= 1;
     crc ^= poly;
    }
    else
    {
     crc >>= 1;
    }
   }
  }

  return crc ^ xorOut;
 }
 else
 {
  crc = init << 3;
  poly <<= 3;

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x80)
    {
     crc <<= 1;
     crc ^= poly;
    }
    else
    {
     crc <<= 1;
    }
   }
  }

  return (crc >> 3) ^ xorOut;
 }
}

uint8_t CheckCrc6(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint8_t i;
 uint8_t crc;

 if (refIn == true)
 {
  crc = init;
  poly = ReflectedData(poly, REF_6BIT);

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x01)
    {
     crc >>= 1;
     crc ^= poly;
    }
    else
    {
     crc >>= 1;
    }
   }
  }

  return crc ^ xorOut;
 }
 else
 {
  crc = init << 2;
  poly <<= 2;

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x80)
    {
     crc <<= 1;
     crc ^= poly;
    }
    else
    {
     crc <<= 1;
    }
   }
  }

  return (crc >> 2) ^ xorOut;
 }
}

uint8_t CheckCrc7(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint8_t i;
 uint8_t crc;

 if (refIn == true)
 {
  crc = init;
  poly = ReflectedData(poly, REF_7BIT);

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x01)
    {
     crc >>= 1;
     crc ^= poly;
    }
    else
    {
     crc >>= 1;
    }
   }
  }

  return crc ^ xorOut;
 }
 else
 {
  crc = init << 1;
  poly <<= 1;

  while (length--)
  {
   crc ^= *buffer++;
   for (i = 0; i < 8; i++)
   {
    if (crc & 0x80)
    {
     crc <<= 1;
     crc ^= poly;
    }
    else
    {
     crc <<= 1;
    }
   }
  }

  return (crc >> 1) ^ xorOut;
 }
}

uint8_t CheckCrc8(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint32_t i = 0;
 uint8_t crc = init;

 while (length--)
 {
  if (refIn == true)
  {
   crc ^= ReflectedData(*buffer++, REF_8BIT);
  }
  else
  {
   crc ^= *buffer++;
  }

  for (i = 0; i < 8; i++)
  {
   if (crc & 0x80)
   {
    crc <<= 1;
    crc ^= poly;
   }
   else
   {
    crc <<= 1;
   }
  }
 }

 if (refOut == true)
 {
  crc = ReflectedData(crc, REF_8BIT);
 }

 return crc ^ xorOut;
}

uint16_t CheckCrc16(uint16_t poly, uint16_t init, bool refIn, bool refOut, uint16_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint32_t i = 0;
 uint16_t crc = init;

 while (length--)
 {
  if (refIn == true)
  {
   crc ^= ReflectedData(*buffer++, REF_8BIT) << 8;
  }
  else
  {
   crc ^= (*buffer++) << 8;
  }

  for (i = 0; i < 8; i++)
  {
   if (crc & 0x8000)
   {
    crc <<= 1;
    crc ^= poly;
   }
   else
   {
    crc <<= 1;
   }
  }
 }

 if (refOut == true)
 {
  crc = ReflectedData(crc, REF_16BIT);
 }

 return crc ^ xorOut;
}

uint32_t CheckCrc32(uint32_t poly, uint32_t init, bool refIn, bool refOut, uint32_t xorOut,
 const uint8_t *buffer, uint32_t length)
{
 uint32_t i = 0;
 uint32_t crc = init;

 while (length--)
 {
  if (refIn == true)
  {
   crc ^= ReflectedData(*buffer++, REF_8BIT) << 24;
  }
  else
  {
   crc ^= (*buffer++) << 24;
  }

  for (i = 0; i < 8; i++)
  {
   if (crc & 0x80000000)
   {
    crc <<= 1;
    crc ^= poly;
   }
   else
   {
    crc <<= 1;
   }
  }
 }

 if (refOut == true)
 {
  crc = ReflectedData(crc, REF_32BIT);
 }

 return crc ^ xorOut;
}

uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length)
{
 switch (crcType.width)
 {
 case 4:
  return CheckCrc4(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 5:
  return CheckCrc5(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 6:
  return CheckCrc6(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 7:
  return CheckCrc7(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 8:
  return CheckCrc8(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 16:
  return CheckCrc16(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 case 32:
  return CheckCrc32(crcType.poly, crcType.init, crcType.refIn, crcType.refOut,
   crcType.xorOut, buffer, length);
 }
 return 0;
}

crc.h

/*  
 *一口Linux
 *2021.6.21
 *version: 1.0.0
*/
#ifndef __CRC_H__
#define __CRC_H__

#include 
#include 

typedef struct {
 uint8_t width;
 uint32_t poly;
 uint32_t init;
 bool refIn;
 bool refOut;
 uint32_t xorOut;
}CRC_Type;

uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length);

#endif

main.c

/*  
 *一口Linux
 *2021.6.21
 *version: 1.0.0
*/
#include 
#include 
#include 
#include "crc.h"

#define LENGTH 8
const uint8_t data[3][LENGTH] = {
 { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },
 { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },
 { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }};

typedef struct {
 CRC_Type crcType;
 uint32_t result[3];
}CRC_Test;

CRC_Test crc4_ITU = { { 4, 0x03, 0x00, true, true, 0x00 }, { 0x0f, 0x0a, 0x0e } };
CRC_Test crc5_EPC = { { 5, 0x09, 0x09, false, false, 0x00 }, { 0x00, 0x0c, 0x17 } };
CRC_Test crc5_ITU = { { 5, 0x15, 0x00, true, true, 0x00 }, { 0x16, 0x0a, 0x17 } };
CRC_Test crc5_USB = { { 5, 0x05, 0x1f, true, true, 0x1f }, { 0x10, 0x09, 0x17 } };
CRC_Test crc6_ITU = { { 6, 0x03, 0x00, true, true, 0x00 }, { 0x1d, 0x30, 0x00 } };
CRC_Test crc7_MMC = { { 7, 0x09, 0x00, false, false, 0x00 }, { 0x57, 0x30, 0x5b } };
CRC_Test crc8 = { { 8, 0x07, 0x00, false, false, 0x00 }, { 0x3e, 0xe1, 0x36 } };
CRC_Test crc8_ITU = { { 8, 0x07, 0x00, false, false, 0x55 }, { 0x6b, 0xb4, 0x63 } };
CRC_Test crc8_ROHC = { { 8, 0x07, 0xff, true, true, 0x00 }, { 0x6b, 0x78, 0x93 } };
CRC_Test crc8_MAXIM = { { 8, 0x31, 0x00, true, true, 0x00 }, { 0x83, 0x60, 0xa9 } };
CRC_Test crc16_IBM = { { 16, 0x8005, 0x0000, true, true, 0x0000 }, { 0xc4f0, 0x2337, 0xa776 } };
CRC_Test crc16_MAXIM = { { 16, 0x8005, 0x0000, true, true, 0xffff }, { 0x3b0f, 0xdcc8, 0x5889 } };
CRC_Test crc16_USB = { { 16, 0x8005, 0xffff, true, true, 0xffff }, { 0x304f, 0xd788, 0x53c9 } };
CRC_Test crc16_MODBUS = { { 16, 0x8005, 0xffff, true, true, 0x0000 }, { 0xcfb0, 0x2877, 0xac36 } };
CRC_Test crc16_CCITT = { { 16, 0x1021, 0x0000, true, true, 0x0000 }, { 0xeea7, 0xfe7c, 0x7919 } };
CRC_Test crc16_CCITT_FALSE = { { 16, 0x1021, 0xffff, false, false, 0x0000 }, { 0x4792, 0x13a7, 0xb546 } };
CRC_Test crc16_X25 = { { 16, 0x1021, 0xffff, true, true, 0xffff }, { 0x6dd5, 0x7d0f, 0xfa6a } };
CRC_Test crc16_XMODEM = { { 16, 0x1021, 0x0000, false, false, 0x0000 }, { 0x76ac, 0x2299, 0x8478 } };
CRC_Test crc16_DNP = { { 16, 0x3D65, 0x0000, true, true, 0xffff }, { 0x7bda, 0x0535, 0x08c4 } };
CRC_Test crc32 = { { 32, 0x04c11db7, 0xffffffff, true, true, 0xffffffff }, { 0x3fca88c5, 0xe0631a53, 0xa4051a26 } };
CRC_Test crc32_MPEG2 = { { 32, 0x4c11db7, 0xffffffff, false, false, 0x00000000 }, { 0x14dbbdd8, 0x6509b4b6, 0xcb09d294 } };

void CrcTest(CRC_Test crcTest)
{
 uint32_t i;
 for (i = 0; i < 3; i++)
 {
  printf("%08xt%08xrn", CrcCheck(crcTest.crcType, data[i], LENGTH), crcTest.result[i]);
 }
 printf("rn");
}

int main(void)
{
 CrcTest(crc4_ITU);
 CrcTest(crc5_EPC);
 CrcTest(crc5_ITU);
 CrcTest(crc5_USB);
 CrcTest(crc6_ITU);
 CrcTest(crc7_MMC);
 CrcTest(crc8);
 CrcTest(crc8_ITU);
 CrcTest(crc8_ROHC);
 CrcTest(crc8_MAXIM);
 CrcTest(crc16_IBM);
 CrcTest(crc16_MAXIM);
 CrcTest(crc16_USB);
 CrcTest(crc16_MODBUS);
 CrcTest(crc16_CCITT);
 CrcTest(crc16_CCITT_FALSE);
 CrcTest(crc16_X25);
 CrcTest(crc16_XMODEM);
 CrcTest(crc16_DNP);
 CrcTest(crc32);
 CrcTest(crc32_MPEG2);

 return 0;
}

注意

不同的CRC算法,對(duì)00H或FFH數(shù)據(jù)流的計(jì)算結(jié)果不一樣,部分算法存在校驗(yàn)結(jié)果也為00H或FFH的情況(也就意味著存儲(chǔ)空間處于初始化狀態(tài)時(shí):全0或全1,CRC校驗(yàn)反而是正確的),在應(yīng)用中需要注意避免。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。