EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

CRC / CHKSUM

Started by Eirik Karlsen February 15, 2004
Hi Karl,
 
Here is the code I use. I downloaded the assembly (PIC side) code and a article (pdf attached) from EDN maginzine's website a couple of years ago and wrote the pascal code (PC side) from the article. On the PC side I assemble the message and then calculate the CRC over it. On the PIC side, I calc the CRC as I receive each byte. One nice feature is that since the complement of the CRC is stored in the message, on the PIC side when you include the 2 CRC bytes included in the received message, if the message was received correctly, your resulting CRC will be zero so you don't have to do a 2 byte comparision at the end.
 
You''ll have to tailor the PIC code to your message format. It written specifically for mine which is formated as:
 
<SOH> <NODEID> <MSGLEN> <CMD> <DATA1>..<<DATAn> <CRC1> <CRC2>
 
I use this between the PC and a device with 2 16F628 PIC's and between the 2 PICs on the board. Works great.
 
Scott Kellish
SoftSystem Solutions
s...@comcast.net
 
PIC Code:
 

;********************************************************************
;
; CRC-16  (x^16+x^15+x^2+x^0)
; No tables, No loops, No temporary registers used.
;
; Input:  W = Data byte for CRC calculation
;         crc_hi:_CRC_LO 16 bit CRC register
;
; Output: crc_hi:_CRC_LO updated.
;
; Notes:  CARRY is trashed.
;         DIGIT CARRY is trashed.
;         ZERO is trashed.
;         W is zero on exit.
;
;********************************************************************
 
; This will calculate a 16bit CRC and store the results as the last two bytes of the message
;
CALC_CRC16  MOVLW   2
            CALL    PROC_CRC16
            MOVF    CRC_HI, W
            XORLW   -1
            MOVWF   INDF
            INCF    FSR, F
            MOVF    _CRC_LO, W
            XORLW   -1
            MOVWF   INDF
            RETURN
 
; This will calculate a 16-bit CRC for a message, including the 2 passed CRC bytes.
; Returns Z = 0 if Message received incorrectly
;             Z = 1 if Message OK
;
CHK_CRC16   MOVLW   4
            CALL    PROC_CRC16
            MOVLW   HIGH CRC_CHECK
            SUBWF   _CRC_HI, W
            BNZ     CHK_CRC16A                      ; CRC MS wrong, ignore message
            MOVLW   LOW CRC_CHECK
            SUBWF   _CRC_LO, W
CHK_CRC16A  RETURN
 
;
PROC_CRC16  ADDWF   IOBUF_LEN, W
            MOVWF   _MSGLEN
            MOVLW   HIGH CRC_INIT          ; SINCE <SOH> IS PRESENT AND CONSTANT IN EACH MSG
            MOVWF   _CRC_HI                         ; INIT CRC WITH <SOH> CRC VALUE INSTEAD OF RUNNING
            MOVLW   LOW CRC_INIT                    ; THROUGH CALCULATION
            MOVWF   _CRC_LO
            MOVLW   IOBUF_ADDR
            MOVWF   FSR
            BTFSS   _SysState, _SysStateBroadcastMsg
            B       PROC_CRC16A
 
            INCF    _MSGLEN, F
            DECF    FSR, F
            MOVLW   0FFH
            B       PROC_CRC16B
 
PROC_CRC16A MOVF    INDF, W
PROC_CRC16B MOVWF   _CRC_TMP0            ; STORE DATA IN TEMPORARY REGISTER
            MOVLW   B'00001000'           ; SET COUNTER FOR 8 DATA BITS 
            MOVWF   _CRC_TMP2            ; LOAD COUNTER REGISTER
MORE_ROTATES  
            CLRWDT
            MOVF    _CRC_TMP0, W          ; MOVE BUFFERED DATA TO 2ND BUFFER
            MOVWF   _CRC_TMP1            ; THIS REGISTER IS CORRUPTED WITH EVERY PASS
            MOVF    _CRC_HI, W            ; MOVE UPPER SHIFT REGISTER TO WORKING REG.
            XORWF   _CRC_TMP1, F          ; XOR SHIFT REGISTER WITH DATA REGISTER
            BTFSS   _CRC_TMP1,7           ; MSB IS XOR OF STAGE16 AND INPUT DATA BIT
            B       NO_XORWF            ; IF BIT IS CLEAR THEN NO COMPLEMENT OF STAGE2,15
                                                  
            MOVLW   B'00000010'           ; PREPARE TO COMPLEMENT STAGE2
            XORWF   _CRC_LO, F           ; COMPLEMENT STAGE2 OF SHIFT REGISTER 
            MOVLW   B'01000000'           ; PREPARE TO COMPLEMENT STAGE15
            XORWF   _CRC_HI, F           ; COMPLEMENT STAGE15 OF SHIFT REGISTER 
NO_XORWF    RLF     _CRC_TMP0,F           ; ROTATE NEXT DATA BIT INTO POSITION
            RLF     _CRC_TMP1,F           ; ROTATE XOR OF INPUT INTO CRC16_LO
            RLF     _CRC_LO, F           ; SHIFT CRC16 REGISTER
            RLF     _CRC_HI, F           ; SHIFT CRC16 REGISTER
            DECFSZ  _CRC_TMP2, F          ; COUNT OUT 8 DATA BITS
            B       MORE_ROTATES          ; NOT FINISHED WITH THIS DATA BYTE
 
            INCF    FSR, F
            DECFSZ  _MSGLEN, F
            B       PROC_CRC16A
            RETURN
endasm
 
 
function TRIOPMessage.CalcCRC16(UseTxBuffer: Boolean): Boolean;
var
 Buf: PByteArray;
 Cnt: Integer;
 CRC16: WORD;
 i: Integer;
 procedure AddCRC16(const c: byte);
 var
  crc: Word;
 begin
  crc := CRC16;
  asm
   mov  dx, crc
   mov  al, c
   mov  ecx, 8
  @MoreRotates:
   mov  ah, al
   xor  ah, dh
   test ah, 128
   jz  @NoXor
   xor  dx, $4002
  @NoXor:  
   CLC 
   rcl  ax, 1
   rcl  dx, 1
   loop @MoreRotates
   mov  crc, dx
  end; 
  CRC16 := crc; 
 end;
begin
 if UseTxBuffer then
 begin
  Buf := FTxBuf;
  Cnt := FTxCnt;
 end
 else
 begin
  Buf := FRxBuf;
  Cnt := FRxCnt;
 end;
  
 CRC16 := $0000;
 i := 0;
 while i <= Cnt-2-1  do    // Iterate
 begin
  AddCRC16(Buf[i]);
  Inc(i);
 end;
 CRC16 := not CRC16;
 if UseTxBuffer then
 begin
  Buf[i] := Hi(CRC16);
  Buf[i+1] := Lo(CRC16);
  Result := True
 end
 else
  Result := ((Buf[i] = Hi(CRC16)) and (Buf[i+1] = Lo(CRC16)));
end;
----- Original Message -----
From: Eirik Karlsen
To: p...@yahoogroups.com
Sent: Monday, February 16, 2004 1:04 PM
Subject: Re: [piclist] Re: CRC / CHKSUM

Hi,
Well....I'd like to avoid crc because of its complexity (and I've never used it before).
And I won't even touch MD5 for the same reason.
Apart from this I've got only 2KB memory left for the entire bootloader.
MD5 and CRC tried to get max performance with minimal extra 'formatting', but for downloading
14KB over a 2meter RS232 cable into a pic the speed is of little importance...
I'm willing to wait a few extra sec's.

So how about transmitting a 32byte frame 2-3 times, receiver compares those and if all are equal accept,
else start a new frame transmission.
And what are the odds of getting 1 or 2  'bad bits'.....same bits, same location, in 3 consecutive 32byte frames?
I dunno....must be 1 in fantazillions !
 
 
 

 

If you want the maximum reliability you would go with a MD5 checksum
but it is a much heavier algorithm and I don't think you would want
to do that in a PIC.  Even MD5 is not 100% but it is so good that
statistically it is.  (The odds of an error that isn't caught is
something on the order of 1 in billions.)
 


--
*******************************************
VISIT MY HOME PAGE:
<http://home.online.no/~eikarlse/index.htm>
LAST UPDATED: 23/08/2003
*******************************************
Best Regards
Eirik Karlsen
 

to unsubscribe, go to http://www.yahoogroups.com and follow the instructions

Attachment (not stored)
PICCRC.pdf
Type: application/pdf

Sergio,
thanks..just send it over so I can have look at it.

sergio masci wrote:No, CRC is very very easy. Fast CRC is a little bit more complicated. I can

let you have the source for the simple solution written in XCSB if you would
like (it's only about 10 lines of code). I don't think the fast version
would be worth the overhead of using the required 512 byte table.

Regards
Sergio Masci


--
*******************************************
VISIT MY HOME PAGE:
<http://home.online.no/~eikarlse/index.htm>
LAST UPDATED: 23/08/2003
*******************************************
Best Regards
Eirik Karlsen
 

Scott and others;
thanks for your valuable input...looks like I'll be going for a CRC16.
This type of code is easily simulated (as opposed to all the real-time motor control stuff in this program),
Will try some of your suggestions, simulate, inject some random noise to verify that 'all' errors are trapped. Scott Kellish wrote:--

> *******************************************
> VISIT MY HOME PAGE:
> <http://home.online.no/~eikarlse/index.htm>
> LAST UPDATED: 23/08/2003
> *******************************************
> Best Regards
> Eirik Karlsen
>




Eirik Karlsen wrote:
> Sergio,
> thanks..just send it over so I can have look at it.
>
> sergio masci wrote:No, CRC is very very easy. Fast CRC is a little bit
more complicated. I can
>
> > let you have the source for the simple solution written in XCSB if you
would
> > like (it's only about 10 lines of code). I don't think the fast version
> > would be worth the overhead of using the required 512 byte table.
> >
> > Regards
> > Sergio Masci


Here you go,

Regards
Sergio Masci

http://www.xcprod.com/titan/XCSB - optimising structured PIC BASIC compiler const CRCCCITT = 0x1021

proc uint CRC_byte(uint acc, ubyte data)

ubyte k

ubyte acc2 for k=8 while k>0 step k-=1 do

acc2 = acc >> 8

acc = acc << 1

if ((data ^ acc2) & 0x80) != 0 then

acc = acc ^ CRCCCITT
endif

data = data << 1
done

return acc
endproc


I have used the following for several years on a variety of Modbus systems.  It works fine on 20mhz xtal for up to 56k.  Bear in mind going any faster might need table lookups and not calcs.  Modbus use the TTY version of CRC checking.
 
Initialise with this before use
 movlw 0xFF
 movwf CRC_Low
 movwf CRC_High
 
add to the checksum with this
 movfp ModbusBuffer1,WREG
 call CallAddCRC16M                    ;call for every byte in packet that is tx'ed except the crc bytes

 
obviously the results in CRC_High and CRC_Low that is calculated on the fly as it comes in is then compared with the last two bytes in the packet that is the crc.
 
;-------------------------
;   ******************************  CRC16 routine  ******************************************
;-------------------------
;--CLASS REGISTERS-----------------------
;Temp1    
;CRC_High
;CRC_Low   
;----
CallAddCRC16M 
  xorwf CRC_High,f
  movlw 8
  movwf Temp1
 
CRC_Loop 
  bcf ALUSTA,C
  rrcf CRC_Low,f 
  rrcf CRC_High,f
  btfss ALUSTA,C
  goto NoXOring
  movlw B'10100000'
  xorwf CRC_Low,f
  movlw B'0000001'
  xorwf CRC_High,f
NoXOring
  decfsz Temp1,F
   goto CRC_Loop
  return
----- Original Message -----
From: Scott Kellish
To: p...@yahoogroups.com
Sent: Tuesday, February 17, 2004 9:11 AM
Subject: Re: [piclist] Re: CRC / CHKSUM

Hi Karl,
 
Here is the code I use. I downloaded the assembly (PIC side) code and a article (pdf attached) from EDN maginzine's website a couple of years ago and wrote the pascal code (PC side) from the article. On the PC side I assemble the message and then calculate the CRC over it. On the PIC side, I calc the CRC as I receive each byte. One nice feature is that since the complement of the CRC is stored in the message, on the PIC side when you include the 2 CRC bytes included in the received message, if the message was received correctly, your resulting CRC will be zero so you don't have to do a 2 byte comparision at the end.
 
You''ll have to tailor the PIC code to your message format. It written specifically for mine which is formated as:
 
<SOH> <NODEID> <MSGLEN> <CMD> <DATA1>..<<DATAn> <CRC1> <CRC2>
 
I use this between the PC and a device with 2 16F628 PIC's and between the 2 PICs on the board. Works great.
 
Scott Kellish
SoftSystem Solutions
s...@comcast.net
 
PIC Code:
 

;********************************************************************
;
; CRC-16  (x^16+x^15+x^2+x^0)
; No tables, No loops, No temporary registers used.
;
; Input:  W = Data byte for CRC calculation
;         crc_hi:_CRC_LO 16 bit CRC register
;
; Output: crc_hi:_CRC_LO updated.
;
; Notes:  CARRY is trashed.
;         DIGIT CARRY is trashed.
;         ZERO is trashed.
;         W is zero on exit.
;
;********************************************************************
 
; This will calculate a 16bit CRC and store the results as the last two bytes of the message
;
CALC_CRC16  MOVLW   2
            CALL    PROC_CRC16
            MOVF    CRC_HI, W
            XORLW   -1
            MOVWF   INDF
            INCF    FSR, F
            MOVF    _CRC_LO, W
            XORLW   -1
            MOVWF   INDF
            RETURN
 
; This will calculate a 16-bit CRC for a message, including the 2 passed CRC bytes.
; Returns Z = 0 if Message received incorrectly
;             Z = 1 if Message OK
;
CHK_CRC16   MOVLW   4
            CALL    PROC_CRC16
            MOVLW   HIGH CRC_CHECK
            SUBWF   _CRC_HI, W
            BNZ     CHK_CRC16A                      ; CRC MS wrong, ignore message
            MOVLW   LOW CRC_CHECK
            SUBWF   _CRC_LO, W
CHK_CRC16A  RETURN
 
;
PROC_CRC16  ADDWF   IOBUF_LEN, W
            MOVWF   _MSGLEN
            MOVLW   HIGH CRC_INIT          ; SINCE <SOH> IS PRESENT AND CONSTANT IN EACH MSG
            MOVWF   _CRC_HI                         ; INIT CRC WITH <SOH> CRC VALUE INSTEAD OF RUNNING
            MOVLW   LOW CRC_INIT                    ; THROUGH CALCULATION
            MOVWF   _CRC_LO
            MOVLW   IOBUF_ADDR
            MOVWF   FSR
            BTFSS   _SysState, _SysStateBroadcastMsg
            B       PROC_CRC16A
 
            INCF    _MSGLEN, F
            DECF    FSR, F
            MOVLW   0FFH
            B       PROC_CRC16B
 
PROC_CRC16A MOVF    INDF, W
PROC_CRC16B MOVWF   _CRC_TMP0            ; STORE DATA IN TEMPORARY REGISTER
            MOVLW   B'00001000'           ; SET COUNTER FOR 8 DATA BITS 
            MOVWF   _CRC_TMP2            ; LOAD COUNTER REGISTER
MORE_ROTATES  
            CLRWDT
            MOVF    _CRC_TMP0, W          ; MOVE BUFFERED DATA TO 2ND BUFFER
            MOVWF   _CRC_TMP1            ; THIS REGISTER IS CORRUPTED WITH EVERY PASS
            MOVF    _CRC_HI, W            ; MOVE UPPER SHIFT REGISTER TO WORKING REG.
            XORWF   _CRC_TMP1, F          ; XOR SHIFT REGISTER WITH DATA REGISTER
            BTFSS   _CRC_TMP1,7           ; MSB IS XOR OF STAGE16 AND INPUT DATA BIT
            B       NO_XORWF            ; IF BIT IS CLEAR THEN NO COMPLEMENT OF STAGE2,15
                                                  
            MOVLW   B'00000010'           ; PREPARE TO COMPLEMENT STAGE2
            XORWF   _CRC_LO, F           ; COMPLEMENT STAGE2 OF SHIFT REGISTER 
            MOVLW   B'01000000'           ; PREPARE TO COMPLEMENT STAGE15
            XORWF   _CRC_HI, F           ; COMPLEMENT STAGE15 OF SHIFT REGISTER 
NO_XORWF    RLF     _CRC_TMP0,F           ; ROTATE NEXT DATA BIT INTO POSITION
            RLF     _CRC_TMP1,F           ; ROTATE XOR OF INPUT INTO CRC16_LO
            RLF     _CRC_LO, F           ; SHIFT CRC16 REGISTER
            RLF     _CRC_HI, F           ; SHIFT CRC16 REGISTER
            DECFSZ  _CRC_TMP2, F          ; COUNT OUT 8 DATA BITS
            B       MORE_ROTATES          ; NOT FINISHED WITH THIS DATA BYTE
 
            INCF    FSR, F
            DECFSZ  _MSGLEN, F
            B       PROC_CRC16A
            RETURN
endasm
 
 
function TRIOPMessage.CalcCRC16(UseTxBuffer: Boolean): Boolean;
var
 Buf: PByteArray;
 Cnt: Integer;
 CRC16: WORD;
 i: Integer;
 procedure AddCRC16(const c: byte);
 var
  crc: Word;
 begin
  crc := CRC16;
  asm
   mov  dx, crc
   mov  al, c
   mov  ecx, 8
  @MoreRotates:
   mov  ah, al
   xor  ah, dh
   test ah, 128
   jz  @NoXor
   xor  dx, $4002
  @NoXor:  
   CLC 
   rcl  ax, 1
   rcl  dx, 1
   loop @MoreRotates
   mov  crc, dx
  end; 
  CRC16 := crc; 
 end;
begin
 if UseTxBuffer then
 begin
  Buf := FTxBuf;
  Cnt := FTxCnt;
 end
 else
 begin
  Buf := FRxBuf;
  Cnt := FRxCnt;
 end;
  
 CRC16 := $0000;
 i := 0;
 while i <= Cnt-2-1  do    // Iterate
 begin
  AddCRC16(Buf[i]);
  Inc(i);
 end;
 CRC16 := not CRC16;
 if UseTxBuffer then
 begin
  Buf[i] := Hi(CRC16);
  Buf[i+1] := Lo(CRC16);
  Result := True
 end
 else
  Result := ((Buf[i] = Hi(CRC16)) and (Buf[i+1] = Lo(CRC16)));
end;
----- Original Message -----
From: Eirik Karlsen
To: p...@yahoogroups.com
Sent: Monday, February 16, 2004 1:04 PM
Subject: Re: [piclist] Re: CRC / CHKSUM

Hi,
Well....I'd like to avoid crc because of its complexity (and I've never used it before).
And I won't even touch MD5 for the same reason.
Apart from this I've got only 2KB memory left for the entire bootloader.
MD5 and CRC tried to get max performance with minimal extra 'formatting', but for downloading
14KB over a 2meter RS232 cable into a pic the speed is of little importance...
I'm willing to wait a few extra sec's.

So how about transmitting a 32byte frame 2-3 times, receiver compares those and if all are equal accept,
else start a new frame transmission.
And what are the odds of getting 1 or 2  'bad bits'.....same bits, same location, in 3 consecutive 32byte frames?
I dunno....must be 1 in fantazillions !
 
 
 

 

If you want the maximum reliability you would go with a MD5 checksum
but it is a much heavier algorithm and I don't think you would want
to do that in a PIC.  Even MD5 is not 100% but it is so good that
statistically it is.  (The odds of an error that isn't caught is
something on the order of 1 in billions.)
 


--
*******************************************
VISIT MY HOME PAGE:
<http://home.online.no/~eikarlse/index.htm>
LAST UPDATED: 23/08/2003
*******************************************
Best Regards
Eirik Karlsen
 

to unsubscribe, go to http://www.yahoogroups.com and follow the instructions
to unsubscribe, go to http://www.yahoogroups.com and follow the instructions




The 2024 Embedded Online Conference