Forum: HF, Funk und Felder AT86RF230 - Automatische CRC16 Erzeugung Fehler..


von Joan P. (joan)


Lesenswert?

Jau, gleich noch ein Problem hier :-(

Der RF230 bekommt Senderseitig die Aufgabe 4 Byte (0x99, 0xB1, 0x99, 
0xB1) mit dem automatischen CRC-16 zu versehen und dann alles zu 
verschicken (in Register PHY_TX_PWR das TX_AUTO_CRC_ON Bit setzen). Das 
Registerbit ist gesetzt, da ich dessen Status hinterher überprüfe, dh. 
die Nachrichten sollten mit CRC16 anhängend geschickt werden.
Das Generator-Polynom laut Datenblatt x^16 + x^12 + x^5 + 1 müsste nach 
meiner Rechnung 0xEE11 == '1000.1000.0001.0001' sein.
Im PHR-Feld hab ich vor dem Senden eine 6 drin stehen (4 byte Payload, 2 
byte CRC-Anhang, wie in Anleitung beschrieben).

Auf Empfängerseite kommt kein CRC_VALID, sobald der TRX_END Interrupt 
aufgetreten ist, deswegen hab ich den durch das CRC16 erzeugten Anhang 
ausgelesen und mit dem verglichen, was herauskommen sollte, wenn man die 
obigen Daten bei dieser Seite eingibt 
(http://www.flechtmann.net/crc/index.php).

Nachricht: 0x99B199B1 == '1001.1001.1011.0001.1001.1001.1011.0001'
Generatorpoly: 0xEE11 == '1000.1000.0001.0001'
=> CRC16-Anhang: '111.0110.0100.0101'

Der Empfänger bringt mir aber folgenden Anhang:
'0111.0110.0100.0101'

Dh. wenn man den Anhang um 1 Bit nach links schiebt stimmen nur die 
ersten 5 und die letzen 3 Bit überein.. die 7 Bits dazwischen sind 
murks..

Hilfe bitte?!

Grüsse und Danke fürs lesen

von Joan P. (joan)


Lesenswert?

Oh oh.. seh grad.. das wurde mir schon mal beantwortet von Jörg..

Rev. A RF230 Chips können das nicht, nur die Rev. B ..

Beitrag "Re: AVR - SPI im MasterMode auch ohne Int-Flag erlaubt?"
"...Falls du noch einen AT86RF230 rev A hast, musst du ja ohnehin auch 
noch die CRC-16 mit berechnen beim Empfang, das kann man bequem parallel 
mit jedem neu gelesenene Byte ausführen, ohne dass man dadurch insgesamt 
langsamer
wird.  Beim rev B gibt's dann ein "CRC OK"-Bit, da muss man das nicht
mehr selbst machen.  Ob du einen rev A oder B in deinem ZigBit hast,
erfährst du aus dem Versionsregister."

Ok, hat sich damit erledigt.. selbst ist die Frau ;-)

von Joan P. (joan)


Lesenswert?

Kommando zurück.. ^^bin scheinbar recht verwirrt gewesen.

Den letzten Satz im Post drüber bitte wegdenken.

Also, da der Rev.A Chip das CRC nicht selber checkt, bzw das CRC_VALID 
Bit nicht setzt, muss ich ja selber rechnen. Dann bekomm ich aber ein 
Problem, weil das Ergebnis der Zu-Fuss-Rechnung nicht stimmt..
Na mal sehen was ich auf diesem Wege noch so entdecke. Erstmal noch ein 
bisschen rumbröseln.. Falls jemand inzwischen ne Idee hat, ich bin ganz 
Ohr :-)

Danke und Grüsse

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Kannst du bitte mal die genaue Bytereihenfolge aufschreiben, die
du versendest, einschließlich der CRC-Bytes?

Im Moment ist mir das alles etwas konfus...  Mir dünkt, dass du
darüber stolperst, dass die Bitreihenfolge bei 802.15.4 andersrum
ist als bei den meisten IEEE-802.x-Standards.

Für einen Frame mit der PSDU 0x99 0xb1 0x99 0xb1 errechne ich als
CRC 0xad75, d. h. der ganze Frame müsste 0x99 0xb1 0x99 0xb1 0x75 0xad
sein, damit die CRC-Prüfung ein OK ergibt.

Die Funktion _crc_ccitt_update() in der avr-libc ist übrigens geeignet,
die CRC-16 eines IEEE-802.15.4-Frames zu testen/ermitteln.  Den dort
in der Doku enthaltenen Pseudocode habe ich mal zu folgendem kleinen
Progrämmchen (das ich wohl Axels µracoli als "contributed software"
spendieren werde) umgefusselt:
1
#include <stdint.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <unistd.h>
5
6
uint16_t crc_ccitt_update (uint16_t crc, uint8_t data)
7
{
8
    data ^= crc & 0xff;
9
    data ^= data << 4;
10
11
    return ((((uint16_t)data << 8) | ((crc & 0xff00) >> 8))
12
            ^ (uint8_t)(data >> 4)
13
            ^ ((uint16_t)data << 3));
14
}
15
16
static void
17
usage(void)
18
{
19
    fprintf(stderr, "usage: gencrc [-b base] number...\n");
20
    exit(64);
21
}
22
23
int
24
main(int argc, char **argv)
25
{
26
    long i;
27
    int c, base;
28
    uint16_t crc = 0;
29
    uint8_t byte;
30
31
    base = 0;
32
    while ((c = getopt(argc, argv, "b:")) != -1)
33
        switch (c)
34
        {
35
        case 'b':
36
            base = strtol(optarg, 0, 0);
37
            break;
38
39
        default:
40
            usage();
41
        }
42
    argc -= optind;
43
    argv += optind;
44
45
    while (argc-- > 0) {
46
        i = strtol(argv[0], 0, base);
47
        byte = (uint8_t)i;
48
        crc = crc_ccitt_update(crc, byte);
49
        argv++;
50
    }
51
    printf("CRC = 0x%04x\n", crc);
52
    return 0;
53
}

Wenn du das mit einem kompletten gültigen Frame aufrufst, muss die
CRC als 0x0000 gemeldet werden.  Um das mal für das im IEEE
802.15.4-2006 genannte Beispiel zu zeigen:
1
./gencrc 0x02 0x00 0x6a 0xe4 0x79
2
CRC = 0x0000

oder eben auch:
1
./gencrc -b2 00000010 00000000 01101010 11100100 01111001
2
CRC = 0x0000

Man beachte die unterschiedliche Bitreihenfolge im letzten Beispiel
zwischen dem, wie wir die Bits für gewöhnlich in Zahlen schreiben
und der Notation im 802.15.4-Standard.

von Joan P. (Gast)


Lesenswert?

Bisher hab ich nur 2 Byte verschickt mit selbstgestrickter CRC4. Für 
Tests hab ich jetzt mal 0x13 und 0x36 genommen (1.0011.0011.0110), was 
mit meinem CRC4 (CRC-Poly = '1011') zu 0x99 0xB1 wird 
(1001.1001.1011.0001).
Im normalen Betrieb ändern sich die enthaltenen Nachrichten (laufende 
Nummer, Zustände, Sender-Empfänger-Kennung, usw..) natürlich andauernd, 
sind aber immer gleich lang.
Nun sind beim gleichzeitigen Betrieb mehrerer Module aber Probleme und 
Fehlinterpretationen aufgetreten, die mein CRC4 manchmal nicht 
rausgefiltert hat. Das darf aber nicht passieren.

Deswegen will ich jetzt 2x 2 byte schicken (immer noch CRC4 gesichert) 
da hab ich schon mal 2-fache Redundanz und dann über alles drüber (0x99 
0xB1 0x99 0xB1) das automatische CRC16 vom Chip. Da hoffe ich dann auf 
genügend Sicherheit.

Also das PHY_AUTO_CRC Bit setze ich wie gesagt bei der Initialisierung.
Die nachfolgende spi_write_frame(byte1,byte2)-Funktion macht nun dieses 
2x 2 byte versenden und erzeugt den Platz für den CRC16-Anhang (PHR=6).
Laut Datenblatt braucht man die 2 Byte für das FCS (CRC16-Anhang) nicht 
mit downloaden.. hab ich aber auch schon getestet. Keine Veränderung.
1
// 1 Frame mit 4 byte Payload + CRC in den Speicher des RF230 schreiben =============
2
static void spi_write_frame (uint16_t two_byte)
3
{
4
  PORTB &= ~(1<<PB0); // SEL low
5
  SPDR = 0x60; // SPI write Frame
6
    // do 17 clock-cycles nothing
7
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
8
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
9
  SPDR = 0x06; // SPI write PHR: 4 bytes Payload + CRC
10
    // do 17 clock-cycles nothing
11
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
12
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
13
  SPDR = (two_byte>>8); // SPI write 'first_byte'
14
    // do 17 clock-cycles nothing
15
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
16
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
17
  SPDR = two_byte; // SPI write 'second_byte'
18
    // do 17 clock-cycles nothing
19
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
20
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
21
  SPDR = (two_byte>>8); // SPI write 'first_byte'
22
    // do 17 clock-cycles nothing
23
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
24
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
25
  SPDR = two_byte; // SPI write 'second_byte'
26
    // do 17 clock-cycles nothing
27
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
28
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );  PORTB |= (1<<PB0); // SEL high
29
} // ======================================================================= */

Das ist wie geschrieben Senderseitig, weiter kann ich dort ja nicht 
reingucken.

Auf Empfängerseite hab ich dann wie oben angegegeben und wie in dem 
anderen Post dargestellt (der mit dem SRAM Zugriff) die Bytes 5&6 
ausgelesen (per normalem auslesen ;-) und angezeigt. Die sollten ja das 
CRC16-Anhängsel, das beim Senden automatisch erzeugt wurde enthalten 
(also 15Bit).. und bis auf ein paar Bits in der Mitte sieht das ja auch 
schon passend aus.

Ich werd morgen.. äh, heute mein ich, angesichts der Uhrzeit, aber erst 
in ein paar Stunden ;-).. mal deine Testnachricht und die aus dem 
Datenblatt verschicken und dann sehen was der Empfänger dazu meint.
Dann seh ich klarer wo MSB und LSB stecken und was da sonst noch so 
passiert.

Vielen Dank jedenfalls schon mal vorab für deine wie immer sehr 
hilfreichen Erläuterungen zum Thema! :)

PS: ich denke die CRC16 Berechnung von der Website macht etwas anderes 
als der Chip. Ich hab grad eben mal schnell das Beispielframe aus dem 
Datenblatt da drüber gejagt.. die Ergebnisse stimmen nicht überein. Muss 
ich aber nochmal nachgucken.. bin schon recht müde heut.

von Joan P. (Gast)


Lesenswert?

Ich seh grad.. in der Funktion oben^^ im letzten Post sind die beiden 
letzten bytes noch verdreht.. Das war bei meinen Tests nicht so. Ich bin 
hier an nem anderen Rechner und hab ne alte Version der Funktion 
genommen.

Also Reihenfolge wie zu erwarten:

CMD=write frame
PHR=6
byte1
byte2
byte1
byte2

Grüsse

von Joan P. (joan)


Lesenswert?

So, also die Berechnung auf der Seite 
(http://www.flechtmann.net/crc/index.php):
1
Nachricht: 100.0000 0000.0000 0101.0110
2
Generatorpolynom: x^16+x^12+x^5+x^0 = 1 0001.0000 0010.0001
3
=> CRC16-Anhang: 0010.0111 1001.1110
stimmt mit dem Beispiel auf S.48 im Datenblatt des RF230 überein :
1
"...Considering a 5 octet ACK frame. The MHR field consists of 0100.0000 0000.0000 0101.0110 .
2
The leftmost bit (b0) is transmitted first in time.
3
The FCS would be following 0010.0111 1001.1110 .
4
The leftmost bit (r0) is transmitted first in time...."

Ich hatte das im RF230 eingebaute CRC16 Generatorpolynom gestern falsch 
von Polynomalschreibweise nach binär übersetzt - Asche auf mein Haupt

Die Nachricht
1
0x40:0x00:0x56
müsste also immer den Anhang
1
0x27:0x9E
ergeben, wenn man mit dem CRC16-GEneratorpolynom (0x01:0x10:0x21) 
rangeht.

Wenn ich diese Test-Bytes nun durch den Senderchip (Rev.A) mit 
automatischer CRC16-Erzeugung gebe (also PHR=5 und Payload: 
0x40:0x00:0x56) dann kommt auf Empfängerseite folgendes an:
1
PHR:   0000.0101 = 0x05 ok
2
byte1: 0100.0000 = 0x40 ok
3
byte2: 0000.0000 = 0x00 ok
4
byte3: 0101.0110 = 0x56 ok
5
crc1:  1100.0101 = 0xC5 äh?
6
crc2:  0011.0001 = 0x31 äh?

Dh. Wirklichkeit und Datenblatt widersprechen sich hier bzw. ich bin zu 
blöd..
Ich denke der Rev.A Chip kann nur den CRC16-Check nicht durchführen, 
aber die automatische CRC16 Erzeugung beim Senden sollte doch klappen 
oder?
Ich hab leider noch kein Datenblatt für die Rev.A Version gefunden.. wo 
findet man die bei Atmel?


Dann weiter..

Wenn ich dein obiges 802.15.4 konformes Beispiel mit der Nachricht
1
0x02 0x00 0x6A
mit der Website berechne, komme ich auf
1
0xA3 0x8C bzw. 1010.0011 1000.1100
als CRC16 Anhang.

Deine Berechnung ergibt aber etwas ganz anderes, nämlich:
1
0xE4 0x79 bzw. 1110.0100 0111.1001

Das sieht mir nicht nur nach vertauschten oder invertierten Bits/bytes 
aus..


Ich werd mal weiter mit dem Zeugs rumspielen.. irgend eine Logik muss ja 
dahinter stecken.

von Joan P. (joan)


Lesenswert?

Ok, jetzt hab ichs wohl gefunden (rotieren) und verstehe damit auch 
deine Ausführungen, Jörg.. :-)

Die einzelnen zu sendenden Bytes des Frames werden rotiert MSB <-> LSB, 
dann wird das CRC16 darüber gejagt, was den Anhang erzeugt. Dieser wird 
dann wiederum rotiert MSB <-> LSB und das kommt dann im Empfänger an.

Wäre für mich jetzt noch interessant zu wissen, warum das so ist.. über 
SPI kommt doch das MSB immer zuerst in den Framebuffer?.. und da im 
Datenblatt des RF230 drin steht, dass der Upload-Pointer beim Senden 
minimal genau an der gleichen Stelle wie der Sende-Pointer sein darf, 
niemals kleiner, sonst gibts einen Sende-UnderRun-Interrupt, dachte ich, 
dass das MSB auch zuerst gesendet wird?..

Warum also wird zum berechnen des CRC16-Anhanges jedes Byte rotiert?

Hat das Vorteile bei der CRC-Rechnung, oder gibts prinzipielle Ursachen? 
Ich seh's noch nicht.. vielleicht stoß ich ja drauf, wenn ich das ganze 
nun rückwärts programmieren muss.
Umsonst macht so was sicherlich keiner, oder? :-)

Danke für alles und schönes WE noch!

PS: für den stillen Mitleser die Zusammenfassung:
1
zu sendende Nachricht: 0x99 0xB1 0x99 0xB1
2
Nachricht byteweise rotieren: 0x99 0x8D 0x99 0x8D (macht Chip)
3
CRC16 Generatorpoly: 0x01 0x10 0x21
4
ergibt Anhang: 0xAE 0xB5 (macht Chip)
5
byteweise rotieren: 0x75 0xAD (macht Chip)
6
empfangene Nachricht: 0x99 0xB1 0x99 0xB1 0x75 0xAD
Nun muss man im Empfänger die letzten beiden bytes (FCS) wieder 
rotieren, bevor man per Hand den CRC-Check macht.. dann sollte als Rest 
0 rauskommen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Die Bitreihenfolge im Standard ist einfach andersrum geschrieben, als
wir das normalerweise tun würden.  Der besagte ACK-Frame, der als
Beispiel für die FCS-Berechnung genannt ist, beginnt mit 01000000,
aber wenn du dir den Standard anguckst, steht auf der linken Seite ein
"b0" drunter: Bit Nummer 0.  Das heißt, das einzig gesetzte Bit in
diesem Octet ist Bit 1, folglich handelt es sich um die Zahl 0x02,
nicht 0x40.

Du musst gar nichts wirklich rotieren, du musst nur den
CRC-Algorithmus entsprechend anpassen (d. h. das Polynom selbst
rotieren).  Wie ich dir schon schrob, macht die avr-libc-Funktion
_crc_ccitt_update() es exakt so, wie du es für IEEE 802.15.4 brauchst.
Analog auf der PC-Seite mein obiges Programmschnipsel, das weiter
nichts ist, als die compilierbare Form des Pseudo-Codes, der in der
Doku zu dieser Funktion sowieso schon enthalten war.

Übrigens kannst du beim kontinuierlichen Einlesen des Frames die
Wartezeiten, bis das SPI-Interface wieder das nächste Byte
reingeschoben hat, sinnvoll mit dem Updaten der CRC verbringen.  Wenn
ich mich nicht verzählt habe, benötigt _crc_ccitt_update() 17
CPU-Takte, während das SPI minimal (d. h. höchste Taktrate und SPI2X
gesetzt) 16 CPU-Takte für das Reinschieben eines Bytes benötigt.
Damit hast du am Ende des Frames auch sofort die Entscheidung, ob die
CRC-16 OK ist.

von Joan P. (joan)


Lesenswert?

Danke für die Tipps.. ich versuche grad den _crc_ccitt_update 
Pseudo-Code zu verstehen.. wenigstens ein bisschen.
Die Vorgehensweise scheint ja hochoptimiert zu sein.. den ursprünglichen 
CRC-Algorythmus kann ich im Prinzip gar nicht mehr ausmachen, höchstens 
den Anfang und das Ende.. dazwischen scheint irgendwie komplett anders 
zu laufen und vor allem verdammt kurz. Wahnsinn.

Das hab ich aber richtig verstanden, dass das CRC-Polynom 
'x^16+x^12+x^5+1' da schon eingebaut ist, oder?
Jedenfalls bekomme ich bei sauberem Empfang am Ende 'crc == 0' raus, 
nachdem ich alle empfangenen Bytes da durchgeschickt habe..
Und eingebaute Fehler in einem der Bytes verursachen 'crc != 0' .. 
super.

Da müssen ja Mannjahre drin stecken in den paar Zeilen Code.. wenn ich 
mir meine 350 cycles CRC3 dagegen so angucke :-D
Was solls, bin halt kein Informatiker - hab nur 2 rechte Hände :-)

Also nochmal herzliches Dankeschön für deine Antworten!
Wie immer sehr hilfreich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joan P. wrote:

> Danke für die Tipps.. ich versuche grad den _crc_ccitt_update
> Pseudo-Code zu verstehen.. wenigstens ein bisschen.
> Die Vorgehensweise scheint ja hochoptimiert zu sein.. den ursprünglichen
> CRC-Algorythmus kann ich im Prinzip gar nicht mehr ausmachen, höchstens
> den Anfang und das Ende..

Tut mir leid, ich bin auch kein Informatiker.  Ich habe davon nur
verstanden, dass man sich ja letztlich nur um die Bits kümmern
muss, die beim Polynom 1 sind, da nur bei denen die XOR-Verknüpfung
einen Effekt hat.  Entsprechend ist die sonst notwendige Schleife
für diese Bits entrollt worden.

> Das hab ich aber richtig verstanden, dass das CRC-Polynom
> 'x^16+x^12+x^5+1' da schon eingebaut ist, oder?

Ja.

> Da müssen ja Mannjahre drin stecken in den paar Zeilen Code..

Ich denke, wenn man den ganzen Kram verstanden hat, hat man's in
einem Tag oder zweien geschrieben.  Andererseits ist der Vorteil von
CRCs, dass man den Algorithmus nicht unbedingt verstanden haben muss
und sie trotzdem benutzen kann. ;-)

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.