Sida 1 av 1

8-bitars CRC-kod

Postat: 30 juni 2010, 04:09:03
av jesse
Ofta överför man ju data fram och tillbaks mellan kretsar och kort med olika metoder. t.ex SPI eller UART.
Flera av dessa metoder saknar ju någon form av felkoll, så jag har gjort en enkel rutin för att beräkna en 8-bitars crc-kod, för att använda vid dataöverföring.

funktionen används så här:

1) du sätter den globala variabeln crc till 0xff (eller annan valfri startkod, dock helst ej 0x00 - se nedan)
2) för varje byte du sänder så anropas funktionen med databyten som argument.
3) när sista byten är överförd skickar du crc. (man kan också välja att skicka t.ex. 0xff - crc eller liknande)

konstanten CRCPOLYNOM kan ha olika värden, men inte vilket värde som helst. Det finns regler för det. 0x07 motsvarar polynomet x8+x2+x+1 vilket förekommer inbyggt i en del kretsar, t.ex. AD-omvandlare med SPI-buss.

på mottagarsidan gör du exakt samma sak, fast med de mottagna byten. Du måste ha en bestämd längd på datapaketet, alternativ ange antalet bytes i början, så att du vet när du ska förvänta dig en crc.
när du tar emot crc-koden gör du en koll om den stämmer med din globala variabel crc. Jag brukar returnera skillnaden från mottagarrutinen så att jag får ett värde som skiljer sig från noll om det blivit fel.

Man kan faktiskt använda crc-koden för att göra enkel diagnostik: om alla data består av 0xff så kommer du att få samma kod (om du har ett fixt antal bytes), likaså för 0x00. Dessa situationer uppstår oftare än andra, t.ex. pga en oansluten enhet, strömbortfall, logisk felkoppling eller kapad kabel. Då kan man i mjukvaran skilja på dessa tillstånd och andra överföringsfel. Att man inte ska använda 0x00 som startvärde beror på att crc alltid kommer att bli 0x00 om alla data också är 0x00... det är ju inte så osannolikt att alla data blir 0x00 om man saknar ett pull-up motstånd t.ex. Då är det tråkigt om mjukvaran inte detekterar det felet.

Här kommer beräkningsfunktionen:

Kod: Markera allt

uint8_t crc; // global variabel

void crccalc(uint8_t data) { // 8 bitars CRC rutin
	// crc ändras för varje databyte in, startvärde ska vara 0.
	// argument: data = inkommen / skickad byte
	#define CRCPOLYNOM 0x07
	uint8_t i, bit;
	for (i=7;i<8;i--) {
		bit = ( crc ^ data );
		crc<<=1;
		data<<=1;
		if (bit & 0x80 )
			crc^= CRCPOLYNOM; 
	}
}

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 04:51:13
av blueint
Man bör nog använda minst CRC 16-bit för att det ska ge någon vettig garanti mot fel.

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 10:10:41
av vfr
Både ja och nej. Egentligen håller jag med. 16-bit is the shit. Å andra sidan så är 8-bit CRC mycket bättre än 8-bit summering eller XOR som man ofta ser. Dom reagerar ö.h.t inte på en nollbyte, vilket gör att man kan bara tappa bort en nollbyte utan att märka det i checksumman.

Jag har kört en del på alla varianterna och allting numera i mina protokolldefinitioner är 16-bitars CRC.

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 10:23:29
av jesse
16-bitars crc funktioner finns färdiga i biblioteken för AVR GCC tror jag. 8-bitars är väl en kompromiss egentligen, men som sagt bättre än checksumma. Det kan vara bra med 8-bitars crc om det är lite tidskritiskt.

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 20:47:04
av Marta
T.ex. DS18B20 temperatursensor och även andra s.k. one-wire-bus produkter därifrån använder 8-bit CRC. Dessvärre initierad till 0, av någon anledning. En finess med 0-initiering på denna är att crc stannar på just 0 när det är rätt om mottagaren räknar crc på alla inkomna bytes inklusive den avslutande crc. När meddelandet inte börjar med nulls så är det inget problem vad beträffar överföringsfel. Polynomet de använder brukar kallas DOW-CRC där bokstäverna lär stå för Dallas One Wire.

Skall det gå snabbt så är tabell-crc det bästa. I fallet 8-bit blir det då bara att göra EOR med den ackumulerade crc'n och sedan använda resultatet för att indexera i en tabell för att få nästa crc.

Det går att göra både crc16 och crc32 med tabell. Med 8-bit ingångsvärde är det alltid 256 platser i tabellen som då är 16 eller 32 bitar bred. Det behövs även lite EOR och annat för de bredare varianterna.

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 21:12:17
av mri
Vad vinner du på att skriva for-loopen som:

Kod: Markera allt

uint8_t i, bit;
for (i=7;i<8;i--) {
förutom att göra den svårläst?

Re: 8-bitars CRC-kod

Postat: 30 juni 2010, 23:44:02
av Icecap
Den är väl ganska tydlig tycker jag.

Den räknar ner till och med noll och när den overflower kommer värdet definitivt att vara större än 8.

Men det hade faktisk varit mycket mer "normalt" att läsa med
for(i = 0; i < 8; i++) och resultatet hade blivit det samma.

Re: 8-bitars CRC-kod

Postat: 1 juli 2010, 05:06:01
av jesse
haha, det tänkte jag inte på. Det var några månader sedan jag gjorde koden, och vi hade en diskussion på forumet om hur man optimerar snabbaste loopen i C eller hur det nu var. Detta måste varit en relik från då. Nu hade jag skrivit "som vanligt"... Tror inte maskinkoden blir bättre på något vis av denna formulering, det enda är väl som sagt att den blir svårläst. :)

Re: 8-bitars CRC-kod

Postat: 1 juli 2010, 07:32:32
av mri
Icecap: Loopen antyder att programmeraren förstått hur språket och variabler fungerar på en mer detaljerad och lägre nivå, dvs att en unsigned variabel vid 0 slår runt till ett stort positivt tal, osv.
Men, om man inte har väldigt goda skäl att göra på det här sättet skall man definitivt hålla sig till "standardsättet" för att göra koden lättläst för andra och inte minst för sig själv! I det här fallet vill man ju bara loopa 8 gånger.
Tycker man om sådana här udda sätt att skriva i övrigt enkla saker på har man nog levt i sin egen lilla bubbla för länge. :)

Re: 8-bitars CRC-kod

Postat: 1 juli 2010, 09:26:01
av jesse
Det visade sig att jag råkade kopiera en testversion istället för originalet, där jag hade experimenterat lite med hur det kompilerades och hur det betedde sig vid olika formuleringar. Sedan har det senaste exemplet helt enkelt legat kvar och jag råkade leta upp den versionen när jag skrev inlägget. Verkar ju annars lite vansinnigt att skriva så.... :roll:

Re: 8-bitars CRC-kod

Postat: 5 juli 2010, 21:04:01
av Mr M
mri skrev:Vad vinner du på att skriva for-loopen som:

Kod: Markera allt

uint8_t i, bit;
for (i=7;i<8;i--) {
förutom att göra den svårläst?
For-loopen körs inte eftersom variabel i redan är mindre än 8. :lol:

Re: 8-bitars CRC-kod

Postat: 5 juli 2010, 21:58:13
av snigelen
Jodå. Prova så får du se. Den körs så länge i är mindre än 8. Dvs ända tills i blir 0-1=255 (för uint8_t).

Re: 8-bitars CRC-kod

Postat: 6 juli 2010, 12:53:45
av jesse
jag tror nog att for-loopen körs, eftersom den används i flera av mina applikationer, och gör rätt.