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;
| |||
|
CRC / CHKSUM
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
|
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
|