Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

PIC CRC CODE

788 views
Skip to first unread message

Peter Warriner

unread,
Jul 14, 1998, 3:00:00 AM7/14/98
to
I am looking for a PIC16F84 based implementation of the
CCITT-16 CRC polynomial checksum, to validate a series of
data in registers. Can anyone help??

Regards,
Peter Warriner.

=========================================================
The sky is full of holes, to let the water in,
But the holes are really small, thats why rain is thin.
=========================================================
WWW: http://www.ncl.ac.uk/~n07tv/index.html
TEL: +44 191 2228552
FAX: +44 191 2228180
=========================================================

tom ray

unread,
Jul 14, 1998, 3:00:00 AM7/14/98
to
Peter Warriner (P.D.Wa...@ncl.ac.uk) wrote:
: I am looking for a PIC16F84 based implementation of the

: Regards,
: Peter Warriner.

I can show you the 'C' code, if it's not too much bother to
extrapolate to asm:

word crc16( byte *data_p, int numchars )
{
byte data;
word crc;

crc = 0xffff;
while( numchars-- ) {
data = *data_p++ ^ (byte)crc;
data = data ^ (data << 4);
crc = (crc >> 8) ^ (data << 8) ^ (data << 3 ) ^ (data >> 4);
}
return crc;
}


I got this from Embedded Systems Programming some years back - I
think the author was Pete Crenshaw but I may be wrong. A table-driven
implementation is also available.


tom ray

unread,
Jul 15, 1998, 3:00:00 AM7/15/98
to
tom ray (tr...@unlinfo.unl.edu) wrote:

<snip>

For proper attribution I should mention this code appeared
in the Jan 1992 issue of Embedded Systems Programming magazine
in an article titled "Implementing CRC's" by Jack Crenshaw.

Also the CRC val should be initialized to 0 for CCITT, 0xffff for
SDLC. And finally the crc must be byte-swapped and inverted
prior to transmission, i.e.

crc = crc16( data, numch );

xmtd_crc = ~(((crc & 0xff)<< 8) + (crc >> 8));

Kevin Towers

unread,
Jul 15, 1998, 3:00:00 AM7/15/98
to

You forgot to mention that this method depends on a lookup table. The
table is:

static uint crc_tab[256] =

{0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,

0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,

0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,

0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,

0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,

0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,

0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,

0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,

0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,

0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,

0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,

0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,

0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,

0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,

0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,

0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,

0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,

0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,

0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,

0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,

0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,

0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,

0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,

0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,

0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,

0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,

0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,

0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,

0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,

0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,

0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,

0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0};

To calc the crc:

unsigned int crc = 0;
char *cptr = &msg[0];
for (i = 0; i < msg_len; i++)
crc = crc_tab[*cptr++ ^ (uchar)crc] ^ *((uchar *)&crc + 1);

This code assumes the processor is little endian (LSB first in
memory), which the PIC processors are.

A great paper on the CRC topic is "A PAINLESS GUIDE TO CRC ERROR
DETECTION ALGORITHMS"

"Everything you wanted to know about CRC algorithms, but were afraid
to ask for fear that errors in your understanding might be detected."

by Ross N. Williams.
ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt

Kevin Towers
kto...@omnexcontrols.com

David Roussel

unread,
Jul 16, 1998, 3:00:00 AM7/16/98
to Peter Warriner
I have coded both an 8-bit and a 16-bit CRC routine for use on a
PIC16F84. The code has been highly optimised and always executes in the
same time (no branches).

I don't have it with me now, but I shall brong it in next time come in
the univeristy.

NB. my code also contains parity generating code.

David Roussel

P.S. Do you know where I can get some full-duplex interrupt driven
serial comms routines for the 16F84?


David Roussel

unread,
Jul 17, 1998, 3:00:00 AM7/17/98
to Peter Warriner

Peter Warriner wrote:

> I am looking for a PIC16F84 based implementation of the
> CCITT-16 CRC polynomial checksum, to validate a series of
> data in registers. Can anyone help??

Here you are.
There are two main routines one for a CRC-16 update and one for a ATM
HEC CRC update. Both routines use indirect addressing to load the data
byte and return after updating the CRC shift register. So to process a
16 byte block the shift reg should be set to zero and the update routine
called once per byte, whilst updating the FSR between calls.

Both routines are highly optimised and do not need look up tables. I
took the basic algorithm from Aram Perez, 'Byte-Wise CRC Calculations',
IEEE Micro June 1983. However I had to total recode it since Aram Perez
numbers his bit positions differently from me. That may mean I have
actually implemented an inverse CRC-16, and it is not easy task to
change it.

Please feel free to use this code, but not hold me responsible for
errors.

David Roussel.

;***************************************************************
;
; crc2.asm David Roussel 12/2/1998
;
; CRC calculation test code
;
;***************************************************************

LIST P=16F84, R=dec, n=0
INCLUDE <p16F84.inc>

;***************************************************************

rambase = 0Ch ; start address of user file registers
blocksize = 10 ; total size of block to compute CRC on,
; including the 2 CRC bytes themselves
; must be greater than 2, since using CRC-16

;***************************************************************

CBLOCK rambase
temp1 ; general purpose regs
temp2
temp3
temp4
bcount ; counter for bytes in datablock
ENDC

CBLOCK
DataBlock: blocksize
CRClo
CRChi
CRC8
ENDC

;***************************************************************

org 0 ; reset vector
goto main

org 04 ; interrupt vector
retfie ; ain't no interrupt here mate

main
call init_CRC
call loaddata
movlw blocksize-2
movwf bcount
addlw DataBlock+1
movwf FSR
mainloop
call update_CRC8

decf FSR, F ; advance to next message byte
decfsz bcount, F ; are we done yet?
goto mainloop

; copy calculated CRC into message and then recompute
; to see if we get remainder zero.

movf CRC8, W
movwf DataBlock+1

call init_CRC

movlw blocksize-1
movwf bcount
addlw DataBlock
movwf FSR
checkloop
call update_CRC8

decf FSR, F ; advance to next message byte
decfsz bcount, F ; are we done yet?
goto checkloop

; was there an error?
movf CRClo, W
iorwf CRChi, W
btfss STATUS, Z ; z=1 if CRC-16==0
goto reception_error

reception_good
; nice one :)
sleep
goto reception_good

infiniteloop
sleep
goto infiniteloop ; ad infinitum

reception_error
; oh dear :(
goto reception_error

;***************************************************************
; Initialise the CRC routine
;***************************

init_CRC
clrf CRClo
clrf CRChi
clrf CRC8
return

;***************************************************************
; update_CRC
;***********
; This function reads the current input byte
; form INDF, and computes the next stage in
; the CRC-16 calculation.
;
; The CRC-16 modulo-2 polynomial is:
; X^16 + X^15 + X^2 + 1
;
; The algorithm used based on an article:
;
; Aram Perez, 'Byte-Wise CRC Calculations', IEEE Micro June 1983.
;
; It works by compling all the operations needed in 8 steps
; of a shift register CRC implemetation into one 8-bit byte
; calculation.
;
; Perez's Algorithm:
; 1) XOR the input byte with the low-order byte of
; the CRC register to obtain T.
; 2) Shift the CRC register eight bits to the right.
; 3) Calculate the 16-bit value V from T.
; (this is the cumulative effect of the 8 steps)
; 4) XOR the CRC register with the calculated value.
; 5) Repeat 1-4, for all message bytes
;
; My version:
; Steps 2) and 3) are combined onto one. (In fact most of the routine
; seems to be be block of xors shifts and movs).
; Perez labeled his bits from 1 to 16, I label mine in a more
conventional
; manner: from 15 to 0, in
;
; To calculate the next CRC define:
; S s[15:0] = the 16 bits of the CRC-16 check digit
; S' s' = the previous value of the CRC (should be set to zero before
first iteration)
; B b[7:0] = the 8-bit input byte
; T t[7:0] = s[15:8] + b[7:0]
; p := t[0] + t[1] + t[2] + t[3] + t[4] + t[5] + t[6] + t[7] ; the even
parity bit
;
; where (+) is modulo-2, e.g. 1 + 1 = 0 (the eXclusixe-OR operation)
;
; s[ 0] := p
; s[ 1] := p + t[0]
; s[ 2] := t[0] + t[1]
; s[ 3] := t[1] + t[2]
; s[ 4] := t[2] + t[3]
; s[ 5] := t[3] + t[4]
; s[ 6] := t[4] + t[5]
; s[ 7] := t[5] + t[6]
;
; s[ 8] := t[6] + t[7] + s'[0]
; s[ 9] := t[7] + s'[1]
; s[10] := 0 + s'[2]
; s[11] := 0 + s'[3]
; s[12] := 0 + s'[4]
; s[13] := 0 + s'[5]
; s[14] := 0 + s'[6]
; s[15] := p + s'[7]
;
; Program size : 32 instructions
; Execution time : 32 cycles, with no branching
; State variables : CRChi and CRClo
; Temporary varaibles : temp1, temp2
;

update_CRC
bcf STATUS, C
movf INDF, W
xorwf CRChi, W
movwf temp1 ; temp1 := INDF XOR CRChi
; temp1 == T
; Calculate X
rrf temp1, W
xorwf temp1, W ; temp1 == Q
movwf temp2 ; temp2 := (temp1 XOR (temp1 << 1))

; move 2 msb of temp2 into 2 lsb of CRChi, via CARRY
clrf CRChi
bcf STATUS, C
rlf temp2, F
rlf CRChi, F ; first bit
rlf temp2, F
rlf CRChi, F ; second bit

; calculate p, using parity routine
; at the end of this block temp1='xxxxxxzp'
; where p is the even parity bit, and z = p xor (bit7 of the orginal
temp1)
bcf STATUS, C
rrf temp1, W
xorwf temp1, F
swapf temp1, W
xorwf temp1, W
movwf temp1
rrf temp1, F
rrf temp1, F
xorwf temp1, F

movf CRClo, W
xorwf CRChi, F

movlw B'00000011'
andwf temp1, W ; W := 000000zp
xorwf temp2, W ; actually this could be an ior, but it makes no
difference
movwf CRClo

rrf temp1, F ; move bit p into C
clrf temp1
rrf temp1, W ; W now contains p of temp1, only, in msb
xorwf CRChi, F ; so now the msb of CRChi is xored with the parity bit

return


;***************************************************************
; Generate Even Parity Bit
; Input: given data byte in temp1
; Ouput: return B'xxxxxxxp' in temp1 ,where p=partiy bit

; (this routine is in-lined to the above routine, but I have included it

; seperately in case you need to generate parity.
; But if you are using a CRC, which is altogether better than parity
checking
; then this code is of no use.
;
;gen_parity
; bcf STATUS, C
; rrf temp1, W
; xorwf temp1, F
; swapf temp1, W
; xorwf temp1, W
; movwf temp1
; rrf temp1, F
; rrf temp1, F
; xorwf temp1, F

;***************************************************************
; Fills up the message buffer files with data from a table.
;
loaddata
movlw blocksize
movwf bcount
addlw DataBlock-1
movwf FSR
loadloop
movf bcount, W
addlw -1
call getdata
movwf INDF
decf FSR, F ; advance to next message byte
decfsz bcount, F ; are we done yet?
goto loadloop

return
;***************************************************************

getdata:
addwf PCL, F
retlw 0 ; Initial CRClo
retlw 0 ; Initial CRChi
retlw 'H'
retlw 'e'
retlw 'l'
retlw 'l'
retlw 'o'
retlw ' '
retlw ':'
retlw ')'

;***************************************************************

;***************************************************************
; update 8-bit CRC
;*****************
;
; Based on the ATM HEC CRC (don't you just love TLAs?)
; <Asynchronous Transmission Mode>
; <Header Error Control>
; <Cyclic Redundancy Check>
; Generator Polynomial:
; X^8 + X^2 + X + 1
; which is expurgated, meaning single bit errors can be isolated and
corrected.
;
; registers used:
; CRC8 - the 8-bit CRC digit and result
; INDF - contains the data byte to process
; dbyte - local variable
;
; Calculation Table: bi = si XOR xi, or i=0..7 and x is input byte, s
is CRC digit.
;
; s7 s6 s5 s4 s3 s2 s1 s0
; ----------------------- .
; b7 b6 b6 b4 b3 b2 b1 b0 /|\
; b6 b5 b4 b3 b2 b1 b0 b7 | XOR
; b5 b4 b3 b2 b1 b0 b6 b6 |
; b7 b6 |
;
; Program size : 15 instructions
; Execution time : 15 cycles, with no branching
; State variables : CRC8
; Temporary varaibles : dbyte
;
#define dbyte temp1

update_CRC8
movf INDF, W
xorwf CRC8 ,W
movwf dbyte ; {b7 b6 b5 b4 b3 b2 b1 b0} := INDF XOR CRC8
movwf CRC8 ; CRC8 := dbyte
rlf dbyte, W ; C := {b7}
rlf dbyte, W ; W := {b6 b5 b4 b3 b2 b1 b0 b7}
xorwf CRC8, F ; CRC8 := CRC8 XOR W
swapf dbyte, W ; W := {b3 b2 b1 b0 b7 b6 b5 b4}
andlw 0x0C ; W := {0 0 0 0 b7 b6 0 0 }
xorwf CRC8, F
rlf dbyte, W ; C := {b6}
rlf dbyte, F ; dbyte := {b6 b5 b4 b3 b2 b1 b0 b6}, C := b7
rlf dbyte, W ; C := b6
rlf dbyte, F ; dbyte := {b5 b4 b3 b2 b1 b0 b6 b6}, C := b6
xorwf CRC8, F

return


;***************************************************************
; The
END
; , c'est fini

Marty Flickinger

unread,
Jul 17, 1998, 3:00:00 AM7/17/98
to
Kevin Towers wrote:
> A great paper on the CRC topic is "A PAINLESS GUIDE TO CRC ERROR
> DETECTION ALGORITHMS"
>
> "Everything you wanted to know about CRC algorithms, but were afraid
> to ask for fear that errors in your understanding might be detected."
>
> by Ross N. Williams.
> ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt

FWIW, the Rocksoft archive has moved to:

ftp://ftp.rocksoft.com/clients/rocksoft

0 new messages