Forum: Mikrocontroller und Digitale Elektronik datenübertragung mit crc5 - seltsame effekte


von Kei N. (mckalle)


Lesenswert?

Hallo,

ich habe zwischen meinem PC und einem µC (atmega88 @ 7,3728MHz) eine 
serielle Datenübertragung über Infrarot realisiert.
Dies funktioniert wunderbar, jedoch kommen manchmal korrupte Daten am µC 
an. Da der µC für Lichtsteuerung zuständig ist, macht sich dies durch 
hässliche Blitze o.ä. bemerkbar. Die Daten werden direkt über eine 
rs232-Schnittstelle gesendet (4800 Baud, 5 Datenbits, 1 Startbit, 1 
Stoppbit, 0 Parität). Dabei werden jeweils 5 Datenblöcke hintereinander 
gesendet, wobei der letzte Datenblock eine xor-Prüfsumme enthält.
Um die Fehlererkennung zu verbessern, würde ich diese gerne durch eine 
crc5-Prüfsumme ersetzen. Die Berechnung der Prüfsumme funktioniert im 
sendenden Programm ohne Probleme (Generatorpolynom: 00101, Datenbytes 
und Rest werden jeweils gespiegelt).
Im Debugmodus vom AVR-Studio wird über eine Tabellenbasierte Lösung auch 
die korrekte CRC-Prüfsumme ermittelt.
Trotzdem funktioniert die Datenübertragung nicht mehr.

So, nun ein bisschen Code ;)
1
//Look-Up-Tabelle
2
volatile uint8_t crctable[32] =
3
    {
4
         0, 14, 28, 18, 17, 31, 13,  3,
5
        11,  5, 23, 25, 26, 20,  6,  8,
6
        22, 24, 10,  4,  7,  9, 27, 21,
7
        29, 19,  1, 15, 12,  2, 16, 30
8
    };
9
10
    //Enthält die Nummer des Empfangenen Bytes
11
    volatile uint8_t incomingByteCounter = 0;
12
13
    //Die Werte der Empfangenen Datenbytes
14
    volatile uint8_t byte0 = 0;
15
    volatile uint8_t byte1 = 0;
16
    volatile uint8_t byte2 = 0;
17
    volatile uint8_t byte3 = 0;
18
19
    //Der aktuelle crc-Wert
20
    volatile uint8_t crcValue = 0;
21
22
    //Wenn nach einer bestimmten Zeit (~6ms) kein weiteres Byte eingetroffen ist, werden die Daten resettet
23
    uint8_t uartResetCounter = 0;
24
25
26
27
// ===== Usart Empfangsinterrupt ================================
28
ISR (USART_RX_vect) //Daten liegen in UDR0
29
{
30
    //TimingInterrupt deaktivieren
31
    cli();
32
    //ResetCounter zurücksetzen
33
    uartResetCounter = 0;
34
    
35
    //die Bytenummer des eingehenden Bytes bestimmen
36
    incomingByteCounter++;
37
    
38
    //crc-lookup-Index für das aktuelle Byte berechnen
39
    uint8_t nextCrcIndex = (UDR0 & 0x1f) ^ (crcValue & 0x1f);
40
41
    //Eingetroffenes Byte verarbeiten
42
  //Je nach Bytenummer, entweder zwischenspeichern, oder alle eingetroffenen Daten verarbeiten
43
    switch(incomingByteCounter)
44
    {
45
        case 1:
46
            byte0 = UDR0;
47
            crcValue = crctable[nextCrcIndex];
48
            break;
49
        case 2:
50
            byte1 = UDR0;
51
            crcValue = crctable[nextCrcIndex];
52
            break;
53
        case 3:
54
            byte2 = UDR0;
55
            crcValue = crctable[nextCrcIndex];
56
            break;
57
        case 4:
58
            byte3 = UDR0;
59
            crcValue = crctable[nextCrcIndex];
60
            break;
61
        case 5:
62
            //Entscheidender Vergleich: letztes Byte == crc-Wert?
63
            if(crcValue == UDR0)
64
            {
65
        //Bytes aufteilen
66
          /*
67
           * Empfangene Bytes:
68
           *  Byte 0: XXAAA
69
           *  Byte 1: AAABB
70
           *  Byte 2: BBBBC
71
           *  Byte 3: CCCCC
72
           *  Byte 4: DDDDD
73
           
74
           Legende:
75
            X: zum bestimmen, was für Daten empfangen wurden
76
            A: 6 Bit Daten 
77
            B: 6 Bit Daten                                                        
78
            C: 6 Bit Daten                                                        
79
            D: Parität/Prüfsumme
80
           */
81
        uint8_t tby1 = ((byte0 & 0x07) << 3) | ((byte1 & 0x1C) >> 2);
82
        uint8_t tby2 = ((byte1 & 0x03) << 4) | ((byte2 & 0x1E) >> 1);
83
        uint8_t tby3 = ((byte2 & 0x01) << 5) | byte3;
84
        if((byte0 & 0x18) == 0) //Es wurden bestimmte Daten empfangen
85
        {
86
          //ocrR, ocrG, ocrB: PWM-Compare-Values, für die Helligkeit
87
          //pwmtable: lookup-Table für logarythmische Helligkeitsausgabe (->Linear fürs Auge), enthält 64 Werte
88
          ocrR = pwmtable[tby1];
89
          ocrG = pwmtable[tby2];
90
          ocrB = pwmtable[tby3];
91
        }
92
        else if((byte0 & 0x18) == 8) //Es wurden andere Daten empfangen
93
        {
94
          ocrK = pwmtable[tby1];
95
          ocrW = pwmtable[tby2];
96
          pinB = tby3;
97
        }
98
            }
99
100
            //Kritische Variablen zurücksetzen
101
            incomingByteCounter = 0;
102
            crcValue = 0;
103
            break;
104
        default:
105
            break;
106
    }
107
108
    //Timinginterrupt wieder aktivieren
109
    sei();
110
}
111
112
113
114
/*
115
 * Timerinterrupt von Timer2
116
 * Timer2 wurde folgendermaßen initialisiert:
117
 * Timereinstellungen setzen: TCCR2A = (1<<COM2A0) | (1<<COM2A1) | (1<<COM2B0) | (1<<COM2B1) | (1<<WGM20);
118
  TCCR2A = 0xF1;
119
120
 * Prescaler auf 8 eingestellt (CS21): TCCR2B = (1<<CS21);
121
  TCCR2B = 0x02;
122
123
 * Interrupt für OCR2A aktivieren (für Timings, OCIE2A)
124
  TIMSK2 = 0x02;
125
126
 * Bei einer Baudrate von 4800 kommt alle 208µs ein bit, also kommt mein ganzes Datenpaket
127
 * (1*Start, 5*Daten, 1*Stop = 7 bit) schnellstens alle 1,45ms.
128
 *
129
 * Das AVR-Studio zeigt als Zeit zwischen dem Auslösen von zwei Interrupten eine Zeitspanne von ~4090µs bei 1MHz,
130
 * was auf 7,3728MHz hochgerechnet etwa 555µs entspricht, daher lasse ich den uartResetCounter bis 10 Zählen (->5,5ms)
131
 * Auf dasselbe Ergebnis komme ich auch, wenn ich manuell die Zeit berechne: 8*512/7232800 = 555µs
132
 * Erst wenn 5ms zwischen zwei Datenpaketen vergangen sind, werden die alten Daten resettet.
133
 */
134
ISR(TIMER2_COMPA_vect) //TIMER2 COMPA
135
{
136
  if(incomingByteCounter != 0) //Wird gerade etwas empfangen? Nur dann muss die Zeit gezählt werden
137
  {
138
    uartResetCounter++;  //Den Counter incrementieren
139
    if(uartResetCounter == 10)  //Wenn der counter bei 10 ist, alles Resetten.
140
            //Dann ist zu viel Zeit vom Empfang des letzten Bytes vergangen (~6ms),
141
            //sofern ein Interrupt nicht wärend der byte-Verarbeitung aufgetreten wäre.
142
            //Dann hat das nächste Byte noch länger Zeit.
143
    {
144
      incomingByteCounter = 0;
145
      uartResetCounter = 0;
146
      crcValue = 0;
147
    }
148
  }
149
}

Ich bin für jegliche Hilfe dankbar!

von Stefan E. (sternst)


Lesenswert?

1
    //TimingInterrupt deaktivieren
2
    cli();
3
...
4
    //Timinginterrupt wieder aktivieren
5
    sei();
Das ist Unsinn. Schmeiße das raus.

Dein eigentliches Problem ist aber, dass du versuchst, UDR0 in der ISR 
zweimal zu lesen. Das geht nicht, schließlich steckt hinter UDR0 ein 
FIFO.

Und so nebenbei: welchen Sinn macht es, eine Look-Up-Tabelle als 
volatile zu deklarieren?

von Kei N. (mckalle)


Lesenswert?

VIELEN DANK!! So ein blöder Fifo ;)

Das mit dem cli(); sei(); hatte ich irgendwo gelesen, dass man das bei 
Timern machen soll, wenn man Werte ändert, hatte ich zum ausprobieren 
noch drin.

>> Und so nebenbei: welchen Sinn macht es, eine Look-Up-Tabelle als
>> volatile zu deklarieren?
Ich dachte, als volatile soll man alles deklarieren, auf das man 
innerhalb eines Interrupts zugreift?

von Stefan E. (sternst)


Lesenswert?

Kei Ner schrieb:
> Das mit dem cli(); sei(); hatte ich irgendwo gelesen, dass man das bei
> Timern machen soll, wenn man Werte ändert, hatte ich zum ausprobieren
> noch drin.

Bei AVRs sind beim Ausführen der ISR die Interrupts sowieso 
abgeschaltet, damit sich Interrupts nicht gegenseitig unterbrechen.

Kei Ner schrieb:
> Ich dachte, als volatile soll man alles deklarieren, auf das man
> innerhalb eines Interrupts zugreift?

Nein. Du deklarierst alles als volatile, auf das sowohl im Interrupt, 
als auch im restlichen Code zugegriffen wird. Und das auch nur dann, 
wenn die Variable auch mal irgendwann irgendwo verändert wird.

von Kei N. (mckalle)


Lesenswert?

Stefan Ernst schrieb:
> Bei AVRs sind beim Ausführen der ISR die Interrupts sowieso
> abgeschaltet, damit sich Interrupts nicht gegenseitig unterbrechen.
Gut, zwei Zeilen Code gespart ;)


> Nein. Du deklarierst alles als volatile, auf das sowohl im Interrupt,
> als auch im restlichen Code zugegriffen wird. Und das auch nur dann,
> wenn die Variable auch mal irgendwann irgendwo verändert wird.
Ok. Werd ich mir merken :)

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.