Forum: Mikrocontroller und Digitale Elektronik CRC 16 nach CCiTT mit ARM und Linux


von Björn N. (bniethammer)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich muss leider noch einmal einen CRC Beitrag aufmachen. Nachdem ich nun 
das gesamte Wochenende gesucht und gelesen habe komme ich trotzdem nicht 
darauf was in meiner Berechnung falsch ist.

Und zwar soll für eine serielle Kommunikation ein CRC16 nach CCiTT 
berechnet werden und den Daten vorweg gestellt werden. Um die CRC zu 
berechnen nutze ich eine Tabelle. Wenn ich nun genau ein ASCII Zeichen 
an meine CRC Funktion übergebe dann wird der CRC korrekt berechnet. Also 
es kommt das raus was auch bei dem Rechner von "Breitbandkatze" 
rauskommt. Wenn ich nun aber 2 oder mehr Zeichen in die Berechnug 
einfüge dann kommt nur Mist raus. Vor allem das Ergebnis ist nicht ein 4 
Byte CRC sondern ein 5 Byte. Was mich doch sehr verwundert. Denn 
spätestens in der sprintf() Funktion sollte das ja auf 4 Zeichen gekappt 
werden.

Anbei ein kleiner Auszug meines Quellecodes. Das ganze senden und 
empfangen über die UART hab ich mal ausgelassen. Und alles am Ende steht 
eigentlich in einer Header Datei crc.h die in der main.c eingebunden 
wird.

Vielen Dank schomal für Eure Mühe. Wird bestimmt nur ein kleiner Fehler 
sein, aber an denen hockt man ja bekanntlich am längsten.

Gruß Björn Niethammer

Hab gerade moch gesehen das ich in der angehängten Datei nur ein Zeichen 
mit dem CRC berechne. Wenn ich also den CRC über alle Zeichen will würde 
cih folgenden Code einfügen:

crc = updcrc(crc, dest[0]);
crc = updcrc(crc, dest[1]);
crc = updcrc(crc, quelle[0]);
crc = updcrc(crc, quelle[1]);
crc = updcrc(crc, befehl[0]);

von Hegy (Gast)


Lesenswert?

Warum nicht erst den String zusammennageln und zum Schluß den CRC 
berechnen? z.b. so:
1
int main(void)
2
{
3
  // Variablen
4
  // Initialisierung
5
6
  // String zusammenbaun
7
  memset(word_out, 0 , 255);
8
  sprintf(&word_out[2], start, 1); // word_out[0] und ...[1] reserviert f. CRC
9
  strncat(&word_out[2], crc_char, 4); // crc_char ist nicht initialisiert!
10
  strncat(&word_out[2], dest, 2);
11
  strncat(&word_out[2], quelle, 2);
12
  strncat(&word_out[2], befehl, 1);
13
  strncat(&word_out[2], ende, 1);
14
15
  crc = updcrc(&word_out[2], 11); // 11 = Länge des Strings ohne CRC
16
  word_out[0] = crc >> 8; // Hi-Byte
17
  word_out[1] = (char)crc; // Lo-Byte
18
  ....
19
}
20
21
int updcrc(const char *zeichen, int length)
22
{
23
  int i;
24
  unsigned char temp;
25
26
  for(i = 0; i < length; i++)
27
  {
28
    temp = (crc >> 8) ^ zeichen[2+i];
29
    crc  = (crc << 8) ^ crctab[temp];
30
  }
31
  
32
  return(crc);
33
}

Sowas ungefähr. Verstehense?

von Björn N. (bniethammer)


Lesenswert?

Hallo Hegy!

Klar hab ich auch schon daran gedacht den String erst zusammen zu 
kopieren und dann den CRC zu berechnen. Das Protokoll sieht aber vor den 
CRC ohne Start und Stop Value zu berechenen. Ich müsste dann also erst 
alle Info Values zusammenkopieren. Dann CRC berechnen lassen und dann 
das Start Value vorweg zu stellen und den Stop Value ans Ende kopieren. 
Wenn ich dies mache kommt aber der gleiche falsche CRC Wert raus. Der 
Fehler liegt also woanders. Ich denke mal das irgendwo ein 
Pufferüberlauf stattfindet oder aber die Tabelle falsch ist bzw. falsch 
ausgelesen wird. Nur leider finde ich nirgends einen solchen Fehler. 
Deswegen hab ich mich damit ans Forum hier gewandt.

Gruß Björn Niethammer

von Freizeitbastler (Gast)


Lesenswert?

Hallo Björn

nimm doch zum Vergleich einen Algorithmus, der nur Bitoperationen macht 
und keine Tabelle benutzt. Bei nichttrivialen Rechnungen ist es zur 
Validierung immer gut, mehrere Algorithmen nebeneinander zu halten.

Schöne Grüße, Peter

von Hegy (Gast)


Lesenswert?

So ganz gerafft habbich das nicht wirklich. Ich vermute mal, du willst 
sowas: Am Anfang war NICHTS.
Dann war da ein Byte, aus dem wurde eine CRC berechnet.
Dann kam noch ein Byte und wieder wurde mit dem schon vorhandenem Daten 
eine CRC berechnet, wobei der alte CRC-Wert überschrieben wurde.
Sowas rekursives eben.

1) CRC aus (A)
2) CRC aus (CRC von 1) + (A) + (B)
3) CRC aus (CRC aus 2) + (A) + (B) + (C)
...

Der vorangegangene CRC-Wert wird also immer mitgeschliffen zur 
Berechnung?

Außerdem habe ich damals meine CRC-Berechnung nicht mit Tabelle wie du 
sondern mit Bitoperation in C-Funktion realisiert. Funxoniert 1A.

Außerdem spielt im Gegensatz zu anderen Checksummen-Algos die Position 
der einezelnen Bytes bei CRC eine Rolle.
Also bekommst du zwei verschiedene Chksum bei diesen Dingern raus
0x58 0xE3 0x08
0x58 0x08 0xE3

So, muß wech....

von Björn N. (bniethammer)


Lesenswert?

Guten Morgen!

Genau sowas rekursives soll es werden. Laut Protokoll werden über alle 
Bits zwischen dem Startzeichen und dem Endezeichen eine CRC16 nach CCiTT 
gemacht und den Info-Bits vorneweg gestellt.

Ich hatte auch schon alles am laufen. Allerdings auf einem kleinen 
ATmega128 Prozessor. Leider hat sich nur im laufe des Projekts 
herausgestellt das der mit allem doch ein wenig überfordert ist und ich 
etwas viel größeres brauche. Also hab ich ein ARM Entwicklungsboard 
bestellt und entwickel nun darauf. Dort läuft ein kleines Minilinux 
drauf. Nun hab ich meinen Code vom ATmega genommen die UART Geschichten 
angepasst und compiliert. Die UART kann er auch richtig ansprechen. 
Allerdings berechnet er jetzt die CRC falsch. Auf dem kleinen ATmega hat 
er sie richtig berechnet. Wenn ich nun den CRC per Hand einfüge also 
einfach crc = <wert> eingebe dann klappt auch alles Problemlos. Nur soll 
er ja nicht nur für genau eine Info alles machen sondern die Info soll 
Variabel sein. Deswegen muss er den CRC schon selbst berechen.

Mein Gedanke ist, dass er mit dem neunen Board falsch rechnet, weil 
irgendeine Variable einen anderen Wertebereich hat wie auf dem ATmega. 
Nur welche???

Hab gestern nochmal so einiges ausprobiert, bin aber immer zu einem 
falschen CRC Wert gekommen. Bastel gerad auch noch an ein paar anderen 
Baustellen rum. Deswegen dauerts manchmal bis ich hier wieder 
reinschaue.

Als Entwicklungsboard habe ich jetzt das Digi ConnectCore 9P. Die 
Routine zur Berechnung des CRC habe ich aus der Doku des Protokolls 
welches in programmieren will. Da sie ja auch auf dem ATmega schon mal 
lief denke ich nicht das darin der Fehler zu suchen ist. Aber sicher bin 
ich mir natürlich nicht.

Vielleicht hat ja jemand Erfahrungen mit dem ATmega und ARM Entwicklung 
und weiß wodrinn die Unterschiede liegen.

Gruß Björn Niethammer

von Freizeitbastler (Gast)


Lesenswert?

Hallo Björn,

ach so ist das. Mit Deiner neuen Plattform hat sich der int auf 32 Bit 
vergrößert. Nimm uint16_t um den alten Code für die CRC-16 weiter zu 
nutzen.

Schöne Grüße, Peter

von Björn N. (bniethammer)


Lesenswert?

Vielen, vielen Danke an alle die mitgeholfen haben!

Es klappt nun endlich. War wie "Freizeitbastler" schon sagte die Größe 
von den integer Variablen. Auf dem kleine ATmega nur 16 bit breit und 
nun auf dem ARM Board 32 bit breit. Hab alle integer zu short gewandelt 
und nun rechnet er auch wieder "richtig".

Der vorsatz mit uint16_t hat leider nicht geklappt. Den kennt mein 
Compiler so nicht wohl. Aber ich konnte es ja trotzdem lösen.

Gruß Björn Niethammer

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.