在无线电通信中,为了保证数据在传输中的完整性,我们通常需要检查其内容,最愚蠢的办法不过是回滚数据咯,需要让主机逐字节检查,不过那种方法很占用通信的时间。CRC效验是一种更好的方法。这样不必每回都回滚数据,只需通过CRC码来检测其完整性。大大节省了时间。
为方便解释其原理,总结了CRC所有操作:
1.装一个16位的寄存器,所有数位均为1 2.取被校验串的一个字节与16位寄存器的高位字节进行"异或"运算.运算结果放入这个16位寄存器. 3.把这个16位寄存器向右移一位. 4.若向右(标记位)移出的位数是1,则生成多项式1010 0000 0000 0001和这个寄存器进行"异或"运算;若向右移出的位数是0,则返回步骤3. 5.重复步骤3和4,直至移出8位. 6.取被校验串的下一个字节 7.重复步骤3~6,直至被校验串的所有字节均与16位寄存器进行"异或"运算,并移位8次. 8.这个16位寄存器的内容即2字节CRC错误校验码. 生成CRC的代码:
C语言: //////////////////////////////////////////////////////////////////////////// // Deions: 计算指定数据的CRC值, 依据的是国家标准协议中所定义的算法 // // Parameters: // // BYTE * 要计算CRC的数据 // // int 被计算CRC的数据的字节数 // //////////////////////////////////////////////////////////////////////////// WORD __fastcall TGBAdapterListener::GetCRCWord(BYTE * Buf, int BufLen) { WORD wRet = 0xFFFF;
for (int iIndex = 0; iIndex < BufLen; iIndex ++) { wRet = (WORD)(HIBYTE(wRet) ^ Buf[iIndex]);
for (int iLooper = 0; iLooper < 8; iLooper ++) { if (wRet & 0x0001) { wRet = (wRet >> 1) ^ 0xA001; } else wRet = (wRet >> 1); } } return wRet; }
DELPHI语言: \\一 GetCRCWord(Buf:array of byte; BufLen:integer):WORD; var wRet:WORD; iIndex:integer; iLooper:integer; begin wRet := $FFFF; for iIndex :=0 to BufLen-1 do begin wRet := WORD(Hi(wRet) xor Buf[iIndex]); for iLooper := 0 to 7 do begin if (wRet and $0001) = 1 then wRet := (wRet shr 1) xor $A001 else wRet := wRet shr 1; end; end; result := wRet; end; \\二 GetCRCWord_2(Buf:PChar; BufLen:integer):WORD; var wRet:WORD; iIndex:integer; iLooper:integer; begin wRet := $FFFF; for iIndex :=0 to BufLen-1 do begin wRet := WORD(Hi(wRet) xor Ord(Buf[iIndex])); for iLooper := 0 to 7 do begin if (wRet and $0001) = 1 then wRet := (wRet shr 1) xor $A001 else wRet := wRet shr 1; end; end; result := wRet; end;
procedure TForm1.Button1Click(Sender: TObject); var s:string; len:integer; a:array of byte;
crc16:integer; begin s := 'asdasd'; len := length(s); SetLength(a, len); CopyMemory(a,Pchar(s),len); crc16 := GetCRCWord(a,len); ShowMessage(IntToHex(crc16,4)); end;
procedure TForm1.Button2Click(Sender: TObject); var s:string; len:integer; crc16:integer; begin s := 'asdasd'; len := length(s); crc16 := GetCRCWord_2(Pchar(s),len); ShowMessage(IntToHex(crc16,4)); end; ////////////////////////////////////////////////////////////////////////////
VB算法:
Public CRC16(data() As Byte) As Byte() Dim CRC16Lo As Byte, CRC16Hi As Byte 'CRC寄存器 Dim CL As Byte, CH As Byte '多项式码&HA001 Dim SaveHi As Byte, SaveLo As Byte Dim i As Integer Dim Flag As Integer Dim ReturnData(1) As Byte CRC16Lo = &HFF CRC16Hi = &HFF CL = &H1 CH = &HA0 For Ii = 0 To UBound(data) CRC16Lo = CRC16Lo Xor data(Ii) '每一个数据与CRC寄存器进行异或 For Flag = 0 To 7 SaveHi = CRC16Hi SaveLo = CRC16Lo CRC16Hi = CRC16Hi \ 2 '高位右移一位 CRC16Lo = CRC16Lo \ 2 '低位右移一位 If ((SaveHi And &H1) = &H1) Then '如果高位字节最后一位为1 CRC16Lo = CRC16Lo Or &H80 '则低位字节右移后前面补1 End If '否则自动补0 If ((SaveLo And &H1) = &H1) Then '如果LSB为1,则与多项式码进行异或 CRC16Hi = CRC16Hi Xor CH CRC16Lo = CRC16Lo Xor CL End If Next Flag Next Ii ReturnData(0) = CRC16Lo 'CRC低位 ReturnData(1) = CRC16Hi 'CRC高位 CRC16 = ReturnData End
C算法: void crc16(unsigned char r_data[],unsigned int length) { unsigned char cl,ch; unsigned char savehi,savelo; int ii,flag; crc16hi=0xFF; crc16lo=0xFF; cl=0x1; ch=0xA0; for (ii=0;ii<length;ii++) { crc16lo=(crc16lo ^ r_data[ii]); for (flag=0;flag<8;flag++) { savehi=crc16hi; savelo=crc16lo; crc16hi=(crc16hi>>1); crc16lo=(crc16lo>>1); if ((savehi & 0x01)==0x01) crc16lo=(crc16lo | 0x80); if ((savelo & 0x01)==0x01) { crc16hi = (crc16hi ^ ch); crc16lo = (crc16lo ^ cl); } } } }
汇编算法 :
;********************************************************** ;CRC16校验子程序 ;R1 需进行CRC校验数据起始地址 ;CRC16_L CRC校验数据低字节 CRC16_H CRC校验数据高字节 ;R7 需进行CRC校验的字节数 ;********************************************************** CRC16: MOV CRC16_L,#0FFH MOV CRC16_H,#0FFH CRC16_LOOP1: MOVX A,@R1 XRL CRC16_L,A MOV R6,#08H CRC16_LOOP2: MOV R3,CRC16_L MOV R4,CRC16_H CLR C MOV A,CRC16_H RRC A MOV CRC16_H,A MOV A,CRC16_L RRC A MOV CRC16_L,A JNC CRC16_STEP XRL CRC16_L,#01H XRL CRC16_H,#0A0H CRC16_STEP: DJNZ R6,CRC16_LOOP2 INC R1 DJNZ R7,CRC16_LOOP1 RETI
|