Log in

View Full Version : CRC Calculation - Need a little help



Steve43
- 10th March 2005, 16:33
Hi all,

I need to calculate a CCITT-16 reversed (0x8408) checksum in PBP but need a little assistance. I've uncovered an assembly routine that would seem to do what I need ( see http://www.dattalo.com/technical/software/pic/crc.php ) but I'm not very familiar with the integration of assembly routines into a PBP program and am hoping someone can help me out a little here.

What I need to do is send an array of bytes to this routine and come up with a two-byte checksum as a result. An example would be to send the bytes 'AA 05 01' to the routine and have the checksum 'D5 50' returned. Problem is that I don't know how to pass the variables back and forth between the BASIC program and the assembly routine, etc.

Sorry if this is a bit elementary... (or perhaps it isn't..?)

Anyway, would greatly appreciate any help...

Thanks,
Steve

Bruce
- 11th March 2005, 00:49
Just place an _underscore before any BASIC variables you want to access in assembler,
and an _underscore in front of the assembler label you want to call from BASIC.

Give this a shot.


crc_hi VAR BYTE bank0
crc_lo VAR BYTE bank0
temp VAR BYTE bank0
ToCrc VAR BYTE bank0
CRC VAR WORD
X VAR BYTE
Space CON " "

Main:
crc_lo = 0
crc_hi = 0
ToCrc = $ff
CALL crc_8408

' Show HEX value
HSEROUT ["Input = ",IHEX2 ToCrc,Space,"Output = ",_
IHEX4 (crc_hi << 8 | crc_lo),13,10]

' Show ASCII values
FOR X = 2 to 8
ToCrc = X + "0" ' Convert X val to ASCII
CALL crc_8408
GOSUB Show
NEXT X
PAUSE 1000
GOTO Main

Show:
HSEROUT ["Input = ",DEC ToCrc-"0",Space,"Output = ",_
IHEX4 (crc_hi << 8 | crc_lo),13,10]
RETURN

ASM
_crc_8408
movf _ToCrc,W ;Load value into W
xorwf _crc_lo,W ;W = CD ^ ab ==> xy
movwf _temp ;temp = xy
swapf _temp,F ;W = yx
rrf _temp,w
andlw 0x78 ;y<<3
xorwf _crc_hi,W ;W = AB ^ (y<<3)
movwf _crc_lo ;LO = AB ^ (y<<3)
swapf _temp,W ;W = xy
xorwf _temp,W ;W = (y^x) | (x^y) = kk
movwf _crc_hi ;HI = kk
andlw 0x0f ;W = 0k
xorwf _crc_lo,W ;W = AB ^ (y<<3) ^ k
btfsc _crc_hi,0 ;
xorlw 0x80 ;W = AB ^ (y<<3) ^ k ^ (k<<7)
movwf _crc_lo ;LO = AB ^ (y<<3) ^ k ^ (k<<7)
swapf _temp,W ;W = xy
andlw 0xf0 ;W = x0
xorwf _crc_hi,F ;W = y<<4 | x^y
rrf _crc_hi,W ;W = (y<<4 | x^y) >> 1
andlw 7 ;W = x^y >> 1
swapf _crc_hi,F ;W = x^y<<4 | y
xorwf _crc_hi,F ;HI = kx ^ k>>1 = (k<<4) ^ y ^ (k>>1)
return
ENDASM

Just be aware of bank & page switching as your program grows.

Steve43
- 11th March 2005, 04:30
Bruce,

Thanks so much for your help! Your example is quite clear and I am able to execute it properly on my device.

One more question if you don't mind... the CRC calculation algorithm seems to want to be passed one byte at a time, but I need to calculate a checksum for a series of bytes (as a complete set, not individually) and I'm not sure how to accumulate the result of each one-byte cycle through the checksum routine.

As a real-world example, I need to be able to send the three bytes $AA $05 $01 into the routine and obtain the resultant accumulated checksum for all three bytes, which should produce a result of $D5 $50 (I am using a PC-based checksum program to determine what the proper output should be), but no matter what I do I can't seem to achieve this result.

I then need to append this checksum (with the high and low order bytes reversed) to the original three-byte command for a final result of $AA $05 $01 $50 $D5 to go out the serial port.

Once again, the help is really appreciated...

Bruce
- 11th March 2005, 15:00
The result is already cumulative. If you compare the results
to the assembly version in your link, you'll see the last version
I posted outputs the same values. crc_hi & crc_lo are only
cleared at the start - so they are cumulative until cleared.

If you change any value, the final result will also change since
crc_hi & crc_low hold the cumulative values from every byte
passed to the assembly routine.

To change the order of the final result, simply change the order
you send them out in. crc_hi holds the high byte. crc_lo holds the
low byte. You can send them both out serially in any order you
need.



crc_hi VAR BYTE bank0
crc_lo VAR BYTE bank0
temp VAR BYTE bank0
ToCrc VAR BYTE bank0
C_Vars VAR BYTE[3] ' Holds byte data passed to crc_8408
X VAR BYTE

Main:
crc_lo = 0
crc_hi = 0
FOR X = 0 TO 2
LOOKUP X,[$AA,$05,$01],ToCrc
C_Vars[X] = ToCrc ' Stash each value for Show output
CALL crc_8408
NEXT X
GOSUB Show
PAUSE 1000
GOTO Main

Show:
HSEROUT [IHEX2 C_Vars[0],IHEX2 C_Vars[1],_
IHEX2 C_Vars[2],IHEX crc_lo,IHEX2 crc_hi,13,10]
RETURN

ASM
_crc_8408
movf _ToCrc,W ;Load value into W
xorwf _crc_lo,W ;W = CD ^ ab ==> xy
movwf _temp ;temp = xy
swapf _temp,F ;W = yx
rrf _temp,w
andlw 0x78 ;y<<3
xorwf _crc_hi,W ;W = AB ^ (y<<3)
movwf _crc_lo ;LO = AB ^ (y<<3)
swapf _temp,W ;W = xy
xorwf _temp,W ;W = (y^x) | (x^y) = kk
movwf _crc_hi ;HI = kk
andlw 0x0f ;W = 0k
xorwf _crc_lo,W ;W = AB ^ (y<<3) ^ k
btfsc _crc_hi,0 ;
xorlw 0x80 ;W = AB ^ (y<<3) ^ k ^ (k<<7)
movwf _crc_lo ;LO = AB ^ (y<<3) ^ k ^ (k<<7)
swapf _temp,W ;W = xy
andlw 0xf0 ;W = x0
xorwf _crc_hi,F ;W = y<<4 | x^y
rrf _crc_hi,W ;W = (y<<4 | x^y) >> 1
andlw 7 ;W = x^y >> 1
swapf _crc_hi,F ;W = x^y<<4 | y
xorwf _crc_hi,F ;HI = kx ^ k>>1 = (k<<4) ^ y ^ (k>>1)
return
ENDASM

This doesn't return crc_hi = $D5 & crc_lo = $50 though. I get
crc_lo = $4F & crc_hi = $7F.

The above returns: $AA$05$01$7F$4F with your hi & lo bytes
swapped around.

If you change it, and plug-in the same values shown in the assembly
version, it returns eactly the same results as the assembly version
shows for each value passed to crc_8408.

Maybe there's a difference in your PC version & the PIC version?

Steve43
- 11th March 2005, 15:28
The result is already cumulative. If you compare the results to the assembly version in your link, you'll see the last version I posted outputs the same values. crc_hi & crc_lo are only cleared at the start - so they are cumulative until cleared.

Yes, that's what I thought by looking at it, but I guess I was thrown by the fact that the results weren't as expected. However, some more experimentation shows that the results aren't as expected even when calculating the CRC for a single byte, so it would indeed appear that the algorithm in the assembly routine above and the one used by the PC-based program are not identical (although they both are supposed to be CCITT-16 reversed (0x8408)).

I have the C source code for the algorithm used in the PC program. I will try to compare this against the PIC assembly routine and see where the difference might be, although unfortunately I'm no expert in either.

Thanks again for your assistance. This forum is an incredible resource thanks to you and all of the people who contribute.

Steve43
- 12th March 2005, 15:53
Well, I'm just not able to get anywhere with this. I have attached C source code for a CCITT-16 Reversed (0x8408) algorithm that gives me what I need, but unfortunately the PIC assembly routine I found doesn't provide the same output. I don't know if anyone is kind enough to take this on but I could really use the assistance. I'd be willing to buy a PIC CRC library to do this but I haven't been able to find such a thing.

Would someone like to take a stab at implementing this in PBP? It doesn't really have to be in assembly because speed isn't much of an issue in my application.

Thanks (much) again.

(Note: the attached file is in .RTF format to preserve the source code formatting but has an extension of .txt so I could attach it. Best to open it with Wordpad or other .RTF-aware application.)

Bruce
- 12th March 2005, 17:37
Here ya go..!


Poly con $8408 ' CRC polynomial
I VAR BYTE
pData VAR BYTE
X VAR BYTE
CRC VAR WORD
C_Data VAR WORD
VAL VAR BYTE[3] ' Holds 3 values passed to CRC routiine

VAL[0] = $AA
VAL[1] = $05
VAL[2] = $01

Begin:
CRC = $FFFF
for X = 0 TO 2
pData = VAL[X]
gosub Calc_CRC
next X
CRC = ~CRC
hserout [IHEX CRC.LowByte,IHEX CRC.HighByte,13,10]
pause 1000
goto Begin

Calc_CRC:
C_Data = $00FF & pData
CRC = CRC ^ C_Data
for I = 0 to 7
if CRC.Bit0 = 1 then
CRC = (CRC >> 1) ^ Poly
else
CRC = CRC >> 1
endif
next I
return

I'll let you figure out how to send your original data with the final result, but there's your C-to-BASIC
conversion. Much easier than the assembly routine, but still easier in C....;o]

Steve43
- 13th March 2005, 00:02
Works perfectly! Thanks Bruce, I once again find myself in your debt. Your solution is simple and elegant and is actually a better option for me than what I started out with (although I did learn something by playing with the assembly integration examples.)

Again, this forum is a great resource and thanks to you and the others who help the rest of us over the rough spots...

Bruce
- 13th March 2005, 01:23
You're very welcome. Glad I could help...;o]

I learn a lot here myself. Great resource.