Forum: Mikrocontroller und Digitale Elektronik CRC Einheit im STM32 -> CRC32 lässt sich nicht bestätigten


von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

Moin!

Ich versuche seit Freitag Vormittag die CRC Peripherie des STM32F103C8T6 
ans laufen zu bekommen, wobei ans laufen zu bekommen ist nicht ganz 
richtig. Laufen tut sie schon jedoch kann ich das Ergebnis welches mir 
die CRC-Engine liefert nicht bestätigten.

Habe mich ein bisschen mit CRC auseinander gesetzt, aber egal was ich 
versuche ich schaffe es nicht den CRC Wert zu überprüfen.

Folgendes habe ich vor: Ein Programm, welches noch in Java geschrieben 
werden muss, sendet in einem bestimmten Interval(250-1000ms) immer 
wieder ein Haufen an Datenpaketen an den Mikrocontroller der diese dann 
verarbeitet. Um Fehler in der Übertragung zu erkennen wollte ich das 
ganze mit CRC32 machen, da dieses per Hardware im STM32 implementiert 
ist. Dabei würde ich dann über die Daten eine CRC32 berechnen und diese 
dann nachträglich an die Daten hängen und übertragen. Der 
Mikrocontroller bastelt das dann alles aus einander und rechnet selber 
die CRC32 nochmal aus und wenn die passt alles gut wenn nicht wird 
erneut angefordert.

Ich habe von STM32 die Application Note "AN4187" gefunden und 
durchgelesen diese beschreibt wie man die CRC Einheit in den 
Mikrocontroller verwendet. Ich habe euch zwei Bilder davon angehängt 
einmal das "CRC Algorithm flowchart" und "CRC Peripheral Features".

So wie ich das sehe wird die CRC32 (nach dem CRC Peripheral Features 
Tabelle) mit folgenden Einstellungen berechnet:

Startwert: 0xFFFFFFFF (fix im F1)

Polynom  : 0x04C11DB7 (fix im F1)

Kein Reflektieren der Eingangs/Ausgangsdaten

Kein Final XOR am Ende


Ich habe für den Wert 0x1F7463AB(zufällig gewählt) die CRC32 wie folgt 
berechnen lassen im STM32:
1
RCC->AHBENR |= RCC_AHBENR_CRCEN;  //Enable CRC
2
CRC->CR |= CRC_CR_RESET;          //Reset CRC to 0xFFFFFFFF
3
CRC->DR = 0x1F7463AB;             //Input Data. Start Calc
4
uint32_t strCRC = CRC->DR;        //Get CRC32

Das Ergebnis ist 0x3A56D979 als CRC32-Prüfsumme. Auf mehreren Webseiten, 
Foren und auch YouTube Videos wurde immer erwähnt das man das Ergebnis 
mit dem "CRC-32/MPEG-2" Standard vergleichen soll jedoch ist die CRC32 
bei MPEG2: 0x4BC4C523 
(https://crccalc.com/?crc=0x1F7463AB&method=crc32&datatype=hex&outtype=hex)

In der Application Note wurde ja auch ein CRC Algorithm flowchart 
dargestellt. Ich vermute jetzt mal das die CRC Peripherie nach diesen 
Schema die CRC32 berechnet. Also habe ich eine C-Funktion geschrieben 
die nach diesem Flowchart arbeitet:
1
uint32_t crc32(uint32_t data) {
2
    uint32_t crc = 0xFFFFFFFFUL;            //CRC with Initial Value
3
    crc ^= data;                            //XOR with Inital Value and Input Data
4
    
5
    for(uint8_t i = 0; i < 32; i++) {       //Go through all 32 Bits of Input Data
6
        if(crc & 0x8000L) {                 //If MSB 1?
7
            crc = (crc << 1) ^ 0x04C11DB7UL;//Shift left by one and XOR with Polynormial
8
        }else{                              //MSB is 0
9
            crc <<= 1;                      //Shift left by one
10
        }
11
    }
12
    return crc;                         
13
}

Jedoch liefert mir die Funktion bei einen Dateneingang von 0x1F7463AB 
ein CRC Wert von 0x4E40D245. Da sollte ja eigentlich der gleiche Wert 
rauskommen wie bei der STM32 Peripherie, sofern ich das richtig 
umgesetzt habe mit der Funktion glaube aber wohl weil in der App Note 
auch ein Binäres Beispiel drin steht.

Dort soll als CRC 0x4C rauskommen Initial ist 0xFF, Input Data ist 0xC1 
und Polynom ist 0xCB. Wenn ich das dort eingebe und die Funktion auf 8 
Bit abändere bekomme ich als Ergebnis 0x4C raus passt.

Ich weiß nicht mehr weiter. Egal was ich versuche ich komme nicht auf 
die gleiche Prüfsumme.

Habt ihr ne Idee woran das liegen kann? Oder vllt. selbst schonmal mit 
der Hardware CRC Engine im STM32 gearbeitet?

Mfg

von Programmierer (Gast)


Lesenswert?

1
RCC->AHBENR |= RCC_AHBENR_CRCEN;
2
CRC->CR |= CRC_CR_RESET;

Wenn das so im Code steht, solltest du ein __DSB(); dazwischen einfügen, 
um darauf zu warten, dass die CRC-Peripherie auch wirklich an ist bevor 
du darauf zugreifst. Siehe auch 
Beitrag "STM32F1: Wartezyklus nach Clock-Enable im RCC" .

von A. B. (Gast)


Lesenswert?

Felix N. schrieb:
>         if(crc & 0x8000L) {                 //If MSB 1?

Das testet Bit 15, nicht Bit 31.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Felix N. schrieb:
> Jedoch liefert mir die Funktion bei einen Dateneingang von 0x1F7463AB

Da war noch was mit big/little endian (wenn man kein byte-array nimmt):
- 
https://stackoverflow.com/questions/39646441/how-to-set-stm32-to-generate-standard-crc32

von Felix N. (felix_n888)


Lesenswert?

Programmierer schrieb:
> Wenn das so im Code steht, solltest du ein __DSB(); dazwischen einfügen,
> um darauf zu warten, dass die CRC-Peripherie auch wirklich an ist bevor
> du darauf zugreifst. Siehe auch

Hallo, ja ist 1:1 raus kopiert. Ich habe das __DSB(); mal eingefügt. 
Dieses hat kein Unterschied gemacht. Jedoch hast du mich da auf eine 
Idee gebracht. Ich habe mal einfach zwischen jeder Codezeile ein 
__NOP(); eingefügt und tatsächlich funktioniert es nun.

Ein __NOP(); zwischen CRC->CR |= CRC_CR_RESET; und den eigentlichen 
Einfügen der Daten ins Datenregister der CRC.

Hatte dieses Problem schonmal bei einer anderen Peripherie glaube es war 
I2C. Die CRC Engine läuft ja nicht auf 72 MHz wie die CPU da wird das 
Datenreinschieben ins Datenregister wahrscheinlich schneller gewesen 
sein als die CRC selbst das Register auf 0xFFFFFFFF zurückgesetzt hat.

Jetzt bekomme ich bei 0x1F7463AB Dateneingang ein CRC32 Wert von 
0x4BC4C523. Passt.

A. B. schrieb:
> Das testet Bit 15, nicht Bit 31.

Oh nein, verdammt nicht wirklich. (facepalm) Das ist das wenn man sich 
das ganze erst selbst zusammenbastelt dann mit anderen Codebeispielen 
vergleicht und rumexperimentiert .... Ich habe es auf 0x80000000 
abgeändert. Nun liefert meine selbst geschrieben C-Funktion nach dem 
Flowchart auch das richtige Ergebnis von 0x4BC4C523. Danke

Irgend W. schrieb:
> Da war noch was mit big/little endian (wenn man kein byte-array nimmt):

Ja diesen Beitrag bei Stackoverflow hatte ich auch schon gelesen 
gemacht. Der Code soll die CRC Berechnung in das "richtige" CRC32 Format 
bringen mit Final XOR 0xFFFFFFFF und Reflektierten Eingangs und 
Ausgangsdaten. Hatte ich aber noch nicht getestet.

Ja also meine Hauptfragen warum das ganze nicht funktioniert hat sind 
beantwortet. Vielen dank an euch!

Hätte aber nochmal eine andere Frage. In Java gibt es eine vorerstelle 
Klasse die sich "CRC32" nennt diese berechnet ein CRC32 Wert nach dem 
ZIP CRC Standard. Habs ausprobiert es ist mit Startwert 0xFFFFFFFF, 
Final XOR 0xFFFFFFFF und Reflektieren Daten an Ein und Ausgang.

Ich würde jetzt meine C Funktion einfach in Java implementieren dann 
würden die richtigen Checksummen gebildet werden mit Java die dann auch 
geprüft werden können vom Mikrocontroller ohne groß jetzt noch die Daten 
der CRC-Engine nachträglich "nachzubearbeiten". Da der PC deutlich 
schneller ist als der Mikrocontroller sollte es keine große Rolle 
spielen wie lange die CRC Berechnung im Java Programm nachher dauert.

Hat das Reflektieren der Ein/Ausgangsdaten bzw. Final XOR irgendwelche 
Vorteile der gegenüber der oben geposteten C-Funktion? Ich habe mal 
gelesen das teilweise manche Peripherie Module wie SPI/USART die Daten 
teils nur reflektiert ausgeben.

Mfg

von Programmierer (Gast)


Lesenswert?

Felix N. schrieb:
> Ein __NOP(); zwischen CRC->CR |= CRC_CR_RESET; und den eigentlichen
> Einfügen der Daten ins Datenregister der CRC.

Verwende lieber DSB, das ist genau dafür da. NOP funktioniert hier eher 
zufällig, und nicht unbedingt immer konsistent.

von Stefan F. (Gast)


Lesenswert?

Programmierer schrieb:
> RCC->AHBENR |= RCC_AHBENR_CRCEN;
> CRC->CR |= CRC_CR_RESET;
> Wenn das so im Code steht, solltest du ein __DSB(); dazwischen einfügen,
> um darauf zu warten, dass die CRC-Peripherie auch wirklich an ist bevor
> du darauf zugreifst.

Da muss ich mal nachhaken. Ich habe bei STM32L0, F1 und F3 schon oft 
ähnliche Kombinationen von Register-Zugriffen gehabt und dabei noch nie 
DSB benötigt. Nun denke ich mir nach deine Info, dass ich vielleicht 
einfach nur Glück hatte. Aber: Die Code-Beispiele und HAL Code von ST 
machen es nicht anders. Auch da habe ich DSB noch nicht gesehen.

Mir ist auch nicht klar, was es hier nützen soll. Sowohl die RCC 
Register als auch die CRC Register hängen am AHB Bus. Soweit ich 
verstehe überlappen sich aufeinanderfolgende Zugriffe auf dem selben Bus 
niemals. Selbst wenn die CRC Einheit an einem langsamen APB1/2 Bus 
hängen würde, gäbe es kein Problem, weil der Weg dorthin durch den AHB 
Bus führt.

Überlappende Zugriffe sind meiner Meinung nach nur möglich, wenn ich 
direkt nacheinander auf APB1 und APB2 (oder anders herum) zugreife.

Habe ich etwas übersehen?

von Stefan F. (Gast)


Lesenswert?

Felix N. schrieb:
> Hallo, ja ist 1:1 raus kopiert. Ich habe das __DSB(); mal eingefügt.
> Dieses hat kein Unterschied gemacht.

Wundert mich nicht, siehe mein vorheriger Beitrag.

> Ein __NOP(); zwischen CRC->CR |= CRC_CR_RESET; und den eigentlichen
> Einfügen der Daten ins Datenregister der CRC.

Schauen wir mal, wie dieses Bit dokumentiert ist: "Resets the CRC 
calculation unit and sets the data register to 0xFFFF FFFF.
This bit can only be set, it is automatically cleared by hardware."

Ich würde sagen, du musst warten, bis die Hardware das Bit wieder 
ge-cleared hat. Das ein NOP hier Genügt, ist Glück.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Felix N. schrieb:
> Hätte aber nochmal eine andere Frage. In Java gibt es eine vorerstelle
> Klasse die sich "CRC32" nennt diese berechnet ein CRC32 Wert nach dem
> ZIP CRC Standard.

Der wiederum auf ISO3309 (HDLC) und V.42 verweist.

> Habs ausprobiert es ist mit Startwert 0xFFFFFFFF,
> Final XOR 0xFFFFFFFF und Reflektieren Daten an Ein und Ausgang.

Laut 
https://reveng.sourceforge.io/crc-catalogue/all.htm#crc.cat.crc-32-iso-hdlc
>> width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true
>> xorout=0xffffffff check=0xcbf43926 residue=0xdebb20e3
>> name="CRC-32/ISO-HDLC"

Also ja, sollte stimmen.

> Hat das Reflektieren der Ein/Ausgangsdaten bzw. Final XOR irgendwelche
> Vorteile der gegenüber der oben geposteten C-Funktion? Ich habe mal
> gelesen das teilweise manche Peripherie Module wie SPI/USART die Daten
> teils nur reflektiert ausgeben.

Ja, das Reflektieren soll von Hardware-CRCs in UARTs stammen. UARTs 
schieben das niederwertigste Bit eines Datums zuerst auf die Leitung. 
Berechnet man eine CRC direkt auf diesem Bitstrom rechnet man auf 
reflektierten Daten. Das Reflektieren in Software, bzw. reflektierte 
Algorithmen sorgen für Kompatibilität mit direkt aus Bitströmen 
berechneten CRCs.

Wenn man Sende- und Empfangsseite in Software macht braucht man das 
nicht, aber es ist irgendwie hängen geblieben.

Das abschließende XOR hat einen ähnlichen Grund wie das anfängliche XOR. 
Allerdings sichert es einen obskuren Fehlerfall ab. Nämlich wenn aus 
irgend einem Grund den Daten und der CRC bei der Übertragung noch 
Müll-Daten angefügt wurden und man das korrekte Frame-Ende nicht erkannt 
hat.

Also statt einem Frame wie
 <Daten> <CRC>
etwas wie
 <Daten> <CRC> <Müll>

Wenn dann die Müll-Daten noch alles Null-Bytes sind, dann ergibt die 
normale CRC-Berechnung ohne abschließendes XOR trotzdem eine gültige 
CRC.

von Felix N. (felix_n888)


Lesenswert?

Programmierer schrieb:
> Verwende lieber DSB, das ist genau dafür da. NOP funktioniert hier eher
> zufällig, und nicht unbedingt immer konsistent.

Alles klar.

Stefan ⛄ F. schrieb:
> Ich würde sagen, du musst warten, bis die Hardware das Bit wieder
> ge-cleared hat. Das ein NOP hier Genügt, ist Glück.

Ich weiß nicht wie oft ich das Datenblatt im Abschnitt "CRC" gelesen 
habe es ist mir nicht aufgefallen das das Bit von der Hardware gelöscht 
wird und man darauf warten könnte. Habs wohl immer überflogen.

Stefan ⛄ F. schrieb:
> Aber: Die Code-Beispiele und HAL Code von ST
> machen es nicht anders.

Später nachdem es Freitag Vormittag + Mittag nicht geklappt hat habe ich 
am Abend mir den HAL Code für CRC angeschaut und mich daran ein bisschen 
gehalten. Hab auch alles gefunden außer ein Makro der die CRC 
zurücksetzt. Wahrscheinlich wäre mir das warten auf das Bit dann 
aufgefallen.

Noch mal eine Frage zu CRC generell:

Wenn ich jetzt doch den STM32 so laufen lassen will das er mir ein 
CRC32-ZIP generiert muss ich ja noch die Daten Final XORn mit 0xFFFFFFFF 
und die Eingangs und Ausgangsdaten reflektieren.

Das final XOR ist ja recht einfach mit (... ^ 0xFFFFFFFFUL) gemacht. 
Aber wie genau ist das mit den Reflektieren wird da einfach nur die 
Bitreihe umgekehrt? Sprich:

0000 0101 -> 0x05 (Original)

1010 0000 -> 0xA0 (Reflektiert)

? Oder wie Reflektiert man das?


//EDIT:

Hannes J. schrieb:
> Das abschließende XOR hat einen ähnlichen Grund wie das anfängliche XOR.
> Allerdings sichert es einen obskuren Fehlerfall ab. Nämlich wenn aus
> irgend einem Grund den Daten und der CRC bei der Übertragung noch
> Müll-Daten angefügt wurden und man das korrekte Frame-Ende nicht erkannt
> hat.

Achso okay. Ja dann ist doch noch sinnvoll mit zu machen. Lässt sich ja 
leicht umsetzten.

Hannes J. schrieb:
> Ja, das Reflektieren soll von Hardware-CRCs in UARTs stammen. UARTs
> schieben das niederwertigste Bit eines Datums zuerst auf die Leitung.

Ja genauso oder in etwa hatte ich das in irgendein Blog über CRC 
Berechnungen und umsetzten und C-Code gelesen gehabt.

Hannes J. schrieb:
> Also ja, sollte stimmen.

Gut

Mfg

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Hannes J. schrieb:

> Das abschließende XOR hat einen ähnlichen Grund wie das anfängliche XOR.
> Allerdings sichert es einen obskuren Fehlerfall ab.

Daß das im STM32 nicht drin ist, dürfte wohl im Hinblick auf 
Bufferlängen sein, die nicht durch 4 teilbar sind. Das 
CRC-Eingangsregister ist ja immer 32 Bit weit, und so kann man die 
"überflüssigen" letzten 1-3 Byte als Nullbytes reinhängen. Ein 
abschließendes XOR muß man dann manuell machen.

von Nop (Gast)


Lesenswert?

Felix N. schrieb:

> Aber wie genau ist das mit den Reflektieren wird da einfach nur die
> Bitreihe umgekehrt?

Ja genau, siehe hier (reverse): 
https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks

Bei "normal" ist links im Bit 16 der höchste Koeffizient, der bei der 
Hex-Darstellung implizit ist und weggelassen wird. Bei "reverse" wird 
dasselbe Bit weggelassen, es ist immer noch Bit 16, nur steht es 
gespiegelterweise jetzt ganz rechts.

Der Unterschied zum reziproken Polynom ist der, daß dabei das volle 
Polynom (also mit 17 Bit dargestellt) ebenfalls gespiegelt wird, aber 
anders als bei reverse beim reziproken nicht das neue rechteste Bit, 
sondern das linkeste weggelassen wird, d.h. das niederwertigste aus der 
normalen Form und nicht das alte (und implizite) höchstwertige.

von M. Н. (Gast)


Lesenswert?

Du hast bei der STM32 CRC ein paar implementationsprobleme, die  daher 
rühren, dass die CRC 32bit-Wortweise arbeitet.

Der STM schiebt quasi Bit 31 zuerst in die CRC und als letztes bit 0 des 
Datenworts. Wenn du jetzt 4 Bytes aus dem Speicher nimmst, sind die aber 
wegen dem little endian layout so angeordnet: [7...0] [15...8] [23...16] 
[31.. 24]

Da muss man aufpassen, wenn man den Datenstream auf einem Rechner bspw. 
byteweise verrechnet. Während du bspw. in Python o.ä.

Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7...

in die CRC nacheinander schieben kannst, muss du darauf achten, dass der 
STM, wenn du die obige Folge hast, jedes 32 bit Wort gespiegelt 
ausließt. Es würde somit folgendes in die CRC wandern:
1
Byte3 Byte2 Byte1 Byte0 Byte7 Byte6 Byte5 Byte4
2
|---- Wort 1 ----------|--------Wort 2---------|

In Python nutze ich für die STM32F4 CRC folgenden Code:
1
import crcmod
2
import crcmod.predefined
3
4
crc_calc = crcmod.predefined.mkCrcFun('crc-32-mpeg')
5
6
def stm32_calculate_crc(input_data):
7
  data = bytearray(input_data)
8
  be_data = bytearray([0 for k in range(0, len(data))])
9
  # Rearrange data, because the STM controller sees it as 32 bit little endian words
10
  for i in range(0, int(len(data)/4)):
11
    be_data[i*4+0] = data[i*4+3]
12
    be_data[i*4+1] = data[i*4+2]
13
    be_data[i*4+2] = data[i*4+1]
14
    be_data[i*4+3] = data[i*4+0]
15
16
  return crc_calc(be_data)

Hier, wie gesagt auf das byte ordering aufpassen. Meine Funktion oben 
dreht, bevor sie die Daten in die CRC schiebt alle 32 bit Worte um.

Wenn du einen C Code brauchst, der das wortweise mal testet: Der hier 
funktioniert und nimmt wie der STM auch 32bit Worte:
1
#include <stdint.h>
2
3
static uint32_t do_crc(uint32_t init, uint32_t data)
4
{
5
  uint32_t crc = init;
6
  uint32_t cnt;
7
8
  for (cnt=0; cnt < 32; cnt++) {
9
    crc = ((int32_t)(crc ^ data))<0 ? (crc << 1) ^ 0x04C11DB7 : crc << 1;
10
    data <<=1;
11
  }
12
13
  return crc;
14
}
15
16
uint32_t calculate_stm_crc(uint32_t *data, size_t len)
17
{
18
  uint32_t crc = ~0U;
19
20
  while (len--) {
21
    crc = do_crc(crc, *data++);
22
  }
23
24
  return crc;
25
}

Einfach die Funktion calculate_stm_crc() mit deinen Daten füttern.

EDIT:
Ich habe das jetzt mal mit deinem Testwert 0x1F7463AB gemacht. Ich komme 
mit meinem C Progrämmchen auf den Wert: 0x4bc4c523.

Woher dien Wert kommt, kann ich nicht nachvollziehen. Mein Code 
funktioniert bei mir für einen STM32F407. Der sollte gemäß Tabelle ja 
dieselbe CRC-Einheit haben... Komisch.

EDIT2: Habe den Wert mal auf meinem STM32F407 durch die CRC unit laufen 
lassen. Ergebnis: 0x4bc4c523.

Ist es sichergestellt, dass du die richtigen Werte in das Modul 
schreibst und auch wieder richtig liest?

Oder das CRC Modul bei deinem F1 ist doch anders als meins...

von Felix N. (felix_n888)


Lesenswert?

Hallo,

Nop schrieb:
> Ein
> abschließendes XOR muß man dann manuell machen.

Das ist ja schnell gemacht.

Nop schrieb:
> Bei "normal" ist links im Bit 16 der höchste Koeffizient, der bei der
> Hex-Darstellung implizit ist und weggelassen wird. Bei "reverse" wird
> dasselbe Bit weggelassen, es ist immer noch Bit 16, nur steht es
> gespiegelterweise jetzt ganz rechts.

Ah okay. Ja ich muss sagen ich habe mich zwar Freitag und den ganzen 
Samstag mit CRC beschäftig und habe auch ein zwei Berechnungen auf dem 
Papier bei einer CRC-8 durchgeführt. Aber so tief bin ich da jetzt nicht 
eingestiegen.

Aber um mal auf das reflektieren zurück zukommen würde man das dann so 
machen?
1
uint32_t reflectBits(uint32_t data) {
2
  uint32_t reflected = 0;                  //Holds the Reflect Value
3
  
4
  for(uint8_t i = 0; i < 32; i++) {        //Go through all Bits
5
    if(data & (1<<(31-i))) {              //Check if Last Bit - i is a 1 
6
      reflected |= (1<<i);                 //Put a 1 at i (reserved to input data)
7
    }else{
8
      reflected &= ~(1<<i);             //Same for 0
9
    }
10
  }
11
  
12
  return reflected;                         //Return Reflected Data
13
}

Also funktionieren tut diese Funktion so wie ich mir das vorgestellt 
habe. Wenn ich das in Visual Studio Code ausführe und als Input 0xAA 
oder 0x0000FFFF reingebe dann kommt für 0xAA 0x55 raus und bei FF 
0xFFFF0000. Nur ist das jetzt richtig? Wenn ich das aus deinem Text 
richtig verstanden habe wird das um das 16 Bit gespiegelt?

Nop schrieb:
> reziproken Polynom

Also muss ich wenn ich die Daten reflektiere 0xDB710641 als Generator 
Polynom nehmen anstatt 0x04C11DB7?

M. H. schrieb:
> Wenn du jetzt 4 Bytes aus dem Speicher nimmst, sind die aber
> wegen dem little endian layout so angeordnet: [7...0] [15...8] [23...16]
> [31.. 24]

Woher weiß ich eigentlich ob die Daten jetzt nach little oder big endian 
angeordnet sind? Ist das standardmäßig immer little endian?

M. H. schrieb:
> In Python nutze ich für die STM32F4 CRC folgenden Code:

Ich habe jetzt von Python keine Ahnung, da später die PC Software von 
mir in Java geschrieben wird habe ich meine CRC32 Berechnungsfunktion 
mal in Java erstellt. In Java sind ein paar mehr Schritte nötig da es 
kein "unsigned int" gibt der 32-Bit breit ist. int hat zwar 32-Bit breit 
jedoch signed reicht also nicht. long ist mindestens 64-Bit breit jedoch 
auch signed reicht für die CRC32. Meine sogar mal gelesen zu haben das 
ein long ob Java SE 8 128-bit breit ist.

Falls es für jemanden relevant ist hier der Code für Java um eine CRC-32 
zu berechnen die vom STM32 CRC Engine gleich berechnet wird:
1
public long crc32_stm32(long data) {
2
  long crc = 0xFFFFFFFFL;
3
  crc ^= data;
4
  for(int i = 0; i < 32; i++) {
5
    if((crc & 0x80000000L) != 0) {
6
      crc = (crc << 1) ^ 0xDB710641L;
7
    }else{
8
      crc <<= 1;
9
    }
10
  }
11
  crc ^= 0xFFFFFFFFL;
12
  return crc & 0xFFFFFFFFL;
13
}

Hier ist jetzt noch ein final XOR mit eingebaut von 0xFFFFFFFF welches 
ich bei dem STM32 auch eingebaut habe nach der eigentlichen Berechnung 
der CRC von der Engine vom STM.

Wichtig ist hierbei zu beachten das & 0xFFFFFFFF bei dem return sonst 
bekommt man ein 64-Bit langen Integer zurück wovon aber nur die ersten 8 
Ziffern die CRC sind. Alternativ kann man auch (crc << 32) >> 32 machen. 
Und das "L" Suffix an den Zahlen nicht vergessen sonst werden die wie 
Integer behandelt und nicht wie longs. Hat mich gestern fast zwei 
Stunden gekostet bis ich das gecheckt hatte.

Felix N. schrieb:
> Jetzt bekomme ich bei 0x1F7463AB Dateneingang ein CRC32 Wert von
> 0x4BC4C523. Passt.

Felix N. schrieb:
> Nun liefert meine selbst geschrieben C-Funktion nach dem
> Flowchart auch das richtige Ergebnis von 0x4BC4C523.

M. H. schrieb:
> Woher dien Wert kommt, kann ich nicht nachvollziehen.

Was genau meinst du? Mittlerweile bekomme ich auch den richtigen Wert 
von 0x4BC4C523 bei dem STM32 und meiner C-Funktion. Die Java Funktion 
liefert auch den gleichen Wert wenn man das Final XOR weg lässt.

Oder reden wir da irgendwo aneinander vorbei?

M. H. schrieb:
> Ist es sichergestellt, dass du die richtigen Werte in das Modul
> schreibst und auch wieder richtig liest?

Denke schon. Zurücksetzten der CRC. Mit dem Bit in einer while Schleife 
warten (Danke @stefanus) dann daten reinschieben und danach wieder 
abholen.
Wüsste nicht was man da jetzt noch falsch machen könnte. Abgesehen von 
der vorher gefehlten while Schleife.

M. H. schrieb:
> Oder das CRC Modul bei deinem F1 ist doch anders als meins...

Also streng genommen habe ich kein STM32F103C8T6 sondern ein 
CKS32F103C8T6 der auf ein Blue-Pill Board verbaut ist. Er ist zwar als 
STM32 gelabelt hat aber eine andere ID und 128K an Flash anstatt 64K. 
Aber bis jetzt hatte ich noch keine Sachen gefunden die damit nicht 
gehen(oder anders funktionieren) im Gegensatz zum Original STM32.

Ich habe oben im Eröffnungsbeitrag ein Bild verlinkt wo die CRC 
Peripherien in unterschiedlichsten STM Controllern aufgeführt sind für 
den L1, F1, F2, F4 sind alle CRC Engines gleich nur der F0 und F3 sind 
dort "leistungsstärker" weil man mehr einstellen kann wie Polynom, 
Startwert ...

Mfg

von M. Н. (Gast)


Lesenswert?

Felix N. schrieb:
> Woher weiß ich eigentlich ob die Daten jetzt nach little oder big endian
> angeordnet sind? Ist das standardmäßig immer little endian?

Du schaust in das Datenblatt deines Controllers. Die meisten Systeme 
sind little-endian. Dein Rechner (x86_64) ist auch little-endian.
Theoretisch kannst du die Cortex-Kerne von ARM auch alle als big-endian 
synthetisieren. Mir ist aber bisher keiner untergekommen, der das so 
macht.
In deinem Compiler aufruf für GCC müsste dementsprechend auch irgendwo 
die Option "-mlittle-endian" auftauchen (sofern du GCC nutzt).

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Felix N. schrieb:
>
> Aber um mal auf das reflektieren zurück zukommen würde man das dann so
> machen?

Auf ARM? Mit RBIT:

https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-instruction-set/general-data-processing-instructions/rev--rev16--revsh--and-rbit

In C:
1
uint32_t __RBIT(uint32_t int value); // in CMSIS
2
uint32_t __rbit(uint32_t int value); // bei manchen Compilern für ARM

Ansonsten gibt es diverse C-Tricks die alle natürlich langsamer als RBIT 
sind:

https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious

von Nop (Gast)


Lesenswert?

Felix N. schrieb:

> Aber um mal auf das reflektieren zurück zukommen würde man das dann so
> machen?

Wenn man's von Hand machen will, wäre das eine portable Möglichkeit. Das 
Setzen der 0 in der Schleife ist aber unnötig, weil "reflected" ja schon 
mit 0 vorinitialisiert ist. Normalerweise würde man das aber nicht 
berechnen, sondern direkt als Konstante im Quelltext nutzen.

Wobei, eigentlich würde man auch die Polynom-Konstante so nicht im 
Quelltext nutzen, sondern sich eine Lookup-Tabelle machen, d.h. wenn man 
keine CRC in Hardware hat oder die HW-CRC nicht das gewünschte Polynom 
kann. Entweder Byte-weise, das kostet 1kB ROM für CRC32, oder 
Nibble-weise, das kostet 64 Bytes.

> Nur ist das jetzt richtig? Wenn ich das aus deinem Text
> richtig verstanden habe wird das um das 16 Bit gespiegelt?

Das kommt nur, weil in dem Link beispielhaft mit einer CRC16 gearbeitet 
wird. Für CRC32 wäre das also entsprechend um 32 Bit gespiegelt, und das 
"implizite" weggelassene Bit, das immer 1 ist, ist dann Bit 32 
(beginnend bei Bit 0 gezählt).

>> reziproken Polynom
>
> Also muss ich wenn ich die Daten reflektiere 0xDB710641 als Generator
> Polynom nehmen anstatt 0x04C11DB7?

Reflektiert und reziprok sind was anderes. Bei reflektiert geht es um 
dasselbe Polynom, nur daß man bei der Berechnung andersrum reinschiebt 
und daher auch das Polynom bitweise andersrum hinschreibt. Das reziproke 
Polynom ist ein tatsächlich anderes, das aber dieselben Eigenschaften 
bezüglich Fehlererkennung hat.

> Woher weiß ich eigentlich ob die Daten jetzt nach little oder big endian
> angeordnet sind? Ist das standardmäßig immer little endian?

Meistens ja, aber das liegt daran, auf welcher CPU Dein Code läuft. 
Solange Deine Daten in Deiner Hardware bleiben, ist das auch meistens 
egal - aber wenn Du welche nach außerhalb schickst oder von außerhalb 
bekommst, muß man im Protkoll nachsehen, wie die Byte-order auf dem 
Draht ist. Oft big endian ("network order").

Portablen Code rüstet man daher mit Funktionen Host_To_Network und 
Network_To-Host (oder so) aus, die man immer aufruft, die aber auf einem 
System, das dieselbe endianess wie das Protokoll auf dem Draht hat, 
einfach nichts tun.

von Gahlen F. (gahlenfeld)


Lesenswert?

Hallo bambel2,
VIELEN DANK für den Tipp mit der Umwandlung ins Big-Endian-Format!!!
Damit klappt es natürlich.
Ich hätte mir beinahe die Zähne daran ausgebisse..
Gruß Gahlen

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.