View Full Version : Generating the 10 bit CRC, for the 16 bit data 'block' (4 needed per frame) for

05-24-2010, 06:59 PM
I am building a low power FM stereo TX, with RDS functionality

(for getting an MP3 player to connect to a car stereo, with, in the longer term, the track name displayed on the car stereo display, but for the·initially, for testing·a static PS {program service} name " * IPOD * "!· !!)

I'm quite confused by Cyclic Redundancy Checks...· Something I really should learn more about!!!

Could someone help me out with the (spin) code to return the 10 bit CRC, for·a 16 bit data 'block' used within the Radio Data system.
{4 'blocks' are required to form a 'frame' of 104 bytes, to properly display the PS name}

I know pretty much everything else about the baseband coding structure, and also the modulation scheme, but I am stumped by the CRC part of this.... I know what the polynomial is, but I dont know if its byte reversed or not, and whether to set the default state of the CRC to $0000 or $ffff etc)

Any ideas??


05-24-2010, 09:30 PM
I would just try the 4 possible ways: LSB first or MSB first and init with all 0s or all 1s. If you have the polynome, calculating the XOR value for the CRC should be easy enough.

Look at what I found in the top rank of googling "rds crc": http://www.ee.ucl.ac.uk/~hamed/docs/final%20report.pdf
In there is a clear description on how to do the CRC on a microcontroller.
1. Load the register with zero bits.
2. Augment the message by appending W zero bits to the end of it.
while (more message bits)
3. Shift the register left by one bit, reading the next bit of the augmented message into register bit position 0.
if (a 1 bit popped out of the register during step 3)
register = register XOR Poly.

I think that's pretty clear, or is it?

long length
long my_crc
byte message[128] ' for example

PUB start
msg_data_base := @message ' set the address of the message buffer
msg_length_ptr := @length ' set the address of the message length long
crc_ptr := @my_crc ' result goes here
cognew(@crc10, 0)

PUB do_crc10(data,count)
bytemove(@message, data, count)
length := count
repeat while length <> 0
return my_crc

PUB do_crc10_16bit(value)
message[ 0] := value & 255
message[ 1] := value >> 8
length := 2
repeat while length <> 0
return my_crc

org 0
rdlong bytecount, msg_length_ptr wz ' wait for a message
if_z jmp #:wait ' with a non-zero length
mov msg_data_ptr, msg_data_base
mov crc, #0
rdbyte data, msg_data_ptr
mov bitcount, #8
:bits test data, #$80 wc ' get bit 7 of data into carry
rcl crc, #1 ' rotate into CRC
test crc, bit10 wz ' test if a 1 bit popped out
if_nz xor crc, poly
shl data, #1 ' next bit
djnz bitcount, #:bits
add msg_data_ptr, #1 ' next message byte
djnz bytecount, #:loop ' until message done
and crc, mask10 ' mask 10 bits
wrlong crc, crc_ptr
mov data, #0
wrlong data, msg_length_ptr ' set length to 0 to indicate we're done
jmp #:wait

poly long $05b9 ' taken from sample program
bit10 long 1<<10
mask10 long (1<<10)-1
msg_length_ptr long 0-0
msg_data_base long 0-0
crc_ptr long 0-0

crc res 1
bytecount res 1
bitcount res 1
msg_data_ptr res 1
data res 1

Pullmoll's Propeller Projects (http://pmbits.ath.cx/prop/)

Post Edited (pullmoll) : 5/24/2010 3:03:57 PM GMT

05-25-2010, 03:20 AM
You can caculate the CRC10 value in Spin, too. Thanks to pullmoll's description and demo code I was able to easily translate to Spin:

pub crc10(pntr, n) | tmpcrc, dbyte


repeat (n #> 0)
dbyte := byte[pntr++]
repeat 8
tmpcrc := (tmpcrc << 1) | ((dbyte & $80) >> 7)
if (tmpcrc & |<10)
tmpcrc ^= POLY
dbyte <<= 1

return tmpcrc & $03FF

I tested this routine against pullmoll's assembly version and got the same result from each test. That said... you'll pay the price in speed. I measured the number of clock ticks like this:

' check JM's version

t0 := cnt
c := crc10(@Test2, 10)
tdelta := (||(cnt - t0) - 368) #> 0

... and found that the Spin version took 313_520 clock ticks (3.9ms), while the assembly version took only 6_032 (75.4us) for the same, 10-byte packet. You may need to take this into consideration, depending on the size of your message packets.

Jon McPhalen
Hollywood, CA

05-26-2010, 06:57 PM
Thanks, it has taken a lot of the mystery/confusion out of CRC generation...· although, I allways though it was a 'black art'!!!

Initially, the SPIN version will be fast enough (Well, at least until I start using a dynamic PS name, later on ...), because·in reality, you only need·to calculate the CRC once for each of the four 'blocks' that makes up the entire data frame.

.... This is·assuming the message data doesnt change (it wont if I stick to a static PS name for initial testing, for·now..)

However, when I start using dynamic (scrolling PS names) at some later time, I will·possibly either use·the ASM code, or use 'double buffered' data (which I only update when I'm ready), and stick to the slower SPIN crc10 method

Because the packet size per block, is allways a fixed width 16 bit message, I've trimmed out the code for counting through multiple pointer referenced message bytes, giving me this even simpler code, for initial testing with static PS name:-

pub crc10(blockdata) | tmpcrc
· tmpcrc~

· 'Radio Data System CRC Polynomial G(x) = x^10 + x^8 + x^7 + x^5 + x^4 + x^3 + 1
· 'Polynomial value =· $05b9

· 'Block data is fixed width, 16 bits, so have dispensed with counting through multiple pointer referenced bytes...

·· repeat 16 '16·bits fixed width packet
···· tmpcrc := (tmpcrc << 1) | ((blockdata & $8000) >> 15)
···· if (tmpcrc & |<10)
······ tmpcrc ^= $05b9
···· blockdata <<= 1

· return tmpcrc & $03FF

regards M.R.B.

B.T.W. Sorry for the confusion, there was a typo in my original post, it should have read:-

{4 'blocks' are required to form a 'frame' of 104 bits, to properly display the PS name}