RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

從原理到代碼理解CRC循環(huán)冗余校驗(yàn)

160513113 ? 來(lái)源:宋雨的工作學(xué)習(xí)筆記 ? 作者:宋雨的工作學(xué)習(xí)筆 ? 2023-01-29 09:00 ? 次閱讀

概述:本文詳細(xì)介紹了CRC循環(huán)冗余計(jì)算的數(shù)學(xué)原理,算法中使用的參數(shù)說(shuō)明,并以Modbus協(xié)議中的CRC-16算法為例,進(jìn)行手算驗(yàn)證,同時(shí)提供LabVIEWC語(yǔ)言的直接計(jì)算CRC-16 值的代碼以及C的查表計(jì)算CRC-16代碼和代碼原理的說(shuō)明。

一、筆者個(gè)人經(jīng)歷

初次接觸CRC校驗(yàn)是因?yàn)轫?xiàng)目需要上位機(jī)軟件來(lái)記錄PLC寄存器中的數(shù)據(jù),實(shí)現(xiàn)PLC控制全過(guò)程中關(guān)鍵數(shù)據(jù)的記錄和查詢。上位機(jī)軟件使用LV進(jìn)行編寫,數(shù)據(jù)的獲取通過(guò)Modbus TCP實(shí)現(xiàn),因?yàn)楫?dāng)時(shí)對(duì)Modbus和CRC都不是很熟悉,就采用了最成熟簡(jiǎn)單的辦法,直接調(diào)用了第三方的Modbus工具包,項(xiàng)目功能也是順利實(shí)現(xiàn)。之后又遇到一個(gè)項(xiàng)目,需要上位機(jī)作為從機(jī),回應(yīng)主控的Modbus RTU指令,這次沒(méi)有選擇直接使用Modbus工具包,而是使用《LabVIEW寶典》中Modbus CRC-16校驗(yàn)算法,根據(jù)Modbus RTU協(xié)議自主編程完成了項(xiàng)目要求。后來(lái)因?yàn)樽?a target="_blank">嵌入式單片機(jī),又了解使用了CRC-8和CRC-16的查表算法,也是沒(méi)有詳細(xì)了解過(guò)CRC循環(huán)冗余校驗(yàn)的原理,僅僅是可以熟練實(shí)現(xiàn)功能。直到后來(lái)遇到一個(gè)項(xiàng)目,需要用示波器捕捉并分析出未知的通信幀的通信協(xié)議,幀頭,幀尾很快通過(guò)對(duì)比分析了出來(lái),通信內(nèi)容也是反復(fù)實(shí)驗(yàn)分析出具體數(shù)據(jù)字節(jié)和位的意義,就是校驗(yàn)方式分析不出,但是可以肯定數(shù)據(jù)幀是一定包含校驗(yàn)字節(jié)的,那時(shí)才認(rèn)真開始考慮CRC循環(huán)冗余算法,試圖找出數(shù)據(jù)幀的校驗(yàn)規(guī)則,很慚愧沒(méi)有分析出來(lái),后來(lái)是通過(guò)第三方的幫助才解決了這個(gè)問(wèn)題,但是當(dāng)時(shí)腦中留下了很多問(wèn)號(hào)和片段式的思考及部分無(wú)序的筆記,現(xiàn)在重新進(jìn)行了整理、思考和驗(yàn)證,形成此文,希望可以解答對(duì)CRC循環(huán)冗余校驗(yàn)算法有疑問(wèn)的同學(xué)的困惑。

二、CRC循環(huán)冗余校驗(yàn)原理

百度CRC可以很容易的獲取CRC的定義:CRC(Cyclic Redundancy Check),即循環(huán)冗余校核,是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或電腦文件等數(shù)據(jù)產(chǎn)生簡(jiǎn)短固定位數(shù)校核碼的快速算法,主要用來(lái)檢測(cè)或校核數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯(cuò)誤。CRC利用除法及余數(shù)的原理,實(shí)現(xiàn)錯(cuò)誤偵測(cè)的功能,具有原理清晰、實(shí)現(xiàn)簡(jiǎn)單等優(yōu)點(diǎn)。

首先明確CRC是一種數(shù)據(jù)校驗(yàn)方法,與和校驗(yàn)、異或校驗(yàn)功能相同,常用于通信的雙方判斷通信幀在通信過(guò)程中數(shù)據(jù)傳輸是否正確,如校驗(yàn)不通過(guò)則需要考慮舍棄此通信幀,同時(shí)需根據(jù)需要判斷是否需要向發(fā)送方反饋通信異常的情況。

1、模2運(yùn)算

從百度到的CRC定義中可以看到CRC是利用除法和余數(shù)的原理,這里所說(shuō)的除法和余數(shù)的原理就是模2算法了,下面是模2算法的百度定義。

模2運(yùn)算是一種二進(jìn)制算法,CRC校驗(yàn)技術(shù)中的核心部分。與四則運(yùn)算相同,模2運(yùn)算也包括模2加法、模2減法、模2乘法、模2除法四種二進(jìn)制運(yùn)算。與四則運(yùn)算不同的是模2運(yùn)算不考慮進(jìn)位和借位,模2算術(shù)是編碼理論中多項(xiàng)式運(yùn)算的基礎(chǔ)。模2算術(shù)在其他數(shù)字領(lǐng)域中的應(yīng)用也是很廣泛的。

這里可以得出模2算法是不考慮進(jìn)位和借位的,也就可以理解為每位都是獨(dú)立的,不會(huì)影響其他位也不會(huì)被其他位影響,這點(diǎn)在后文中的計(jì)算驗(yàn)證部分有體現(xiàn)。

下面是模2運(yùn)算的四則運(yùn)算法則:

0+0=0;0+1=1;1+0=1;1+1=0;

0-0 =0;0-1=1;1-0=1;1-1=0;

0 *0=0;0 *1=0;1*0=0;1*1=1;

0/1=0; 1/1=1;

CRC算法中主要使用到的就是模2減法和模2除法。通過(guò)上述模2減法法則可以發(fā)現(xiàn),模2減法實(shí)際和異或運(yùn)算在結(jié)果上是完全相同的,也就不再過(guò)多描述。這里最關(guān)鍵的是多位二進(jìn)制的除法,這是CRC校驗(yàn)的核心部分。模2除法具有以下性質(zhì):

(1)每一位除的結(jié)果不影響其他位,即不向上一位借位;

(2)當(dāng)被除數(shù)的位數(shù)小于除數(shù)位數(shù)時(shí),則商為0,被除數(shù)就是余數(shù),也可以理解為被除數(shù)首位為0時(shí),商為0。

(3)只要被除數(shù)的位數(shù)和除數(shù)一樣多,且最高位為1,不管其他位是什么數(shù),皆可商1,也可以理解為被除數(shù)首位為1,商為1,余數(shù)為被除數(shù)與除數(shù)的模2減的結(jié)果;

(4)要保證每次除完首位都為0,才能進(jìn)行右移;

(5)當(dāng)最后余數(shù)的位數(shù)小于除數(shù)位數(shù)時(shí),除法停止。

通過(guò)對(duì)上述多位二進(jìn)制的模2除法性質(zhì)的思考,我們可以感覺(jué)到模2除與循環(huán)異或的本質(zhì)是相同的,這個(gè)可以通過(guò)下文中具體的計(jì)算過(guò)程體現(xiàn)。

2、CRC算法參數(shù)

這里給大家推薦一個(gè)很好用的CRC計(jì)算工具:image.png

,這個(gè)計(jì)算工具包含了很多種CRC算法,并且標(biāo)示出了具體的關(guān)鍵參數(shù),從我個(gè)人的使用經(jīng)歷上來(lái)說(shuō),需要注意的就僅僅是CRC-16 Modbus的計(jì)算結(jié)果是高字節(jié)在前,低字節(jié)在后的,這個(gè)與實(shí)際使用中通常低字節(jié)在前高字節(jié)在后不同,需要注意一下。下面我們就以CRC-16 Modbus為例對(duì)CRC算法進(jìn)行說(shuō)明。

image.png

*附件:CRC_Calc v0.1.exe

(1)標(biāo)準(zhǔn)CRC生成多項(xiàng)式

每種CRC算法的標(biāo)準(zhǔn)生成多項(xiàng)式可能是不同的,這個(gè)需要進(jìn)行注意。從上面截圖的左下方我們可以得知,CRC-16 Modbus的標(biāo)準(zhǔn)生成多項(xiàng)式是X16+X15+X2+1,其中1可以換成X0,這樣就很容易可以看出,16、15、2、0這些數(shù)字代表的是多位二進(jìn)制數(shù)的數(shù)位,則標(biāo)準(zhǔn)生成多項(xiàng)式可寫為1 1000 0000 0000 0101,對(duì)應(yīng)十六進(jìn)制就是0x18005,也就是對(duì)應(yīng)上圖右側(cè)的Poly:0x8005。這里的0x8005實(shí)際是標(biāo)準(zhǔn)生成多項(xiàng)式的簡(jiǎn)記式,因?yàn)闃?biāo)準(zhǔn)生成多項(xiàng)式的最高位固定為1,故在簡(jiǎn)記式中就忽略了最高位1了,同時(shí)在程序編程中實(shí)際使用的也是簡(jiǎn)記式,這個(gè)在下文的程序部分有所體現(xiàn)。

(2)CRC初始值

初始值,這個(gè)也是根據(jù)具體哪種CRC標(biāo)準(zhǔn)來(lái)確定的,不同的CRC標(biāo)準(zhǔn)對(duì)應(yīng)不同的計(jì)算初始值。還是以CRC-16 Modbus為例,計(jì)算初始值就是0xFFFF,對(duì)應(yīng)上圖Init:0xFFFF。計(jì)算初始值先與需要校驗(yàn)的數(shù)據(jù)的首字節(jié)數(shù)據(jù)進(jìn)行異或,異或后結(jié)果進(jìn)行模2除法運(yùn)算,這個(gè)后續(xù)程序部分會(huì)體現(xiàn)。

(3)正序/反序

就像串口通信需要考慮低位先傳還是高位先傳一樣,循環(huán)冗余計(jì)算時(shí)也需要考慮從高位開始還是低位開始,即編程時(shí)需要考慮數(shù)據(jù)進(jìn)行左移還是右移。需要說(shuō)明的一點(diǎn)是,數(shù)據(jù)進(jìn)行正序或者反序,最后的結(jié)果是不相同的,這個(gè)下文也會(huì)進(jìn)行驗(yàn)證說(shuō)明。上圖計(jì)算工具中是通過(guò)RefIn和RefOut來(lái)進(jìn)行體現(xiàn)的。

RefIn:true或false表示在進(jìn)行計(jì)算之前,原始數(shù)據(jù)是否翻轉(zhuǎn),如原始數(shù)據(jù):0x34 = 0011 0100,如果REFIN為true,進(jìn)行翻轉(zhuǎn)之后為0010 1100 = 0x2C。

REFOUT:true或false表示運(yùn)算完成之后,得到的CRC值是否進(jìn)行翻轉(zhuǎn),如計(jì)算得到的CRC值:0x97 = 1001 0111,如果REFOUT為true,進(jìn)行翻轉(zhuǎn)之后為1110 1001 = 0xE9。

以CRC-16 Modbus為例,都是True,則表示反序循環(huán)冗余校驗(yàn)。這個(gè)結(jié)合下文程序會(huì)更好理解,下文也會(huì)進(jìn)行相應(yīng)的說(shuō)明。

(4)CRC結(jié)果異或值

CRC結(jié)果異或值就是CRC循環(huán)冗余計(jì)算的結(jié)果與CRC結(jié)果異或值進(jìn)行異或處理,結(jié)果為CRC計(jì)算的最終值,對(duì)應(yīng)上圖中的XorOut:0x0000。

3、手算CRC算法及驗(yàn)證

前面已經(jīng)介紹了模2算法以及CRC算法的參數(shù),下面就來(lái)驗(yàn)證一下上面的理論是否正確。還是以CRC-16 Modbus為例,對(duì)單字節(jié)0x12數(shù)據(jù)計(jì)算校驗(yàn)值。

首先需要確定CRC-16 Modbus算法的參數(shù):

(1)標(biāo)準(zhǔn)生成多項(xiàng)式為X16+X15+X2+1,轉(zhuǎn)換成二進(jìn)制則為1 1000 0000 0000 0101;

(2)初始值為0xFFFF;

(3)采用反序的計(jì)算順序;

(4)CRC結(jié)果異或值為0x0000;

然后就按照CRC-16 Modbus算法來(lái)進(jìn)行計(jì)算,

0x12轉(zhuǎn)為二進(jìn)制為0001 0010;

與0xFFFF即1111 1111 1111 1111異或,結(jié)果為1111 1111 1110 1101;

反序?yàn)?011 0111 1111 1111;

下面進(jìn)行模2除,被除數(shù)為1011 0111 1111 1111 0000 0000,除數(shù)為1 1000 0000 0000 0101,因?yàn)橥ㄐ艓幕締挝皇亲止?jié),所以被除數(shù)為1011 0111 1111 1111后面加8個(gè)0。

模2除余數(shù)為1111 1100 1011 0010;(這里需要注意一下,CRC循環(huán)冗余算法中關(guān)注的是模2除的余數(shù),而不是商)

反序?yàn)?100 1101 0011 1111,即0x4D3F;

結(jié)果再與0x0000進(jìn)行異或,最終結(jié)果為0x4D3F。

利用上面介紹的CRC計(jì)算小工具進(jìn)行驗(yàn)證,結(jié)果如下圖所示。

image.png

同理,筆者針對(duì)不同標(biāo)準(zhǔn)生成多項(xiàng)式進(jìn)行手算實(shí)驗(yàn),選擇了CRC-32 對(duì)0x12進(jìn)行CRC校驗(yàn),手算結(jié)果與CRC計(jì)算工具結(jié)果相同。

同時(shí)針對(duì)數(shù)據(jù)的正序反序問(wèn)題,選擇CRC-8對(duì)0x12進(jìn)行CRC校驗(yàn),手算結(jié)果與CRC計(jì)算工具結(jié)果相同。

對(duì)于多字節(jié)數(shù)據(jù)的校驗(yàn)過(guò)程是:首先首字節(jié)與初始默認(rèn)值進(jìn)行異或校驗(yàn),結(jié)果作為被除數(shù)進(jìn)行模2除法;下一個(gè)字節(jié)與上一個(gè)字節(jié)的模2除法的結(jié)果進(jìn)行異或,作為下一次模2除法的被除數(shù);以此類推,這個(gè)在下文代碼部分體現(xiàn)。

筆者針對(duì)多字節(jié)也進(jìn)行了手算實(shí)驗(yàn),選擇了CRC-16 Modbus對(duì)0x12、0x34兩個(gè)字節(jié)進(jìn)行了CRC校驗(yàn),手算結(jié)果與CRC計(jì)算工具結(jié)果相同。

三、CRC 編程實(shí)現(xiàn)方法

1、直接計(jì)算法

CRC算法這里還是以CRC-16 Modbus為例,其直接計(jì)算編程實(shí)現(xiàn)過(guò)程為:

1)設(shè)置CRC寄存器,并給其賦值0xFFFF。

2)將數(shù)據(jù)的第一個(gè)8-bit字符(將此8位高位補(bǔ)0為16位)與16位CRC寄存器的值進(jìn)行異或,并把結(jié)果存入CRC寄存器。

3)CRC寄存器向右移(即最低位方向)一位,MSB補(bǔ)零,移出并檢查L(zhǎng)SB。

4)如果LSB為0,重復(fù)第三步;若LSB為1,CRC寄存器與多項(xiàng)式碼(0xA001)相異或。此時(shí)的0xA001即為0x8005的反序。

注意:該步檢查L(zhǎng)SB應(yīng)該是右移前的LSB,即第3步前的LSB。

5)重復(fù)第3與第4步直到8次移位全部完成。此時(shí)一個(gè)8-bit數(shù)據(jù)處理完畢。

6)重復(fù)第2至第5步直到所有數(shù)據(jù)全部處理完成。

7)最終CRC寄存器的內(nèi)容即為CRC值。

這里對(duì)上文中提及的標(biāo)準(zhǔn)多項(xiàng)式和簡(jiǎn)記式的區(qū)別再進(jìn)行一下說(shuō)明:

在上文中手算部分可以看到實(shí)際標(biāo)準(zhǔn)多項(xiàng)式最高位對(duì)應(yīng)被除數(shù)的那一位必定是1,與標(biāo)準(zhǔn)多項(xiàng)式最高位異或的結(jié)果或者說(shuō)模2減的結(jié)果必定是0,因此,在步驟4)就是僅判斷LSB是1還是0,進(jìn)而確定是先進(jìn)行異或再移位還是直接進(jìn)行移位,而不參與異或運(yùn)算,同時(shí)也可以理解上述步驟中僅涉及簡(jiǎn)記式進(jìn)行異或或者說(shuō)模2減。

讀者可以結(jié)合上文中的手算部分的運(yùn)算步驟來(lái)理解這里的處理步驟,關(guān)鍵是理解模2除和數(shù)據(jù)右移的關(guān)系,筆者這里不再進(jìn)行過(guò)多說(shuō)明。

LabVIEW直接計(jì)算 CRC-16 Modbus:

image.png

程序中while循環(huán)實(shí)際就是模2除法的體現(xiàn),以下程序同理。

C語(yǔ)言直接計(jì)算CRC-16 Modbus:

unsigned short do_crc(unsigned char *ptr, int len)

{


unsigned char i;

unsigned int crc16 = 0xFFFF;

while(len--)

{

crc16 ^= *ptr++;

for (i = 0; i < 8; ++i)

{

if (crc16 &0x0001)

crc16 = (crc16 >> 0x01) ^ 0xA001;

else

crc16 = (crc16 >> 0x01);

}

}

return crc16;

}

這里有一點(diǎn)需要注意的是,返回的CRC16為16位數(shù),分為兩個(gè)字節(jié),高低字節(jié)需轉(zhuǎn)換(僅針對(duì)Modbus,因?yàn)閙odbus一般要求校驗(yàn)值低位在前高位在后)。

2、查表法

對(duì)于查表法,其實(shí)就是利用空間換時(shí)間,通過(guò)直接查詢CRC運(yùn)算結(jié)果表格來(lái)減少計(jì)算時(shí)間,這個(gè)在嵌入式單片機(jī)方面使用較多,這邊還是以CRC-16 Modbus為例。

首先針對(duì)表格進(jìn)行說(shuō)明:

因?yàn)闃?biāo)準(zhǔn)生成多項(xiàng)式為X16+X15+X2+1,則可以確定校驗(yàn)結(jié)果為2字節(jié),因此表格分為高字節(jié)和低字節(jié),高低位CRC數(shù)組中(即下面的 Table_CRCL[256] 和 Table_CRCH[256]中 )同下標(biāo)的兩個(gè)單字節(jié)數(shù)組合成一個(gè)雙字節(jié)校驗(yàn)值。

關(guān)于下面表格中數(shù)值,是使用CRC-16,(標(biāo)準(zhǔn)生成多項(xiàng)式為X16+X15+X2+1;初始值0x0000;RefIn:True,RefOut:True,即反序;XorOut:0x0000)計(jì)算的0-255的CRC值。例如0x01按照上述CRC算法,結(jié)果是0xC0C1,即對(duì)應(yīng)Table_CRCH[1]=0xC0和Table_CRCL[1]=0xC1。這里讀者可能有疑問(wèn),為什么不是使用CRC-16 Modbus的算法?對(duì)比CRC-16 Modbus和上述算法的參數(shù),可以看到僅僅是初始值不同,CRC-16 Modbus 初始值為0xFFFF。這里先明確,表的意義是取代模2除法的計(jì)算過(guò)程。

再回到之前的手算部分,即如下所示。

從上式可以發(fā)現(xiàn),被除數(shù)從左邊起,8位及之后的7位,即1111 1111,這些數(shù)位僅參與異或且全程參與異或,或者說(shuō)模2減,而異或是符合交換律的,即A^B^C=A^C^B,則1111 1111也可最后再參與異或,而0x00與任何單字節(jié)數(shù)異或均為單字節(jié)數(shù)本身,則上式被除數(shù)可以改成1011 0111 0000 0000 0000 0000 ,最后的余數(shù)低字節(jié)再與原被除數(shù)高字節(jié)異或,得到的數(shù)就是符合CRC-16 Modbus算法的最終的低字節(jié)值,也就是說(shuō)原被除數(shù)高字節(jié)的值僅影響模2除余數(shù)的低字節(jié)值,這也就是下文代碼部分的解釋。

C語(yǔ)言查表計(jì)算CRC-16 Modbus:

const  uint8_t Table_CRCL[256] =     // CRC 高位字節(jié)值表

{

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40

};

const  uint8_t Table_CRCH[256] =      // CRC高位位字節(jié)值表

{

0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,

0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,

0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,

0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,

0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,

0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,

0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,

0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,

0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,

0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,

0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,

0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,

0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,

0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,

0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,

0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,

0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,

0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,

0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,

0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,

0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,

0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,

0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,

0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,

0x43, 0x83, 0x41, 0x81, 0x80, 0x40

};

uint16_t CRC16(uint8_t *puchMsg, uint8_t DataLen)

{

uint8_t CRCH= 0xFF ;              // 高CRC字節(jié)初始化

uint8_t CRCL = 0xFF ;              // 低CRC 字節(jié)初始化

uint8_t Index ;                         // CRC表中的索引

while (DataLen--)

{

Index = CRCL ^ *puchMsg++ ;

CRCL = CRCH ^ CRCL[Index];

CRCH = CRCH[Index];

}

return ((uint16_t)CRCL<< 8 | CRCH);	// Modbus校驗(yàn)值一般低字節(jié)在前,高字節(jié)在后

}

四、總結(jié)

本文選擇以CRC-16 Modbus算法標(biāo)準(zhǔn)進(jìn)行了詳細(xì)的舉例及手算驗(yàn)證,同時(shí)筆者也對(duì)比其他算法,如CRC-8,CRC-32進(jìn)行了針對(duì)性的驗(yàn)證,結(jié)果均證明正確。之所以選擇CRC-16 Modbus,感覺(jué)這個(gè)可能是大家平時(shí)使用中較為常用的,尤其是工控領(lǐng)域,同時(shí)也相信讀者舉一反三的能力,可以按照本文介紹方法掌握其他CRC算法。本文編寫期間也是對(duì)文字,計(jì)算及代碼反復(fù)考量驗(yàn)證,力求正確性和邏輯性,轉(zhuǎn)發(fā)及引用請(qǐng)注明出處。

——文章來(lái)自宋雨的個(gè)人分享

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • LabVIEW
    +關(guān)注

    關(guān)注

    1970

    文章

    3654

    瀏覽量

    323282
  • crc
    crc
    +關(guān)注

    關(guān)注

    0

    文章

    199

    瀏覽量

    29461
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7604

    瀏覽量

    136683
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4779

    瀏覽量

    68521
  • 循環(huán)冗余校驗(yàn)

    關(guān)注

    0

    文章

    7

    瀏覽量

    6540
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    PSoC 4 循環(huán)冗余校驗(yàn) (CRC)

    循環(huán)冗余校驗(yàn) (CRC) 組件的默認(rèn)用途是根據(jù)任意長(zhǎng)度的串行比特流計(jì)算 CRC。在數(shù)據(jù)時(shí)鐘的上升沿上對(duì)輸入數(shù)據(jù)進(jìn)行采樣。在啟動(dòng)前,
    發(fā)表于 07-04 11:30

    CRC循環(huán)冗余校驗(yàn)的算法

    想問(wèn)下51單片機(jī)和所有嵌入式中一個(gè)很基本也很常見(jiàn)的問(wèn)題,CRC循環(huán)冗余校驗(yàn)的算法中生成多項(xiàng)式為什么要那樣取(有個(gè)生成多項(xiàng)式的表格),如果信息段中一位或多位傳輸錯(cuò)誤,
    發(fā)表于 01-21 21:02

    轉(zhuǎn):循環(huán)冗余校驗(yàn)CRC)算法入門引導(dǎo)

    寫給嵌入式程序員的循環(huán)冗余校驗(yàn)CRC)算法入門引導(dǎo)前言CRC校驗(yàn)
    發(fā)表于 08-01 18:32

    循環(huán)冗余校驗(yàn)CRC)算法入門引導(dǎo)

    循環(huán)冗余校驗(yàn)CRC)算法入門引導(dǎo)
    發(fā)表于 08-17 12:40

    如何利用循環(huán)冗余校驗(yàn)CRC)計(jì)算單元進(jìn)行傳輸數(shù)據(jù)的校驗(yàn)

    循環(huán)冗余校驗(yàn)CRC)計(jì)算單元是什么?如何利用循環(huán)冗余校驗(yàn)
    發(fā)表于 12-15 06:04

    循環(huán)冗余校驗(yàn)碼的單片機(jī)及CPLD 實(shí)現(xiàn)

    循環(huán)冗余校驗(yàn)(CRC)是一種可靠性很高的串行數(shù)據(jù)校驗(yàn)方法。介紹循環(huán)
    發(fā)表于 04-16 14:19 ?16次下載

    循環(huán)冗余校驗(yàn)碼---CRC

    循環(huán)冗余校驗(yàn)碼---CRC碼   二進(jìn)制信息位串沿一條信號(hào)線逐位在部件之間或計(jì)算機(jī)之間傳送稱為串行傳送。CRC(Cyclic Redund
    發(fā)表于 10-13 16:52 ?7174次閱讀
    <b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>碼---<b class='flag-5'>CRC</b>碼

    STM32L4循環(huán)冗余校驗(yàn)模塊(CRC)介紹

    STM32L4循環(huán)冗余校驗(yàn)模塊(CRC)介紹 有興趣的可以參考下
    發(fā)表于 12-25 10:38 ?27次下載

    循環(huán)冗余校驗(yàn)奇偶校驗(yàn)累加和校驗(yàn)等知識(shí)分享

    CRC校驗(yàn)循環(huán)冗余校驗(yàn))是數(shù)據(jù)通訊中最常采用的校驗(yàn)方式。在嵌入式軟件開發(fā)中,經(jīng)常要用到
    的頭像 發(fā)表于 11-08 09:31 ?8734次閱讀
    <b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>奇偶<b class='flag-5'>校驗(yàn)</b>累加和<b class='flag-5'>校驗(yàn)</b>等知識(shí)分享

    crc循環(huán)冗余校驗(yàn)碼算法

     循環(huán)冗余校驗(yàn)(Cyclic Redundancy Check, CRC)是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或電腦文件等數(shù)據(jù)產(chǎn)生簡(jiǎn)短固定位數(shù)校驗(yàn)碼的一種散
    發(fā)表于 12-04 10:11 ?2.3w次閱讀
    <b class='flag-5'>crc</b><b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>碼算法

    PIC24F系列參考手冊(cè)之可編程循環(huán)冗余校驗(yàn)CRC

    本文主要介紹了PIC24F系列參考手冊(cè)之可編程循環(huán)冗余校驗(yàn)CRC)。
    發(fā)表于 06-25 03:20 ?0次下載
    PIC24F系列參考手冊(cè)之可編程<b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>(<b class='flag-5'>CRC</b>)

    Verilog數(shù)字系統(tǒng)基礎(chǔ)設(shè)計(jì)中的循環(huán)冗余校驗(yàn)

    CRC循環(huán)冗余校驗(yàn)CRC介紹 臨時(shí)“插播”,后面有實(shí)例。 CRC(Cyclic Redun
    的頭像 發(fā)表于 08-17 17:08 ?6758次閱讀

    CRC循環(huán)冗余校驗(yàn)簡(jiǎn)介

    CRC 是Cyclic Redundancy Check的縮寫,循環(huán)冗余校驗(yàn),用于校驗(yàn)數(shù)據(jù)傳輸?shù)耐暾?。一般情況下在數(shù)據(jù)發(fā)送前計(jì)算
    的頭像 發(fā)表于 04-24 13:04 ?6985次閱讀
    <b class='flag-5'>CRC</b><b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>簡(jiǎn)介

    32位可編程循環(huán)冗余校驗(yàn)(CRC)

    電子發(fā)燒友網(wǎng)站提供《32位可編程循環(huán)冗余校驗(yàn)(CRC).pdf》資料免費(fèi)下載
    發(fā)表于 09-25 11:22 ?0次下載
    32位可編程<b class='flag-5'>循環(huán)</b><b class='flag-5'>冗余</b><b class='flag-5'>校驗(yàn)</b>(<b class='flag-5'>CRC</b>)

    CRC循環(huán)冗余校驗(yàn))應(yīng)用舉例

    CRC循環(huán)冗余校驗(yàn))應(yīng)用舉例
    的頭像 發(fā)表于 05-16 16:12 ?1164次閱讀
    RM新时代网站-首页