|
|
View previous topic :: View next topic |
Author |
Message |
simat
Joined: 05 Feb 2008 Posts: 7
|
Dallas CRC 16 for DS2423 1-wire counter |
Posted: Tue Feb 05, 2008 7:12 am |
|
|
Hi,
I've found a small bug in a 16 bit CRC routine that can be used with the Dallas DS2423 - this code was originally found at http://www.piclist.com/techref/microchip/crc16ca.htm after many attempts to find a Dallas 16 bit version at this site.
The code works most of the time, it's supposed to produce a result of 0xB001h, but for some reason it can also produce 0x7000h - this may be something to do with parity ? CRC16 (2 bytes) is accessed directly to give an accumulated result.
Code: | void docrc16(int8 newbyte)
{
int crc_lo;
int crc_hi;
#byte STATUS = 0x03
#byte crc_lo = 0x21 // Lower Byte of CRC16
#byte crc_hi = 0x22 // Upper Byte of CRC16
#ASM
movf newbyte,W ;W = input
xorwf crc_lo,W ;W = input XOR old crc_lo
xorwf crc_hi,W ;Swap old crc_hi with W
xorwf crc_hi,F ;
xorwf crc_hi,W ;new crc_hi = input XOR old crc_lo
movwf crc_lo ;new crc_lo = old crc_hi
movf crc_hi,W ;Save crc_hi in W
swapf crc_hi,F ;Trade nibbles
xorwf crc_hi,F ;XOR high half byte with low
rrf crc_hi,F ;Initialize Carry
btfsc crc_hi,0
incf STATUS,F ;Compliment carry
btfsc crc_hi,1
incf STATUS,F ;Compliment carry
btfsc crc_hi,2
incf STATUS,F ;Compliment carry
movwf crc_hi ;Restore crc_hi from W
movlw 0x01
btfsc STATUS,0 ; If carry
xorwf crc_lo,F ; flip bit 0 of crc_lo
movlw 0x40
rrf crc_hi,F ; shift parity into crc_hi
btfsc STATUS,0 ; if shift out is one
xorwf crc_lo,F ; flip bit 6 of crc_lo
rlf crc_hi,W ; unshift crc_hi into W
xorwf crc_hi,F ; combine them
rrf crc_hi,F ; shift parity back into crc_hi
movlw 0x80
btfsc STATUS,0 ; if shift out is one
xorwf crc_lo,F ; flip bit 7 of crc_lo
#ENDASM
}
my less efficient C version works fine all the time..
void docrc16(int8 newbyte) {
int16 nbyte;
nbyte = (newbyte ^ (crc16 & 0xff)) & 0xff; // Xor and return lower byte
crc16 >>= 8; // Right shift 8 for upper byte
if ((odd_parity(nbyte)) == 1) crc16 ^= 0xC001;
(nbyte *= 0x40) & 0xffff;
crc16 ^= nbyte;
(nbyte *= 0x02) & 0xffff;
crc16 ^= nbyte;
}
int8 odd_parity(int8 testbyte) { // get 8 bit parity - 0xff no ,0x100 yes
int8 n;
int8 i;
//printf("Odd parity called \n\r");
n = 0;
for (i = 0 ; i < 8 ; i++) {
n += testbyte & 0x01;
testbyte = (testbyte & 0xfe) / 2 ;
}
return (n & 1);
} |
The input sequence is -
Start Value, Input Byte, Result of Assembly CRC16 routine
CRC16= 0000, newbyte= A5 CRC16= BBC1
CRC16= BBC1, newbyte= DF CRC16= C83A
CRC16= C83A, newbyte= 01 CRC16= 1388
CRC16= 1388, newbyte= 04 CRC16= 6513
CRC16= 6513, newbyte= BF CRC16= 7D65
CRC16= 7D65, newbyte= 2A CRC16= F43C
CRC16= F43C, newbyte= 2D CRC16= 0C34
CRC16= 0C34, newbyte= 04 CRC16= D40D
CRC16= D40D, newbyte= 00 CRC16= C515
CRC16= C515, newbyte= 00 CRC16= 0F05
CRC16= 0F05, newbyte= 00 CRC16= C3CE
CRC16= C3CE, newbyte= 00 CRC16= 5443
CRC16= 5443, newbyte= BC CRC16= 4014
CRC16= 4014, newbyte= AB CRC16= 7000
Result COUNTER= 042D2ABF CRC= 7000 - incorrect (should be 0xB001h)
Start Value, Input Byte, Result of C CRC16 routine
CRC16= 0000, newbyte= A5 CRC16= 7BC0
CRC16= 7BC0, newbyte= DF CRC16= C83A
CRC16= C83A, newbyte= 01 CRC16= D389
CRC16= D389, newbyte= 04 CRC16= 6513
CRC16= 6513, newbyte= BF CRC16= 7D65
CRC16= 7D65, newbyte= 2A CRC16= F43C
CRC16= F43C, newbyte= 2D CRC16= 0C34
CRC16= 0C34, newbyte= 04 CRC16= 140C
CRC16= 140C, newbyte= 00 CRC16= 0514
CRC16= 0514, newbyte= 00 CRC16= 0F05
CRC16= 0F05, newbyte= 00 CRC16= 03CF
CRC16= 03CF, newbyte= 00 CRC16= 5443
CRC16= 5443, newbyte= BC CRC16= 4014
CRC16= 4014, newbyte= AB CRC16= B001
COUNTER= 042D2ABF and CRC= B001 - correct
Anyone got a working CRC16 for Dallas chips or any idea where I'm going wrong?
Thanks
Simon.
Last edited by simat on Tue Feb 05, 2008 10:16 am; edited 2 times in total |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Feb 05, 2008 9:37 am |
|
|
First remark: please use the 'code' buttons when posting code, this will help to preserve the layout and makes for easier reading.
Code: | #byte crc_lo = 0x21 // Lower Byte of CRC16
#byte crc_hi = 0x22 // Upper Byte of CRC16 | This is dangerous code. From the CCS manual on the #byte command: Quote: | Warning: In both cases memory at x is not exclusive to this variable. Other
variables may be located at the same location. In fact when x is a variable, then id
and x share the same memory location. | The risk you are running here is that the compiler will assign another variable to the same memory locations as your checksum. The address you have chosen is low in memory and often used by the compiler as 'scratch' memory, i.e. temporary memory for function variables, etc. It would have been better to use the #locate command which will prevent the compiler from using these memory locations.
Looking at your code I don't understand why you want to assign the CRC code variables to a fixed memory address. Nowhere in your code you are directly accessing the data at the specified address 0x20 and 0x21. Better is to leave it to the compiler as to where to store the data. Remove these two #byte lines. It will make your code easier to read, saves a few bytes and likely will fix your bug. |
|
|
simat
Joined: 05 Feb 2008 Posts: 7
|
|
Posted: Tue Feb 05, 2008 10:15 am |
|
|
Hi ckielstra,
Thanks for the reply, i've tried the routine with the #byte lines removed and manually setting the crc_lo and crc_hi just for a test and still get the same output.
If CRC16 = 0 and I input 0xA5 into the CRC16 routine I should get 0x7BC0 out, instead I get 0xBBC1 and it's not the TV channel :-)
I was using the #byte code to access the upper and lower bytes of the 16 bit CRC16 number, the addresses were chosen from whatever CRC16 was assigned in the compiler. #locate seems to stop the C code from accessing it so making it useless for my purpose, anyway no big problem, just need this Dallas CRC to work.
At the moment I'm working on translations the following 8051 code into ASM for the 16F628A
Code: | CRC16:
PUSH ACC ;save this in case the caller needs
it
XRL A,CRCL
MOV CRCL,CRCH ;put the high byte of the crc in its
dest..
MOV CRCH,A ;save data xor low(crc) for later
MOV C,P
JNC CRC0
XRL CRCL,#001H
CRC0:
RRC A ;get the low bit in c
JNC CRC1
XRL CRCL,#040H
CRC1:
MOV C,ACC.7
XRL A,CRCH ;compute the results for bits P...U
RRC A ;shift them into place
MOV CRCH,A ;and save them
JNC CRC2
XRL CRCL,#080H
CRC2:
POP ACC ;and restore everything and return
RET
end
|
Thanks
Simon. |
|
|
simat
Joined: 05 Feb 2008 Posts: 7
|
|
Posted: Tue Feb 05, 2008 12:50 pm |
|
|
opted for the slightly slower version from Dallas Appnote 27
Code: | void docrc16 (int8 newbyte)
{
int crc_lo;
int crc_hi;
#byte STATUS = 0x03
#locate CNTR = 0xB8
#byte crc_lo = 0x21 // Lower Byte of CRC16
#byte crc_hi = 0x22 // Upper Byte of CRC16
#ASM
MOVLW 0x08 ; 8 bits
MOVWF CNTR
crc_get_bit:
RRF newbyte,F ; Bit in C
MOVF newbyte,W ; Value to W
BTFSC STATUS,0
GOTO crc_in_1
BTFSS crc_lo,0 ; Lowest bit set ?
GOTO crc_cont ; Go to count with C=0
BSF STATUS,0
GOTO crc_cont ; Go to count with C=1
crc_in_1:
BTFSC crc_lo,0 ; Lowest bit zero ?
BCF STATUS,0 ; If no, C=0 = complement
crc_cont:
BTFSS STATUS,0
GOTO crc_shift ; If C=0 only shift
BTFSC crc_hi,6 ; Complement 15th bit of CRC
GOTO crc1
BSF crc_hi,6 ; If clear, set
GOTO crc2
crc1:
BCF crc_hi,6 ; If set, clear
crc2:
BTFSC crc_lo,1 ; Complement 2nd bit of CRC
GOTO crc3
BSF crc_lo,1
GOTO crc_shift
crc3:
BCF crc_lo,1
crc_shift:
RRF crc_hi,F ; 16bit rotate
RRF crc_lo,F
MOVF newbyte,W
DECFSZ CNTR,F
GOTO crc_get_bit
#ENDASM
} |
this works.. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|