計算CRC-16循環(huán)冗余校驗碼的程序開發(fā)
【內(nèi)容摘要】CRC-16是檢測數(shù)據(jù)在發(fā)送過程中發(fā)生錯誤的常用校驗方法,本文通過從工程應(yīng)用的角度,講述如何實現(xiàn)CRC-16的程序開發(fā),并給出了Visual Basic和Visual C++程序代碼,給從事工業(yè)控制的人員在寫通信程序的時候提供一些有價值的參考。
一、前言
CRC的計算方式是將欲傳輸?shù)臄?shù)據(jù)塊視為一堆連續(xù)位所構(gòu)成的整個數(shù)值,將此數(shù)值除以一個特定的除數(shù),通常以二進(jìn)制表示,此除數(shù)稱為生成多項式(ANSI CRC-16的生成多項式為:x16 + x15 + x2 + 1)。目前較常用的CRC位數(shù)目有8和16以及32,在這里只講述如何寫CRC位數(shù)目為16的程序。CRC位數(shù)目越大,數(shù)據(jù)的錯誤檢測率則越高,但必須多花一些時間進(jìn)行數(shù)據(jù)的計算。
二、CRC-16計算步驟
CRC-16的計算方法有兩種:查表法與計算法,在這里只講述計算法。本人在2000年在江西亞東水泥公司上班時,在某電力儀表上的說明書中摘錄下來的計算CRC-16步驟如下:
1、 Load a 16-bit register with FFFF hex. Call this the CRC register.
2、 Exclusive OR the first 8-bit byte of the message with the low-order byte of the 16-bit CRC, putting the result in the CRC register.
3、 Shift the CRC register one bit to the right (toward the LSB), zero-filling the MSB. Extract and Examine.
4、 If the LSB was 1: Exclusive OR the CRC register with polynomial value A001 hex. If the LSB was 0: Repeat step 3 (another shift).
5、 Repeat step 3 and 4 until 8 shifts have been perbbbbed. When this is done, a complete 8-bit byte will have been processed.
6、 Repeat step 2 through 5 for the next 8-bit byte of them message.
7、 The final content of the CRC register is the CRC value.
中文解釋如下:
1、定義一個初始值為FFFF的16位的變量,該變量稱為CRC寄存器。(想想在程序中,應(yīng)該怎么表示16位的變量呢?)
2、把欲發(fā)送或接收消息的高8位和CRC寄存器的底8位作異或運(yùn)算,并把結(jié)果在賦到CRC寄存器。
3、CRC寄存器右移1位(朝最低位),同時最高位添零。取出并檢查最低位是否為1。(Visual Basic 里如何做移位的運(yùn)算呢?)
4、如果為1,則CRC寄存器與多項式A001異或;如果為0,則重復(fù)第3步的動作。(Visual Basic 里如何判斷一個字節(jié)里某個位的值呢?)
5、重復(fù)3和4直到完成了8次移位。這樣完整的8位字節(jié)將完成處理了。
6、對于下一個8位字節(jié)的處理就是重復(fù)第2步到第5步了
7、把所有的欲發(fā)送或接收消息這樣處理后, CRC寄存器里的值就是我們最終需要得到的CRC校驗碼。
如果你能正確回答我的問題,那么恭喜你,你自己可以用Visual Basic寫數(shù)據(jù)采集卡的控制程序了。
三、Visual Basic程序?qū)崿F(xiàn)
以江陰長江斯菲爾電力儀表公司CD194E系列多功能電力儀表的Modbus-RTU通訊協(xié)議的報文格式為例,該表通信報文格式使用的校驗方式就是CRC-16。通過對地址01H、命令04H、數(shù)據(jù)地址005CH和數(shù)據(jù)長度0004H的CRC-16運(yùn)算后得到主機(jī)請求數(shù)據(jù)報文的CRC16的校驗碼為31DBH。
程序界面
程序代碼:
Private Sub cmdGenerate_Click()
Dim OutByte(7) As Byte, CRC16() As Byte, CRC16LO As Byte, CRC16HI As Byte, TempHI As Byte, TempLO As Byte
Dim i As Integer, j As Integer
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
OutByte(0) = Val("&H" & txtOutByte(0).Text)
OutByte(1) = Val("&H" & txtOutByte(1).Text)
OutByte(2) = Val("&H" & txtOutByte(2).Text)
OutByte(3) = Val("&H" & txtOutByte(3).Text)
OutByte(4) = Val("&H" & txtOutByte(4).Text)
OutByte(5) = Val("&H" & txtOutByte(5).Text)
CRC16LO = &HFF '(1)Load a 16-bit register with FFFF hex.call this the CRC register.
CRC16HI = &HFF
For i = 0 To UBound (OutByte) - 2
CRC16LO = CRC16LO Xor OutByte (i) '(2)Exclusive OR the first 8-bit byte of the message
'with the low-order byte of the 16-bit CRC,
'putting the result in the CRC register
For j = 0 To 7
TempHI = CRC16HI
TempLO = CRC16LO
CRC16HI = CRC16HI \ 2 '(3)Shift the CRC register one bit to the right(toward the LSB),zero-filling CRC16LO = CRC16LO \ 2 'the MSB.Extract and Examine
If ((TempHI And &H1) = &H1) Then
CRC16LO = CRC16LO Or &H80 '移位時,如果高低位是1,則低高位加1
End If
If ((TempLO And &H1) = &H1) Then '(4)if the LSB was 1:
CRC16HI = CRC16HI Xor &HA0 'Exclusive OR the CRC register with polynomial value A001 hex.
CRC16LO = CRC16LO Xor &H1
End If 'if the LSB was 0: Repeat step 3 (another shift)
Next j '(5)Repeat step 3 and 4 until 8 shifts have been perbbbbed.
'when this is done, a complete 8-bit byte will have been processed.
Next i '(6)Repeat step 2 through 5 for the next 8-bit byte of them message.
OutByte(6) = CRC16LO '(7)The final contents of the CRC register is the CRC value
txtOutByte(6) = Hex(OutByte(6))
OutByte(7) = CRC16HI
txtOutByte(7) = Hex(OutByte(7))
End Sub
四、Visual C++程序?qū)崿F(xiàn)
程序界面
主要代碼:
#define CHECKVALUE(pt,nl) if((btData==NULL)||(nLength<=0)){AfxMessageBox("無效數(shù)組!");return -1;}
BOOL CCRC_16Dlg::OnInitDialog()
{
……
m_strCRC="01 04 00 5C 00 04";
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
void CCRC_16Dlg::OnOK()
{
UpdateData(TRUE);
Cbbbbbb strTemp;
strTemp=m_strCRC;
WORD CRC;
BYTE btData[4096];
int nLength=StrToHex(strTemp,btData);
CRC=ANSI_CRC_16(btData,nLength); //ANSI-16
Cbbbbbb str;
str.bbbbat(" %02X %02X",(BYTE)(CRC>>8),(BYTE)CRC);
AfxMessageBox(str);
CDialog::OnOK();
}
WORD CCRC_16Dlg::ANSI_CRC_16(BYTE* btData,int nLength) //ANSI CRC-16,x16 + x15 + x2 + 1
{
CHECKVALUE(btData,nLength);
WORD CRC=0xFFFF;
BYTE j,Tmp=0;
int i;
for(i=0;i<nLength;i++)
{
CRC^=btData[i];
for (j=0;j<8;j++)
{
Tmp=CRC&0x0001;
CRC=CRC>>1;
if(Tmp)CRC=(CRC^0xA001);
}
}
return ((CRC>>8)+(CRC<<8)); /* 應(yīng)用時高在先 */
}
int CCRC_16Dlg::StrToHex(Cbbbbbb str, BYTE *btData)
{
int nLength=str.GetLength();
if(nLength<2) return 0;
for(int i=0;i<nLength;i+=3) _stscanf(str.Mid(i,2),"%02X",&btData[i/3]);
return (i+1)/3;
}
五、結(jié)論
1, 這兩個實例可以當(dāng)作計算CRC-16的小工具,代碼對于自己要開發(fā)CRC-16的人員也提供一些參考價值。
2, 如果你在Visual Basic里,右移應(yīng)該做什么運(yùn)算,那么你就真的看進(jìn)去了,如果你不知道,可以發(fā)E-Mail給我。
3, 同一個實例,兩種開發(fā)語言,值得學(xué)習(xí)一下??吹竭@里,你可以告訴我在VC++中,異或是用什么符號?是不是很意外呢?
一、前言
CRC的計算方式是將欲傳輸?shù)臄?shù)據(jù)塊視為一堆連續(xù)位所構(gòu)成的整個數(shù)值,將此數(shù)值除以一個特定的除數(shù),通常以二進(jìn)制表示,此除數(shù)稱為生成多項式(ANSI CRC-16的生成多項式為:x16 + x15 + x2 + 1)。目前較常用的CRC位數(shù)目有8和16以及32,在這里只講述如何寫CRC位數(shù)目為16的程序。CRC位數(shù)目越大,數(shù)據(jù)的錯誤檢測率則越高,但必須多花一些時間進(jìn)行數(shù)據(jù)的計算。
二、CRC-16計算步驟
CRC-16的計算方法有兩種:查表法與計算法,在這里只講述計算法。本人在2000年在江西亞東水泥公司上班時,在某電力儀表上的說明書中摘錄下來的計算CRC-16步驟如下:
1、 Load a 16-bit register with FFFF hex. Call this the CRC register.
2、 Exclusive OR the first 8-bit byte of the message with the low-order byte of the 16-bit CRC, putting the result in the CRC register.
3、 Shift the CRC register one bit to the right (toward the LSB), zero-filling the MSB. Extract and Examine.
4、 If the LSB was 1: Exclusive OR the CRC register with polynomial value A001 hex. If the LSB was 0: Repeat step 3 (another shift).
5、 Repeat step 3 and 4 until 8 shifts have been perbbbbed. When this is done, a complete 8-bit byte will have been processed.
6、 Repeat step 2 through 5 for the next 8-bit byte of them message.
7、 The final content of the CRC register is the CRC value.
中文解釋如下:
1、定義一個初始值為FFFF的16位的變量,該變量稱為CRC寄存器。(想想在程序中,應(yīng)該怎么表示16位的變量呢?)
2、把欲發(fā)送或接收消息的高8位和CRC寄存器的底8位作異或運(yùn)算,并把結(jié)果在賦到CRC寄存器。
3、CRC寄存器右移1位(朝最低位),同時最高位添零。取出并檢查最低位是否為1。(Visual Basic 里如何做移位的運(yùn)算呢?)
4、如果為1,則CRC寄存器與多項式A001異或;如果為0,則重復(fù)第3步的動作。(Visual Basic 里如何判斷一個字節(jié)里某個位的值呢?)
5、重復(fù)3和4直到完成了8次移位。這樣完整的8位字節(jié)將完成處理了。
6、對于下一個8位字節(jié)的處理就是重復(fù)第2步到第5步了
7、把所有的欲發(fā)送或接收消息這樣處理后, CRC寄存器里的值就是我們最終需要得到的CRC校驗碼。
如果你能正確回答我的問題,那么恭喜你,你自己可以用Visual Basic寫數(shù)據(jù)采集卡的控制程序了。
三、Visual Basic程序?qū)崿F(xiàn)
以江陰長江斯菲爾電力儀表公司CD194E系列多功能電力儀表的Modbus-RTU通訊協(xié)議的報文格式為例,該表通信報文格式使用的校驗方式就是CRC-16。通過對地址01H、命令04H、數(shù)據(jù)地址005CH和數(shù)據(jù)長度0004H的CRC-16運(yùn)算后得到主機(jī)請求數(shù)據(jù)報文的CRC16的校驗碼為31DBH。
程序界面
程序代碼:
Private Sub cmdGenerate_Click()
Dim OutByte(7) As Byte, CRC16() As Byte, CRC16LO As Byte, CRC16HI As Byte, TempHI As Byte, TempLO As Byte
Dim i As Integer, j As Integer
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
OutByte(0) = Val("&H" & txtOutByte(0).Text)
OutByte(1) = Val("&H" & txtOutByte(1).Text)
OutByte(2) = Val("&H" & txtOutByte(2).Text)
OutByte(3) = Val("&H" & txtOutByte(3).Text)
OutByte(4) = Val("&H" & txtOutByte(4).Text)
OutByte(5) = Val("&H" & txtOutByte(5).Text)
CRC16LO = &HFF '(1)Load a 16-bit register with FFFF hex.call this the CRC register.
CRC16HI = &HFF
For i = 0 To UBound (OutByte) - 2
CRC16LO = CRC16LO Xor OutByte (i) '(2)Exclusive OR the first 8-bit byte of the message
'with the low-order byte of the 16-bit CRC,
'putting the result in the CRC register
For j = 0 To 7
TempHI = CRC16HI
TempLO = CRC16LO
CRC16HI = CRC16HI \ 2 '(3)Shift the CRC register one bit to the right(toward the LSB),zero-filling CRC16LO = CRC16LO \ 2 'the MSB.Extract and Examine
If ((TempHI And &H1) = &H1) Then
CRC16LO = CRC16LO Or &H80 '移位時,如果高低位是1,則低高位加1
End If
If ((TempLO And &H1) = &H1) Then '(4)if the LSB was 1:
CRC16HI = CRC16HI Xor &HA0 'Exclusive OR the CRC register with polynomial value A001 hex.
CRC16LO = CRC16LO Xor &H1
End If 'if the LSB was 0: Repeat step 3 (another shift)
Next j '(5)Repeat step 3 and 4 until 8 shifts have been perbbbbed.
'when this is done, a complete 8-bit byte will have been processed.
Next i '(6)Repeat step 2 through 5 for the next 8-bit byte of them message.
OutByte(6) = CRC16LO '(7)The final contents of the CRC register is the CRC value
txtOutByte(6) = Hex(OutByte(6))
OutByte(7) = CRC16HI
txtOutByte(7) = Hex(OutByte(7))
End Sub
四、Visual C++程序?qū)崿F(xiàn)
程序界面
主要代碼:
#define CHECKVALUE(pt,nl) if((btData==NULL)||(nLength<=0)){AfxMessageBox("無效數(shù)組!");return -1;}
BOOL CCRC_16Dlg::OnInitDialog()
{
……
m_strCRC="01 04 00 5C 00 04";
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
void CCRC_16Dlg::OnOK()
{
UpdateData(TRUE);
Cbbbbbb strTemp;
strTemp=m_strCRC;
WORD CRC;
BYTE btData[4096];
int nLength=StrToHex(strTemp,btData);
CRC=ANSI_CRC_16(btData,nLength); //ANSI-16
Cbbbbbb str;
str.bbbbat(" %02X %02X",(BYTE)(CRC>>8),(BYTE)CRC);
AfxMessageBox(str);
CDialog::OnOK();
}
WORD CCRC_16Dlg::ANSI_CRC_16(BYTE* btData,int nLength) //ANSI CRC-16,x16 + x15 + x2 + 1
{
CHECKVALUE(btData,nLength);
WORD CRC=0xFFFF;
BYTE j,Tmp=0;
int i;
for(i=0;i<nLength;i++)
{
CRC^=btData[i];
for (j=0;j<8;j++)
{
Tmp=CRC&0x0001;
CRC=CRC>>1;
if(Tmp)CRC=(CRC^0xA001);
}
}
return ((CRC>>8)+(CRC<<8)); /* 應(yīng)用時高在先 */
}
int CCRC_16Dlg::StrToHex(Cbbbbbb str, BYTE *btData)
{
int nLength=str.GetLength();
if(nLength<2) return 0;
for(int i=0;i<nLength;i+=3) _stscanf(str.Mid(i,2),"%02X",&btData[i/3]);
return (i+1)/3;
}
五、結(jié)論
1, 這兩個實例可以當(dāng)作計算CRC-16的小工具,代碼對于自己要開發(fā)CRC-16的人員也提供一些參考價值。
2, 如果你在Visual Basic里,右移應(yīng)該做什么運(yùn)算,那么你就真的看進(jìn)去了,如果你不知道,可以發(fā)E-Mail給我。
3, 同一個實例,兩種開發(fā)語言,值得學(xué)習(xí)一下??吹竭@里,你可以告訴我在VC++中,異或是用什么符號?是不是很意外呢?
本文標(biāo)簽:計算CRC-16循環(huán)冗余校驗碼的程序開發(fā)
* 由于無法獲得聯(lián)系方式等原因,本網(wǎng)使用的文字及圖片的作品報酬未能及時支付,在此深表歉意,請《計算CRC-16循環(huán)冗余校驗碼的程序開發(fā)》相關(guān)權(quán)利人與機(jī)電之家網(wǎng)取得聯(lián)系。
關(guān)于“計算CRC-16循環(huán)冗余校驗碼的程序開發(fā)”的更多資訊










