Forum: Mikrocontroller und Digitale Elektronik STM32 CRC mit 3 Byte Daten


von Martin (Gast)


Lesenswert?

Hi,

ich bin gerade dabei mein Kommunikationsprotokoll über UART zu erweitern 
um CRC.
Das klappt aber nur in bestimmten fällen:
Ich habe folgende Funktion auf dem STM32F103:
1
uint32_t CRC_CalcBlockCRC_8(uint8_t* data, uint32_t len)
2
{
3
  uint32_t* pBuffer = (uint32_t*)data;
4
  uint32_t BufferLength = len / 4;
5
  uint32_t index = 0;
6
7
  for (index = 0; index < BufferLength; index++)
8
  {
9
    CRC->DR = __RBIT(pBuffer[index]);
10
  }
11
  return __RBIT(CRC->DR) ^ 0xFFFFFFFF;
12
}
Die Funktion hilft mir dabei, aus einem uint8_t Array die CRC32 mithilfe 
des CRC-Registers des STM32 zu berechnen. Solange len ein Vielfaches von 
4 ist, klappt das wunderbar. Ist len % 4 != 0, kommt keine korrekte CRC 
mehr bei raus.
Ich habe bereits versucht ein eigenes Array anzulegen, das Array mit 
0x00 zu füllen, anschließend die Daten rein zu kopieren und dann immer 
ein Vielfaches von 4 an die Funktion zu übergeben.
Mit dem Online-Calculator von 
https://www.lammertbies.nl/comm/info/crc-calculation.html bin ich dann 
darauf gestoßen, dass bei einem Array-Inhalt in der Art von "0x01, 0x01, 
0x07" auf dem µC die Checksumme raus kommt für "0x01, 0x01, 0x07, 0x00".

Wie kann ich das verhindern? Wie kann ich (ohne immer leere Daten 
schicken zu müssen) mit der integrierten CRC des STM32 die richtige 
Checksumme berechnen wenn ich, wie in meinem Beispiel, nur 3 Byte Daten 
habe?

von nicht"Gast" (Gast)


Lesenswert?

Nur so ne Idee.

rechne deine 8Bit CRC auch nur mit 8Bit breiten Datentypen.

von Sebastian V. (sebi_s)


Lesenswert?

Nach dem Datenblatt scheint die CRC Funktionalität nur in ganzen 32Bit 
Blöcken zu arbeiten. Von daher würde ich sagen, dass es schlecht 
aussieht. Mir ist nicht bekannt das man die fehlenden Bytes mit 
irgendwas einfachem auffüllen könnte um die Checksumme nicht zu 
verändern. Bleibt also nur die Daten auf vielfache von 4 Byte 
aufzufüllen oder die CRC Berechnung in Software zu machen (wenigstens 
für den letzten unvollständigen Block).

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Wenn Du doch nur 3 Byte per CRC prüfen willst, brauchst Du da eine 32bit 
Prüfsumme?

Habe die Prüfsummen-Routine für den DS18B20 leider auch nur kopiert 
(geht mir halt nicht in den Kopf), soll halt für 'viele Daten' 
ungeeignet sein, was ich gelesen habe.
Vll. kannst Du damit was anfangen, ist allerdings Assembler :/
1
; ATmega8                    
2
; Bernhard.Erfurt@gmx.de            
3
; 06/2012                    
4
; ##############################################################################
5
; ##############################################################################
6
; ##############################################################################
7
; CRC Berechnung übernommen und abgeändert von: http://s-huehn.de        
8
; CRC-Berechnung Application Note 27 von Dallas/Maxim              
9
;                                        
10
; zu berechnende Daten  ow_Data (sRam)
11
; CARRY bei CRC-Fehler, sonst Carry gelöscht
12
;                                        
13
ow_CRC8:        ;meinem Verständnis nach, berechnet die Routiene eine CRC8 Prüfsumme, Name auf '8' geändert
14
  push XL            ;auf dem Stack sichern            
15
  push XH
16
  push r16
17
  push r17
18
  push r18
19
  push r19
20
  push r20
21
  push r21
22
;ONE_WIRE_ROM_CRC7_RUN:            ;wenn erneut berechnet werden soll, dann hier einspringen
23
  ; INITIALISIERUNG
24
   ldi XL,low (ow_Data) ;Zeiger         
25
    ldi XH,high(ow_Data) 
26
    ldi     r19, 8                          ;7 Bytes berechnen für ID nebst CRC, 8 Bytes für Scratchpad
27
                      ;wenn eine ID geprüft werden soll, erstes Byte 00, restliche 8 Byte ID
28
                      ;dahinter, da führende Nullen bei der CRC keine Änderung hervorrufen
29
    ldi     r20, 0x18                       ;Konstante für Berechnung
30
    clr     r17                            ;CRC-Startwert = 0 setzen
31
;ONE_WIRE_CRC7_100: 
32
  ld      r18, x+                         ;Byte aus Puffer holen
33
    mov     r21, r18                        ;Byte zwischenspeichern
34
    ldi     r16, 8                          ;8 Verschiebungen
35
;ONE_WIRE_CRC7_200: 
36
  eor     r18, r17                        ;CRC berechnen
37
    ror     r18                             ;Ergebnis in Carry schieben
38
    mov     r18, r17                        ;letzten CRC-Wert holen
39
    ;brcc    ONE_WIRE_CRC7_300                          ;war Ergebnis = 0? ja -> weiter
40
  brcc    PC+2                            ;war Ergebnis = 0? ja -> weiter
41
    eor     r18, r20                        ;sonst CRC-Wert updaten
42
;ONE_WIRE_CRC7_300: 
43
  ror     r18                             ;neuen CRC-Wert positionieren
44
    mov     r17, r18                        ;und speichern
45
    mov     r18, r21                        ;restliche Bits holen
46
    bst     r18, 0                          ;LSB sichern, nächste Bitposition
47
    ror     r18                             ;gesichertes Bit als MSB einsetzen
48
    bld     r18, 7                          ;(Ersatz für RR-Befehl)
49
    mov     r21, r18                        ;und Wert zwischenspeichern
50
    dec     r16                             ;alle Verschiebungen erledigt?
51
  brne    PC-13  ;ONE_WIRE_CRC7_200      ;nein -> Schleife
52
    dec     r19                             ;alle Bytes bearbeitet?
53
  brne    PC-18  ;ONE_WIRE_CRC7_100      ;nein -> Schleife
54
;-------------------------------------------------------------------------------
55
;  hier ist die CRC-Summe berechnet, mit empfangenem Wert überprüfen
56
  LDS temp,(ow_Data+8)
57
  ; ev. Korrektur
58
  ;STS(ow_Data+7),R17    ;eigentlich unnötig
59
  cp temp,r17
60
  brne PC+3  ;ONE_WIRE_CRC7_ERROR
61
  CLC                    ; CARRY=0 für 'CRC ok'
62
  rjmp PC+2  ;ONE_WIRE_CRC7_FERTIG
63
;-------------------------------------------------------------------------------
64
;ONE_WIRE_CRC7_ERROR:
65
  ;ggf Fehlerausgabe ect.pp.
66
  ;dann müssen aber die Sprünge angepasst werden
67
  ;da vor dem Test der berechnete CRC-Wert im Speicher abgelegt wurde (sofern das STS nicht geremmt ist), würde eine erneute Berechnung 'CRC ok' bringen - weshalb erschließt sich mir nicht, da die Daten ja wohl zuvor fehlerhaft übertragen wurden
68
;  rjmp ONE_WIRE_ROM_CRC7_RUN    ;die erneute Prüfung ist deshalb geremmt, da ohne STS erneut CRC-Fehler und mit STS definitiv kein CRC-Fehler, da CRC selber zusammen gerechnet und überschrieben
69
  SEC                    ;für 'CRC-Fehler'
70
;-------------------------------------------------------------------------------
71
;ONE_WIRE_CRC7_FERTIG:
72
  pop r21                    ; wiederherstellen                  
73
  pop r20
74
  pop r19
75
  pop r18
76
  pop r17
77
  pop r16
78
  pop XH
79
  pop XL
80
ret
Ich habe die erneute Berechnung auskommentiert, da, wenn die Daten 
falsch empfangen wurden, eine erneute Prüfung ebenfalls nur zu einem 
CRC-Fehler führt und das Programm sich dann aufknüpft, da immer wieder 
die Berechnung durchgeführt wird.

Auch ist in den Kommentaren ersichtlich, daß führende Null-Bytes keine 
Auswirkung auf die Prüfsumme haben - eine unterschiedliche Anzahl von 
Null-Bytes am Anfang geht also gar nicht in die Berechnung ein.
CRC von 00 00 00 (oder beliebig viele 00er mehr) -> 00

MfG

von Sebastian V. (sebi_s)


Lesenswert?

Da kann man sich vermutlich besser den Code auf Wikipedia anschauen: 
https://de.wikipedia.org/wiki/Zyklische_Redundanzpr%C3%BCfung#Berechnung_einer_CRC-Pr.C3.BCfsumme_in_C_und_Pascal_bzw._Delphi

Dort wird im ersten Beispiel auch schnell klar, dass 0 Bits am Anfang 
die Checksumme nicht verändern (Shiften von 0 bleibt 0). Allerdings ist 
die CRC Variante im STM32F1 wohl die aus dem zweiten Beispiel wo der 
Startwert auf 0xFFFFFFFF initialisiert wird. Hier ist es etwas 
kniffeliger.

Das Invertieren des Startwerts hat laut Wikipedia den gleichen Effekt 
wie das Invertieren der ersten 4 Bytes. Aus dem einfachen Voranstellen 
von 0x00 Bytes werden also 0xFF Bytes und zusätzlich müssen alle Bytes 
die vom ersten 4 Byte Block in den zweiten gerutscht sind invertiert 
werden:

   11 22 33 44 55 66 77
FF 11 22 33 BB 55 66 77

Diese beiden Blöcke liefern nun die gleiche CRC Summe. Bei weniger als 4 
Bytes füllt man ebenfalls auf mit 0xFF am Anfang auf 4 Bytes auf und 
muss anschließend in der CRC Summe die Bits flippen die durch die 
führenden 0xFF auf 0 gesetzt wurden.

von Martin (Gast)


Lesenswert?

Perfekt, vielen dank Sebastian!
Da wäre ich so nie drauf gekommen :)

Funktioniert mit diesem Schema auch mit 5 oder 6 Daten-Bytes.

von Sebastian V. (sebi_s)


Lesenswert?

Sollte für beliebig lange Daten funktionieren. Habe ich allerdings nicht 
alles ausprobiert.

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.