x1021實(shí)際是0x11021。
I、基本算法(人工筆算):
以CRC16-CCITT為例進(jìn)行說明,CRC校驗(yàn)碼為16位,生成多項(xiàng)式17位。假如數(shù)據(jù)流為4字節(jié):BYTE[3]、BYTE[2]、BYTE[1]、BYTE[0];
數(shù)據(jù)流左移16位,相當(dāng)于擴(kuò)大256×256倍,再除以生成多項(xiàng)式0x11021,做不借位的除法運(yùn)算(相當(dāng)于按位異或),所得的余數(shù)就是CRC校驗(yàn)碼。
發(fā)送時(shí)的數(shù)據(jù)流為6字節(jié):BYTE[3]、BYTE[2]、BYTE[1]、BYTE[0]、CRC[1]、CRC[0];
II、計(jì)算機(jī)算法1(比特型算法):
1)將擴(kuò)大后的數(shù)據(jù)流(6字節(jié))高16位(BYTE[3]、BYTE[2])放入一個(gè)長度為16的寄存器;
2)如果寄存器的首位為1,將寄存器左移1位(寄存器的最低位從下一個(gè)字節(jié)獲得),再與生成多項(xiàng)式的簡記式異或;
否則僅將寄存器左移1位(寄存器的最低位從下一個(gè)字節(jié)獲得);
3)重復(fù)第2步,直到數(shù)據(jù)流(6字節(jié))全部移入寄存器;
4)寄存器中的值則為CRC校驗(yàn)碼CRC[1]、CRC[0]。
III、計(jì)算機(jī)算法2(字節(jié)型算法):256^n表示256的n次方
把按字節(jié)排列的數(shù)據(jù)流表示成數(shù)學(xué)多項(xiàng)式,設(shè)數(shù)據(jù)流為BYTE[n]BYTE[n-1]BYTE[n-2]、、、BYTE[1]BYTE[0],表示成數(shù)學(xué)表達(dá)式為BYTE[n]×256^n+BYTE[n-1]×256^(n-1)
+...+BYTE[1]*256+BYTE[0],在這里+表示為異或運(yùn)算。設(shè)生成多項(xiàng)式為G17(17bit),CRC碼為CRC16。
則,CRC16=(BYTE[n]×256^n+BYTE[n-1]×256^(n-1)+...+BYTE[1]×256+BYTE[0])×256^2/G17,即數(shù)據(jù)流左移16位,再除以生成多項(xiàng)式G17。
先變換BYTE[n-1]、BYTE[n-1]擴(kuò)大后的形式,
CRC16=BYTE[n]×256^n×256^2/G17+BYTE[n-1]×256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=(Z[n]+Y[n]/G17)×256^n+BYTE[n-1]×256^(n-1)×256^2/G17+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{Y[n]×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{(YH8[n]×256+YHL[n])×256/G17+BYTE[n-1]×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
=Z[n]×256^n+{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17}×256^(n-1)+...+BYTE[1]×256×256^2/G17+BYTE[0]×256^2/G17
這樣就推導(dǎo)出,BYTE[n-1]字節(jié)的CRC校驗(yàn)碼為{YHL[n]×256/G17+(YH8[n]+BYTE[n-1])×256^2/G17},即上一字節(jié)CRC校驗(yàn)碼Y[n]的高8位(YH8[n])與本字節(jié)BYTE[n-1]異或,
該結(jié)果單獨(dú)計(jì)算CRC校驗(yàn)碼(即單字節(jié)的16位CRC校驗(yàn)碼,對單字節(jié)可建立表格,預(yù)先生成對應(yīng)的16位CRC校驗(yàn)碼),所得的CRC校驗(yàn)碼與上一字節(jié)CRC校驗(yàn)碼Y[n]的低8位(YL8[n])
乘以256(即左移8位)異或。然后依次逐個(gè)字節(jié)求出CRC,直到BYTE[0]。
字節(jié)型算法的一般描述為:本字節(jié)的CRC碼,等于上一字節(jié)CRC碼的低8位左移8位,與上一字節(jié)CRC右移8位同本字節(jié)異或后所得的CRC碼異或。
字節(jié)型算法如下:
1)CRC寄存器組初始化為全'0'(0x0000)。(注意:CRC寄存器組初始化全為1時(shí),最后CRC應(yīng)取反。)
2)CRC寄存器組向左移8位,并保存到CRC寄存器組。
3)原CRC寄存器組高8位(右移8位)與數(shù)據(jù)字節(jié)進(jìn)行異或運(yùn)算,得出一個(gè)指向值表的索引。
4)索引所指的表值與CRC寄存器組做異或運(yùn)算。
5)數(shù)據(jù)指針加1,如果數(shù)據(jù)沒有全部處理完,則重復(fù)步驟2)。
6)得出CRC。
unsigned short GetCrc_16(unsigned char * pData, int nLength)
//函數(shù)功能:計(jì)算數(shù)據(jù)流* pData的16位CRC校驗(yàn)碼,數(shù)據(jù)流長度為nLength
{
unsigned short cRc_16 = 0x0000; // 初始化
while(nLength>0)
{
cRc_16 = (cRc_16 < 8)="" ^="" crctable_16[((crc_16="">>8) ^*pData) & 0xff]; //cRctable_16表由函數(shù)mK_cRctable生成
nLength--;
pData++;
}
return cRc_16;
}
void mK_cRctable(unsigned short gEnpoly)
//函數(shù)功能:生成0-255對應(yīng)的16CRC校驗(yàn)碼,其實(shí)就是計(jì)算機(jī)算法1(比特型算法)
//gEnpoly為生成多項(xiàng)式
//注意,低位先傳送時(shí),生成多項(xiàng)式應(yīng)反轉(zhuǎn)(低位與高位互換)。如CRC16-CCITT為0x1021,反轉(zhuǎn)后為0x8408
{
unsigned short cRc_16=0;
unsigned short i,j,k;
for(i=0,k=0;i<>
{
cRc_16 = i<>
for(j=8;j>0;j--)
{
if(cRc_16&0x8000) //反轉(zhuǎn)時(shí)cRc_16&0x0001
cRc_16=(cRc_16<=1)^genpoly; 反轉(zhuǎn)時(shí)crc_16="(cRc_16">>=1)^gEnpoly
else
cRc_16<=1; 反轉(zhuǎn)時(shí)crc_16="">>=1
}
cRctable_16[k] = cRc_16;
}
}
Cyclic RedundancyCheck循環(huán)冗余檢驗(yàn),是基于數(shù)據(jù)計(jì)算一組效驗(yàn)碼,用于核對數(shù)據(jù)傳輸過程中是否被更改或傳輸錯(cuò)誤。
算法原理
假設(shè)數(shù)據(jù)傳輸過程中需要發(fā)送15位的二進(jìn)制信息g=101001110100001,這串二進(jìn)制碼可表示為代數(shù)多項(xiàng)式g(x) = x^14+ x^12 + x^9 + x^8 + x^7 + x^5 +1,其中g(shù)中第k位的值,對應(yīng)g(x)中x^k的系數(shù)。將g(x)乘以x^m,既將g后加m個(gè)0,然后除以m階多項(xiàng)式h(x),得到的(m-1)階余項(xiàng)r(x)對應(yīng)的二進(jìn)制碼r就是CRC編碼。
h(x)可以自由選擇或者使用國際通行標(biāo)準(zhǔn),一般按照h(x)的階數(shù)m,將CRC算法稱為CRC-m,比如CRC-32、CRC-64等。國際通行標(biāo)準(zhǔn)可以參看
http://en./wiki/Cyclic_redundancy_checkg(x)和h(x)的除運(yùn)算,可以通過g和h做xor(異或)運(yùn)算。比如將11001與10101做xor運(yùn)算:

明白了xor運(yùn)算法則后,舉一個(gè)例子使用CRC-8算法求101001110100001的效驗(yàn)碼。CRC-8標(biāo)準(zhǔn)的h(x) = x^8 +x^7 + x^6 + x^4 + x^2 + 1,既h是9位的二進(jìn)制串111010101。

經(jīng)過迭代運(yùn)算后,最終得到的r是10001100,這就是CRC效驗(yàn)碼。
通過示例,可以發(fā)現(xiàn)一些規(guī)律,依據(jù)這些規(guī)律調(diào)整算法:
1.每次迭代,根據(jù)gk的首位決定b,b是與gk進(jìn)行運(yùn)算的二進(jìn)制碼。若gk的首位是1,則b=h;若gk的首位是0,則b=0,或者跳過此次迭代,上面的例子中就是碰到0后直接跳到后面的非零位。

2.每次迭代,gk的首位將會(huì)被移出,所以只需考慮第2位后計(jì)算即可。這樣就可以舍棄h的首位,將b取h的后m位。比如CRC-8的h是111010101,b只需是11010101。

3.每次迭代,受到影響的是gk的前m位,所以構(gòu)建一個(gè)m位的寄存器S,此寄存器儲(chǔ)存gk的前m位。每次迭代計(jì)算前先將S的首位拋棄,將寄存器左移一位,同時(shí)將g的后一位加入寄存器。若使用此種方法,計(jì)算步驟如下:

※藍(lán)色表示寄存器S的首位,是需要移出的,b根據(jù)S的首位選擇0或者h(yuǎn)。黃色是需要移入寄存器的位。S'是經(jīng)過位移后的S。
查表法
同樣是上面的那個(gè)例子,將數(shù)據(jù)按每4位組成1個(gè)block,這樣g就被分成6個(gè)block。

下面的表展示了4次迭代計(jì)算步驟,灰色背景的位是保存在寄存器中的。

經(jīng)4次迭代,B1被移出寄存器。被移出的部分,不我們關(guān)心的,我們關(guān)心的是這4次迭代對B2和B3產(chǎn)生了什么影響。注意表中紅色的部分,先作如下定義:
B23 = 00111010
b1 = 00000000
b2 = 01010100
b3 = 10101010
b4 = 11010101
b' = b1 xor b2 xor b3 xor b4
4次迭代對B2和B3來說,實(shí)際上就是讓它們與b1,b2,b3,b4做了xor計(jì)算,既:
B23 xor b1 xor b2 xor b3 xor b4
可以證明xor運(yùn)算滿足交換律和結(jié)合律,于是:
B23 xor b1 xor b2 xor b3 xor b4 = B23 xor (b1 xor b2 xor b3 xor b4)= B23 xor b'
b1是由B1的第1位決定的,b2是由B1迭代1次后的第2位決定(既是由B1的第1和第2位決定),同理,b3和b4都是由B1決定。通過B1就可以計(jì)算出b'。另外,B1由4位組成,其一共2^4有種可能值。于是我們就可以想到一種更快捷的算法,事先將b'所有可能的值,16個(gè)值可以看成一個(gè)表;這樣就可以不必進(jìn)行那4次迭代,而是用B1查表得到b'值,將B1移出,B3移入,與b'計(jì)算,然后是下一次迭代。

可看到每次迭代,寄存器中的數(shù)據(jù)以4位為單位移入和移出,關(guān)鍵是通過寄存器前4位查表獲得
,這樣的算法可以大大提高運(yùn)算速度。
上面的方法是半字節(jié)查表法,另外還有單字節(jié)和雙字節(jié)查表法,原理都是一樣的——事先計(jì)算出2^8或2^16個(gè)b'的可能值,迭代中使用寄存器前8位或16位查表獲得b'。
反向算法
之前討論的算法可以稱為正向CRC算法,意思是將g左邊的位看作是高位,右邊的位看作低位。G的右邊加m個(gè)0,然后迭代計(jì)算是從高位開始,逐步將低位加入到寄存器中。在實(shí)際的數(shù)據(jù)傳送過程中,是一邊接收數(shù)據(jù),一邊計(jì)算CRC碼,正向算法將新接收的數(shù)據(jù)看作低位。
逆向算法顧名思義就是將左邊的數(shù)據(jù)看作低位,右邊的數(shù)據(jù)看作高位。這樣的話需要在g的左邊加m個(gè)0,h也要逆向,例如正向CRC-16算法h=0x4c11db8,逆向CRC-16算法h=0xedb88320。b的選擇0還是h,由寄存器中右邊第1位決定,而不是左邊第1位。寄存器仍舊是向左位移,就是說迭代變成從低位到高位。

以下的源程序全部以 CCITT 為例。其實(shí)本質(zhì)都是一樣,搞明白一種,其他的都是小菜。
圖 1,圖 2 說明了 CRC 校驗(yàn)中 CRC 值是如何計(jì)算出來的,體現(xiàn)的多項(xiàng)式正是 X16+X12+X5+1。 SerialData即是需要校驗(yàn)的數(shù)據(jù)。從把數(shù)據(jù)移位開始計(jì)算,將數(shù)據(jù)位(從最低的數(shù)據(jù)位開始)逐位移入反向耦合移位寄存器(這個(gè)名詞我也不懂,覺得蠻酷的,就這樣寫了,嘿)。當(dāng)所有數(shù)據(jù)位都這樣操作后,計(jì)算結(jié)束。此時(shí),16位移位寄存器中的內(nèi)容就是 CRC 碼。


圖中進(jìn)行 XOR 運(yùn)算的位與多項(xiàng)式的表達(dá)相對應(yīng)。
X5 代表 Bit5,X12 代表 Bit12,1 自然是代表 Bit0,X16 比較特別,是指移位寄存器移出的數(shù)據(jù),即圖中的DATAOUT。可以這樣理解,與數(shù)據(jù)位做XOR運(yùn)算的是上次 CRC值的 Bit15。
根據(jù)以上說明,可以依葫蘆畫瓢的寫出以下程序。(程序都是在 keil C 7.10 下調(diào)試的)
typedef unsigned char uchar;
typedef unsigned int uint;
code uchar crcbuff [] = {0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};
uint crc; // CRC 碼
void main(void)
{
uchar *ptr;
crc = 0; // CRC 初值
ptr = crcbuff; // 指向第一個(gè) Byte 數(shù)據(jù)
crc = crc16l(ptr,8);
while(1);
}
uint crc16l(uchar *ptr,uchar len) // ptr 為數(shù)據(jù)指針,len 為數(shù)據(jù)長度
{
uchar i;
while(len--)
{
for(i=0x80; i!=0; i>>=1)
{
if((crc&0x8000)!=0) {crc<=1; crc^="0x1021;}">=1;>
else crc<=1;>=1;>
if((*ptr&i)!=0) crc^=0x1021; 1-3
}
ptr++;
}
return(crc);
}
執(zhí)行結(jié)果 crc = 0xdbc0;
程序 1-1,1-2,1-3 可以理解成移位前 crc 的 Bit15 與數(shù)據(jù)對應(yīng)的 Bit(*ptr&i)做XOR運(yùn)算,根據(jù)此結(jié)果來決定是否執(zhí)行 crc^=0x1021。只要明白兩次異或運(yùn)算與原值相同,就不難理解這個(gè)程序。
很多資料上都寫了查表法來計(jì)算,當(dāng)時(shí)是怎么也沒想通。其實(shí)蠻簡單的。假設(shè)通過移位處理了 8 個(gè) bit 的數(shù)據(jù),相當(dāng)于把之前的 CRC碼的高字節(jié)(8bit)全部移出,與一個(gè) byte 的數(shù)據(jù)做XOR 運(yùn)算,根據(jù)運(yùn)算結(jié)果來選擇一個(gè)值(稱為余式),與原來的 CRC碼再做一次 XOR 運(yùn)算,就可以得到新的 CRC 碼。
不難看出,余式有 256 種可能的值,實(shí)際上就是 0~255 以 X16+X12+X5+1 為權(quán)得到的 CRC碼,可以通過函數(shù)crc16l來計(jì)算。以1 為例。
code test[]={0x01};
crc = 0;
ptr = test;
crc = crc16l(ptr,1);
執(zhí)行結(jié)果 crc = 1021,這就是1 對應(yīng)的余式。
進(jìn)一步修改函數(shù),我這里就懶得寫了,可得到 X16+X12+X5+1 的余式表。
code uint crc_ta[256]={ // X16+X12+X5+1 余式表
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6,0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce,0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7,0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff,0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4,0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac,0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695,0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d,0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802,0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a,0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33,0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b,0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60,0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68,0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51,0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59,0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e,0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046,0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f,0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277,0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c,0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424,0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d,0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615,0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a,0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882,0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb,0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3,0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8,0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0,0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9,0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1,0x1ef0
};
根據(jù)這個(gè)思路,可以寫出以下程序:
uint table_crc(uchar *ptr,uchar len) // 字節(jié)查表法求 CRC
{
uchar da;
while(len--!=0)
{
da=(uchar) (crc/256); // 以 8 位二進(jìn)制數(shù)暫存 CRC 的高 8 位
crc<=8; 左移="" 8="">=8;>
crc^=crc_ta[da^*ptr]; // 高字節(jié)和當(dāng)前數(shù)據(jù) XOR 再查表
ptr++;
}
return(crc);
}
本質(zhì)上 CRC 計(jì)算的就是移位和異或。所以一次處理移動(dòng)幾位都沒有關(guān)系,只要做相應(yīng)的處理就好了。
下面給出半字節(jié)查表的處理程序。其實(shí)和全字節(jié)是一回事。
code uint crc_ba[16]={
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6,0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce,0xf1ef,
};
uint ban_crc(uchar *ptr,uchar len)
{
uchar da;
while(len--!=0)
{
da = ((uchar)(crc/256))/16;
crc <=>=>
crc ^=crc_ba[da^(*ptr/16)];
da = ((uchar)(crc/256)/16);
crc <=>=>
crc ^=crc_ba[da^(*ptr&0x0f)];
ptr++;
}
return(crc);
}
crc_ba[16]和crc_ta[256]的前 16 個(gè)余式是一樣的。
其實(shí)講到這里,就已經(jīng)差不多了。反正當(dāng)時(shí)我以為自己是懂了。結(jié)果去看別人的源代碼的時(shí)候,也是說采用 CCITT,但是是反相的。如圖3

反過來,一切都那么陌生,faint.吐血,吐血。
仔細(xì)分析一下,也可以很容易寫出按位異或的程序。只不過由左移變成右移。
uint crc16r(unsigned char *ptr, unsigned char len)
{
unsigned char i;
while(len--!=0)
{
for(i=0x01;i!=0;i <=>=>
{
if((crc&0x0001)!=0) {crc >>= 1; crc ^= 0x8408;}
else crc >>= 1;
if((*ptr&i)!=0) crc ^= 0x8408;
}
ptr++;
}
return(crc);
}
0x8408 就是 CCITT 的反轉(zhuǎn)多項(xiàng)式。
套用別人資料上的話
“反轉(zhuǎn)多項(xiàng)式是指在數(shù)據(jù)通訊時(shí),信息字節(jié)先傳送或接收低位字節(jié),如重新排位影響 CRC計(jì)算速度,故設(shè)反轉(zhuǎn)多項(xiàng)式。”
如
code uchar crcbuff [] = {0x00,0x00,0x00,0x00,0x06,0x0d,0xd2,0xe3};
反過來就是
code uchar crcbuff_fan[] ={0xe3,0xd2,0x0d,0x06,0x00,0x00,0x00,0x00};
crc = 0;
ptr = crcbuff_fan;
crc = crc16r(ptr,8);
執(zhí)行結(jié)果 crc = 0x5f1d;
如想驗(yàn)證是否正確,可改
code uchar crcbuff_fan_result[] ={0xe3,0xd2,0x0d,0x06,0x00,0x00,0x00,0x00,0x1d,0x5f};
ptr = crcbuff_fan_result;
crc = crc16r(ptr,10);
執(zhí)行結(jié)果 crc = 0; 符合 CRC 校驗(yàn)的原理。
請注意 0x5f1d 在數(shù)組中的排列中低位在前,正是反相運(yùn)算的特點(diǎn)。不過當(dāng)時(shí)是把我搞的暈頭轉(zhuǎn)向。
在用半字節(jié)查表法進(jìn)行反相運(yùn)算要特別注意一點(diǎn),因?yàn)槭怯乙?,所?CRC 移出的 4Bit與數(shù)據(jù) XOR 的操作是在 CRC的高位端。因此余式表的產(chǎn)生是要以下列數(shù)組通過修改函數(shù)crc16r 產(chǎn)生。
code uchar ban_fan[]=
{0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0};
得出余式表
code uint fan_yushi[16]={
0x0000, 0x1081, 0x2102, 0x3183,
0x4204, 0x5285, 0x6306, 0x7387,
0x8408, 0x9489, 0xa50a, 0xb58b,
0xc60c, 0xd68d, 0xe70e, 0xf78f
};
uint ban_fan_crc(uchar *ptr,uchar len)
{
uchar da;
while(len--!=0)
{
da = (uchar)(crc&0x000f);
crc >>= 4;
crc ^= fan_yushi [da^(*ptr&0x0f)];
da = (uchar)(crc&0x000f);
crc >>= 4;
crc ^= fan_yushi [da^(*ptr/16)];
ptr++;
}
return(crc);
}
主程序中
crc = 0;
ptr = crcbuff_fan;
crc = ban_fan_crc(ptr,8);
執(zhí)行結(jié)果 crc = 0x5f1d;
=1;>=1)^genpoly;>