Hur räkna ut CRC-8 för i 1-Wire?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

Hej,

Har skrivit en rutin i C för PIC som letar efter enheter på bussen och detekterar deras ROM kod (ROM SEARCH). Funger så vitt jag kan se, får rätt familjekod i alla fall och mitt 1-Wire "bibliotek" klarar att välja upplösning och att läsa av temperaturen från en 18B20 så på HW nivå fungerar det.

Men jag sliter mitt hår i försöket att få CRC check att fungera på ROM koden.
Jag lagrar ROM koderna efter varandra i en byte array med familjekoden först.

crc1=crc2=0 innan det startar, codes är min byte array, i är från en yttre loop som pekar ut vilken 8 byte sekvens som ska räknas på (value 1,2,3...,n)

Kod: Markera allt

					for (j=0;j<64;j++) {
						crc2=(((crc1&0x01)^((codes[((i-1)*8)+(j>>3)]>>(j&0x07))&0x01))<<4)^crc2;
						crc2=(((crc1&0x01)^((codes[((i-1)*8)+(j>>3)]>>(j&0x07))&0x01))<<3)^crc2;
						crc2>>1;
						crc2=(((crc1&0x01)^((codes[((i-1)*8)+(j>>3)]>>(j&0x07))&0x01))<<7)|crc2;
						crc1=crc2;
					}

Är det någon som skrivit en CRC-8 check i C för data lagrad i byte sträng?
Användarvisningsbild
AndLi
Inlägg: 18282
Blev medlem: 11 februari 2004, 18:17:59
Ort: Knivsta
Kontakt:

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av AndLi »

JAg har inte skrivt, men har hittat en kodsnutt på nätet, här är delarna som är intressant för dig..

Kod: Markera allt

/******************************************************************************\
|* One-wire library code
|*
|* Ported from the Atmel application note AVR318 and accompanying source code
|* to the GCC compiler (the appnote uses IAR)
|*
\******************************************************************************/

/******************************************************************************\
|* Compute an 8-bit CRC 
|*
|* Pass a seed of 0 to start the CRC of a byte-stream. Pass the result as 
|* the seed for subsequent bytes
\******************************************************************************/
unsigned char oneWireCrc8(unsigned char inData, unsigned char seed)
	{
    unsigned char bitsLeft;
    unsigned char temp;

    for (bitsLeft = 8; bitsLeft > 0; bitsLeft--)
    	{
        temp = ((seed ^ inData) & 0x01);
        if (temp == 0)
            seed >>= 1;
        else
        	{
            seed ^= 0x18;
            seed >>= 1;
            seed |= 0x80;
        	}
        inData >>= 1;
    	}
    return seed;    
	}
	
/******************************************************************************\
|* Compute a 16-bit CRC 
|*
|* Pass a seed of 0 to start the CRC of a byte-stream. Pass the result as 
|* the seed for subsequent bytes
\******************************************************************************/
unsigned int oneWireCrc16(unsigned char inData, unsigned int seed)
	{
    unsigned char bitsLeft;
    unsigned char temp;

    for (bitsLeft = 8; bitsLeft > 0; bitsLeft--)
    	{
        temp = ((seed ^ inData) & 0x01);
        if (temp == 0)
            seed >>= 1;
        else
        	{
            seed ^= 0x4002;
            seed >>= 1;
            seed |= 0x8000;
        	}
        inData >>= 1;
    	}
    return seed;    
	}
	
/******************************************************************************\
|* Compute and check the CRC for a ROM identifier 
\******************************************************************************/
unsigned char oneWireRomCrc(unsigned char * romValue)
	{
    unsigned char i;
    unsigned char crc8 = 0;
    
    for (i = 0; i < 7; i++)
    	{
        crc8 = oneWireCrc8(*romValue, crc8);
        romValue++;
    	}
    if (crc8 == (*romValue))
    	{
        return ONEWIRE_OK;
    	}
    return ONEWIRE_ERROR;
	}
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

Tack!

Den fungerade men jag har spöken i koden (minnet är nästan fullt)...

Använder jag read ROM så blir det rätt och CRC räknas rätt... men med Search ROM så lagras fel värde på LSB i alla ROM code bytes (läser rätt på porten), LSB blir alltid 0 i alla byte... Ska skriva om den helt så får vi se...
snigelen
Inlägg: 815
Blev medlem: 8 maj 2009, 11:02:14
Ort: Lund

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av snigelen »

Det där känner jag igen. Det är inte så att du räknar bit-nummer från 1 till och med 64 som de gör i app-noten (i stället för 0-63)? Så man kan ju råka skriva

Kod: Markera allt

    while(bit_number < 64)
av gammal vana i stället för

Kod: Markera allt

while(bit_number <= 64)
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av sodjan »

Blev det där rätt ?
snigelen
Inlägg: 815
Blev medlem: 8 maj 2009, 11:02:14
Ort: Lund

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av snigelen »

Det tror jag. Möjligen dåligt formulerat. Normalt i c räknar man N element från noll till N-1 eftersom arrayer är indexerade så.

När man söker en entrådsbuss kan man tänka sig att sökrutinen returnerar index till det bit-nummer där man har den senaste differensen och returnera noll om det inte var någon skillnad (dvs man har hittat sista enheten). I så fall är det kanske naturligare att räkna bit-nummer från 1 till 64. Om man då slutar vid 63 så kommer man att missa sista biten.
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

@snigelen: Tack för förslaget, men nej, det sitter inte där för jag får LSB = 0 i alla åtta byte men resten av byten blir rätt. Var först inne på att använda noll som exit vilkor men landade på en done flagga som i appnoten i stället.

Kod: Markera allt

unsigned char bitIndex=0;	//bit pointer
    while (bitIndex < 64) {	//loop all 64 bits
	a = OW_getbit();			//read bit
	b = OW_getbit();			//read bit complement
.
.
.
	bitIndex++;
    }
Men jag har gjort liknande komplexa konstruktioner som i den icke fungerande CRC beräkningen.

Kod: Markera allt

//mask out a bit in code pointed to by BitIndex//
#define Bit (*(code+(bitIndex>>3))&(0x01<<(bitIndex&0x07)))

//set the bit as a//
#define SetBit *(code+(bitIndex>>3))=(*(code+(bitIndex>>3))&(~(0x01<<(bitIndex&0x07))))|(a<<(bitIndex&0x07))
Ja, det är svårt att se vad den gör, men den gör rätt på 7 av 8 bitar :humm:

bitIndex räknar från 0b000000 till 0b111111 (0 till 63)
bitIndex>>3 ger 0b000 till 0b111 som adderas till code (pointer to char) för att peka ut rätt byte
bitIndex&0x07 ger 0x0 till 0x7 som används för att skifta 0x01 uppåt som efter ~ ger bitmasken för byten
Samma sak med a som skiftas uppåt och genom | läggs till byten

hittar inte felet men ibland gör man inte det utan måste skriva om det (optimerande kompilatorer kan man försöka skylla på :wink: )

Såg nu att bitmasken inte behövs då a bara kan vara 0x00 eller 0x01 så {"byte" | a<<(bitIndex&0x07)} ger samma resultat då "byte" är 0x00 vid start.
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

Något går snett i kompileringen (eller blir i alla fall olika) för om jag har

Kod: Markera allt

*(code+(bitIndex/8))=(*(code+(bitIndex/8))|(a<<(bitIndex&0x07)))
eller

Kod: Markera allt

*(code+(bitIndex>>3))=(*(code+(bitIndex>>3))|(a<<(bitIndex&0x07)))
Så får jag olika resultat och mig veterligen är >>3 samma sak som /8 ...
Precis som %8 är samma sak som &0x07

Ska pröva att bryta isär bitIndex i en bit och en byte räknare så jag slipper dessa skiftningar.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av sodjan »

En sak som kan röra till det ibland är skillnaden mellan
"logical shift" och "arithmetic shift". Det blir lite olika beroende
på om variablen (t.ex bitIndex) är "signed" eller inte.

Kanske att "/" hanterar det korrekt, men att ">>" gör lite
annorlunda (tar inte hänsyn till om/att variablen är "signed").
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

Helt rätt sodjan, men tyvärr sitter det nog inte i signed eller unsigned för erfarenheten har lärt mig att alltid skriva unsigned char (använder sällan char till aritmetik).
Däremot tror jag att kompilatorn implementerar det helt annorlunda och det festliga är att koden krymper med /8 jämfört med >>3. Det var inte vad jag förväntade mig. Så uppenbarligen ger C-koden annan maskinkod.

Misstänker att HI-TECH gratiskompilator inte är den "vassaste kniven i lådan"... :wink:

LSB är den bit som laddas in först per byte och om det i maskinkoden av misstag skiftas ett steg för långt och sedan tillbaka så "tappas" en bit i änden vilket då är LSB och den blir ofrivilligt 0.
Med tanke på att jag använder en så lång sammansatt sats så är det inte helt otroligt att kompilatorn tappar bort sig på vägen, det är min troligaste förklaring. Men jag knäcker nog inte detta utan omskrivning får bli lösningen.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av sodjan »

OK, ja *någon* skillnad är det ju uppenbarligen... :-)
Om man vill gräva lite djupare i det så kan man ju fixa
ett par små testcase och studera output från kompilatorn.
Alltså assemblerlistningen.

Eftersom "code+(bitIndex/8)" används på två ställen i uttrycken så
behöver inte en omskrivning (flera separata rader där just den delen
enbart utförs en gång) nödvändigtsvis behöva vara till en nackdel.
RoPa
Inlägg: 630
Blev medlem: 13 januari 2006, 10:32:06

Re: Hur räkna ut CRC-8 för i 1-Wire?

Inlägg av RoPa »

Så sant sodjan, optimalt är sällan optimalt :(
Av någon anledning fick jag för mig att använda en #define sats för att få det "kortare" i själva program loopen.
Där av att inga mellanslag får användas och att alla uttryck måste skrivas ut varje gång de behövs, #tydlig_not

Nu fungerar det och det ser ut så här (del av koden, a är läst bit från bussen)

Kod: Markera allt

			if (a==1) {
				*code = (*code | byteBit);		// Set the bit as 1
			} else {
				*code = (*code & (~byteBit));	// Set the bit as 0
			}
			
			OW_putbit(a);						// Send the bit
			byteBit = byteBit << 1;
			if (byteBit == 0) {
				byteBit = 0x01;
				code++;
			}
Och vet ni vad??? nu blev resulterande koden flera hundra byte mindre... :oops:

Slutet gott allting gott :D
Skriv svar