CRC16 筆算和程式算的值完全不一樣 是哪一邊出問題呢? |
缺席
|
tyw6455
一般會員 ![]() ![]() 發表:7 回覆:9 積分:3 註冊:2005-06-23 發送簡訊給我 |
最近因工作上的需求,必須使用CRC 所以找了一些CRC算法的資料及程式。
當把程式修改成在BCB上可以用的模式之後發現,用手算出來的CRC值 和電腦算出來的值是不同的,不知道是不是我已經踩到的地雷而不自知, 希望有人能指點一下。 工作上採用的是 CRC16 的 ModBus的多項式A001(1010 0000 0000 0001) 我用筆算了一下 0x41 過程及結果如下: 多項式:1010 0000 0000 0001 資料:0x41 0100 0001 0000 0000 0000 000 *^101 0000 0000 0000 1 ---------------------------------------------- **001 0001 0000 0000 100 ****^1 0100 0000 0000 001 -------------------------------------------- *****0 0101 0000 0000 1010 0 *******^101 0000 0000 0000 1 ---------------------------------------------- ******** 000 0000 0000 1010 100 CRC 值 -> 0000 0000 0101 0100 不過用程式算出來的值卻不是如此,算出來的值是 7F70 程式如下 [code cpp] unsigned int __fastcall TfmCRC::ChkCRC(unsigned char *vcBuf, unsigned int viLen) { unsigned char cHi; unsigned char cLo; unsigned int i; unsigned int iCRC; iCRC = 0xFFFF; for (i = 0; i < viLen; i ) { iCRC = CalcCRC(*vcBuf, iCRC); vcBuf ; } cHi = iCRC % 256; cLo = iCRC / 256; iCRC = (cHi << 8) | cLo; return iCRC; } //--------------------------------------------------------------------------- unsigned int __fastcall TfmCRC::CalcCRC(unsigned char vcCRCBuf,unsigned int viCRC) { unsigned int i; unsigned char cChk; viCRC = viCRC ^ vcCRCBuf; for (i = 0; i < 8; i ) { cChk = viCRC & 1; viCRC = viCRC >> 1; viCRC = viCRC & 0x7FFF; if (cChk == 1) { viCRC = viCRC ^ 0xA001; } viCRC = viCRC & 0xFFFF; } return viCRC; } //--------------------------------------------------------------------------- void __fastcall TfmCRC::btnCountClick(TObject *Sender) { int iLen = etData->Text.Trim().Length(); int iCRC; AnsiString sData = ""; char cData[1] ; iLen = 1; cData[0] = 0x41; sData = (AnsiString)cData[0]; /* //0106000503E8 CRC->9975 char cData[6]; iLen = 6; cData[0] = 0x01; cData[1] = 0x06; cData[2] = 0x00; cData[3] = 0x05; cData[4] = 0x03; cData[5] = 0xE8; sData = (AnsiString)cData[0] (AnsiString)cData[1] (AnsiString)cData[2] (AnsiString)cData[3] (AnsiString)cData[4] (AnsiString)cData[5]; */ iCRC = ChkCRC(sData.c_str(), iLen); // iCRC = ChkCRC(etData->Text.Trim().c_str(), iLen); etCode->Text = IntToHex(iCRC, 4); } //--------------------------------------------------------------------------- [/code] 一開始是怕程式編寫過程有問題,所以找到了有算出CRC值的資料來核對一下(即程式中)註解的程式 發現是OK的,又用了查表的方式讓電腦做了一次,結果電腦算的查表的結果跟電腦算的的相同的,但是跟筆算的不一樣, 於是找了一篇有資料、有CRC值、有多項式的文章用筆推算了一下 那篇資料網址:http://delphi.ktop.com.tw/board.php?cid=169&fid=963&tid=21165 推算過程 多項式:1 0001 0000 0010 0001 資料:0xD5 *1101 0101 0000 0000 0000 000 ^1000 1000 0001 0000 1 ------------------------------------------------- *0101 1101 0001 0000 10 *^100 0100 0000 1000 01 -------------------------------------------------- **001 1001 0001 1000 1100 ****^1 0001 0000 0010 0001 -------------------------------------------------- *****0 1000 0001 1010 1101 0 ******^1000 1000 0001 0000 1 -------------------------------------------------- *******0000 1001 1011 1101 1000 CRC->1001 1011 1101 1000 推算的結果跟文章裡面的結果是OK的,推算方法跟最上面的推算方法並無不同, 所以希望有人能指正一下,我是踏了什麼地雷! PS.下面的例子並沒有用電腦算過,中間的電腦驗證的地方並無用筆算過。 星號是為了讓算式對齊別無其他用意 編輯記錄
tyw6455 重新編輯於 2007-10-11 17:06:57, 註解 讓算式對齊‧
|
jow
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
|
tyw6455
一般會員 ![]() ![]() 發表:7 回覆:9 積分:3 註冊:2005-06-23 發送簡訊給我 |
應該不是這的問題,因為用查表的方式做出來的答案跟程式RUN出來的結果是一樣
以下是查表的程式碼 [code cpp] unsigned int __fastcall TfmCRC::CRC16(unsigned char *vcUpData,unsigned int viLen) { unsigned char cCRCHi = 0xFF; unsigned char cCRCLo = 0xFF; unsigned int iIndex; while (viLen--) { iIndex = cCRCHi ^ *vcUpData ; cCRCHi = cCRCLo ^ auchCRCHi[iIndex]; cCRCLo = auchCRCLo[iIndex]; } return ((cCRCHi << 8) | cCRCLo); } //--------------------------------------------------------------------------- void __fastcall TfmCRC::btnCheckClick(TObject *Sender) { int iLen = etData->Text.Trim().Length(); int iCRC; AnsiString sData = ""; iLen = 1; char cData[1] ; cData[0] = 0x41; sData = (AnsiString)cData[0]; /* //0106000503E8 CRC->9975 char cData[6]; iLen = 6; cData[0] = 0x01; cData[1] = 0x06; cData[2] = 0x00; cData[3] = 0x05; cData[4] = 0x03; cData[5] = 0xE8; sData = (AnsiString)cData[0] (AnsiString)cData[1] (AnsiString)cData[2] (AnsiString)cData[3] (AnsiString)cData[4] (AnsiString)cData[5]; */ iCRC = CRC16(sData.c_str(), iLen); // iCRC = ChkCRC(etData->Text.Trim().c_str(), iLen); etCodeC->Text = IntToHex(iCRC, 4); } unsigned char auchCRCHi[]= { 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 }; unsigned char auchCRCLo[] = { 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 }; [/code] 再不然就是整個程式的演算是錯誤的 這個要再試試了 ===================引 用 jow 文 章=================== 沒有測試, 用猜的^^ 這裡需不需要轉型? cHi = iCRC % 256; cLo = iCRC / 256; iCRC = (cHi << 8) | cLo; return iCRC; |
tyw6455
一般會員 ![]() ![]() 發表:7 回覆:9 積分:3 註冊:2005-06-23 發送簡訊給我 |
「CRC碼求餘數的除法運算規則是:做減法不產生借位,做加法不產生進位,除此以外,與二進制(模二)除法相同。」
上面這句話應該就是說需要用XOR的方式去求餘數吧, 今天早上用純粹的除法去求餘數,結果出來的值還是不對 真的看不見問題點了 純粹計算式如下:(省略商) 多項式是:0xA001 資料是:0x41 0100 0001 0000 0000 0000 0000 **-10 1000 0000 0000 01 ----------------------------------------------- *001 1000 1111 1111 110 ***-1 0100 0000 0000 001 ----------------------------------------------- ****0 0100 1111 1111 1010 00 ********-10 1000 0000 0000 01 ----------------------------------------------- *********10 0111 1111 1001 110 **********-1 0100 0000 0000 001 ------------------------------------------------ **********1 0011 1111 1001 101*<---如果左移15個位置 9FCD ***********-1010 0000 0000 0001 ------------------------------------------------ ************1001 1111 1001 1001<---如果左移16個位置 9F99
編輯記錄
tyw6455 重新編輯於 2007-10-15 14:12:58, 註解 無‧
|
jow
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
0x1111 1111 1111 1111(0xFFFF)
0x0000 0000 0100 0001(0x0041) ^--------------------- 0x1111 1111 1011 1110(0xFFBE), *** Start for loop *** >>1-------------------i==0,cChk==0 0x0111 1111 1101 1111(0x7FDF) 0x0111 1111 1111 1111(0x7FFF) &--------------------- 0x0111 1111 1101 1111(0x7FDF) 0x1111 1111 1111 1111(0xFFFF) &---------------------i==0,cChk==0 --> & 0xFFFF 0x0111 1111 1101 1111(0x7FDF) >>1-------------------i==1,cChk==1 0x0011 1111 1110 1111(0x3FEF) (以下請自行推演) [code cpp] unsigned int __fastcall TfmCRC::ChkCRC(unsigned char *vcBuf, unsigned int viLen) { unsigned char cHi; unsigned char cLo; unsigned int i; unsigned int iCRC; iCRC = 0xFFFF; AnsiString S; ListBox1->Items->Add("iCRC==" S.sprintf("0x%4.4X", iCRC)); for (i = 0; i < viLen; i ) { iCRC = CalcCRC(*vcBuf, iCRC); vcBuf ; } ListBox1->Items->Add("iCRC==" S.sprintf("0x%4.4X", iCRC)); cHi = iCRC % 256; cLo = iCRC / 256; iCRC = (cHi << 8) | cLo; ListBox1->Items->Add("iCRC==" S.sprintf("0x%4.4X", iCRC)); return iCRC; } [/code] [code cpp] unsigned int __fastcall TfmCRC::CalcCRC(unsigned char vcCRCBuf,unsigned int viCRC) { unsigned int i; unsigned char cChk; viCRC = viCRC ^ vcCRCBuf; AnsiString S; ListBox1->Items->Add(S.sprintf("viCRC^vcCRCBuf=0x%4.4X", viCRC)); for (i = 0; i < 8; i ) { cChk = viCRC & 1; ListBox1->Items->Add(S.sprintf("i=%d, cChk=%d", i, cChk)); viCRC = viCRC >> 1; ListBox1->Items->Add(S.sprintf("0x%4.4X", viCRC)); viCRC = viCRC & 0x7FFF; ListBox1->Items->Add(S.sprintf("0x%4.4X", viCRC)); if (cChk == 1) { viCRC = viCRC ^ 0xA001; ListBox1->Items->Add(S.sprintf("0x%4.4X", viCRC)); } viCRC = viCRC & 0xFFFF; ListBox1->Items->Add(S.sprintf("0x%4.4X", viCRC)); } return viCRC; } [/code] 電腦執行結果: iCRC==0xFFFF viCRC^vcCRCBuf=0xFFBE i=0, cChk=0 0x7FDF 0x7FDF 0x7FDF i=1, cChk=1 0x3FEF 0x3FEF 0x9FEE 0x9FEE i=2, cChk=0 0x4FF7 0x4FF7 0x4FF7 i=3, cChk=1 0x27FB 0x27FB 0x87FA 0x87FA i=4, cChk=0 0x43FD 0x43FD 0x43FD i=5, cChk=1 0x21FE 0x21FE 0x81FF 0x81FF i=6, cChk=1 0x40FF 0x40FF 0xE0FE 0xE0FE i=7, cChk=0 0x707F 0x707F 0x707F iCRC==0x707F iCRC==0x7F70 僅供參考...^__^ |
tyw6455
一般會員 ![]() ![]() 發表:7 回覆:9 積分:3 註冊:2005-06-23 發送簡訊給我 |
謝謝JOW兄給的程式 找了不少資料,有驗證了上面不管是查表或是用算的程式都OK的 只是很納悶用紙筆按照數學的邏輯方式下去算CRC的值 一直找不出來他的多項式到等是什麼 只知道在程式裡面用的多項式(0xA001:1010 0000 0000 0001) 是由CRC-16的多項式 (X16 X15 X2 1) (0x11005)捨棄最高位數 (0x1005:1000 0000 0000 0101) 翻轉過來的 但是數學推算的多項式還是匹配不出來只能先把這個暫時先擱置了,有時間再來研究這一段 以下附上網路上部分原文: The CRC-16 error check sequence is implemented as described in the following paragraphs. The message (data bits only, disregarding start/stop and optional parity bits) is considered as one continuous binary number whose most significant bit (MSB) is transmitted first. The message is pre-multiplied by X16 (shifted left 16 bits), then divided by X16 X15 X2 1 expressed as a binary number (11000000000000101). The integer quotient digits are ignored and the 16-bit remainder (initialized to all ones at the start to avoid the case of all zeros being an accepted message) is appended to the message (MSB first) as the two CRC check bytes. The resulting message including CRC, when divided by the same polynominal (X16 X15 X2 1) at the receiver will give a zero remainder if no errors have occurred. (The receiving unit recalculates the CRC and compares it to the transmitted CRC). All arithmetic is performed modulo two (no carries). An example of the CRC-16 error check for message HEX 0207 (address 2, function 7 or a status request to slave number 2) is given by the following example. The device used to serialize the data for transmission will send the conventional LSB or right-most bit of each character first. In generating the CRC, the first bit transmitted is defined as the MSB of the dividend. For convenience then, and since there are no carries used in arithmetic, let’s assume while computing the CRC that the MSB is on the right. To be consistent, the bit order of the generating polynomial must be reversed. The MSB of the polynomial is dropped since it affects only the quotient and not the remainder. This yields 1010 0000 0000 0001 (Hex A001). Note that this reversal of the bit order will have no affect whatever on the interpretation or bit order of characters external to the CRC calculations. 擷取自: http://www.emersonprocess.com/raihome/documents/RAI_GC_Manual_GC_All_Modbus_Communications_Model_2500_3-9000-545-D_199211.pdf 這份文件 ===================引 用 jow 文 章=================== 以下省略 |
jow
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
===================引 用 tyw6455 文 章===================
謝謝JOW兄給的程式 那是你的程式, 我只是將每次運算的結果, Add到 ListBox1中, 顯示出來而已 ^^ 找了不少資料,有驗證了上面不管是查表或是用算的程式都OK的 只是很納悶用紙筆按照數學的邏輯方式下去算CRC的值 一直找不出來他的多項式到等是什麼 只知道在程式裡面用的多項式(0xA001:1010 0000 0000 0001) Trace你的程式, 其中並沒有引用到你所說的多項式 0xA001 ^^ 上篇所附的內容中, 最前面是手動推算...到 for loop i=1時, 與電腦 計算的結果相符: 0x0111 1111 1101 1111(0x7FDF) >>1-------------------i==1,cChk==1 0x0011 1111 1110 1111(0x3FEF) 0x7FDF i=1, cChk=1 0x3FEF 0x3FEF 再來是稍微修改了你的程式碼, 主要是 Keep一些運算過程的結果. 最後是電腦直接跑出來的結果, 以ListBox1->Items->SaveToFile(), 再 轉貼上來給你參考. 建議你Trace一下你自己的程式, CRC 計算之初值 為 0xFFFF, 傳入的0x41, 其實在你自己推算的式子裡高低位元組錯置了, 並且也引用程式中沒用到的值 0xA001, 所以你推算的結果, 會與電腦不同.. 僅供參考...^_^ |
q023057866
一般會員 ![]() ![]() 發表:0 回覆:1 積分:0 註冊:2010-01-08 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |