Forum: Compiler & IDEs Uart Rx Interrupt und Weiterverarbeitung der Daten = Salat


von Danny (Gast)


Angehängte Dateien:

Lesenswert?

Hi zusammen!

Ich möchte eine UART-Kommunikation mit einem Handy aufbauen. Ich 
empfange die Handydaten per UartInterrupt und brüte seit vier Tagen über 
einen wahrscheinlich total dummen Fehler...

Ihr kennt es sicher, je länger man grübelt, desto blinder wird man...

Ich habe mein Code mal auf ein übersichtliches Maß reduziert und im 
angehängten File direkt das Problem beschrieben...

Würd mich sehr über nen Schupps in die richtige Richtung freuen...

Danke und Gruß
Danny

von Karl H. (kbuchegg)


Lesenswert?

Du gehst mir für meine Begriffe etwas zu sorglos mit deinem uart read 
Index um. Als Ausgleich dafür hast du viel zu viele fixe Annahmen über 
Stringlängen drinnen.

Ganz zum Schluss, die do-while Schleife sollte eine while Schleife sein, 
sonst passiert es dir, dass du eine Message komplett überliest.

Teile die Dinge auf:
Erst kopierst du dir eine komplette(!) Message aus dem UART Buffer in 
eine String Variable um. Also ALLES vom Beginn der Message bis zum Ende. 
Musst du sowieso, wenn dein Ringbuffer erst einmal zum Ring geschlossen 
wurde.

Und erst dann zerlegst du diese Message in ihre Einzelteile. Keine 
Annahmen über Argumentlängen. Sowas ist immer schlecht. Zb zeigt dein 
Beispiel die 2-te Zahl 2 stellig, dein Code wertet sie aber 3-stellig 
aus. Zerlege die Message anhand der dir bekannten Trennzeichen in 
Einzelteile und werte die dann entsprechend aus. Um einen Zahlen-String 
in eine Zahl umzuformen kannst du zb atoi nehmen oder strtol und bist so 
von Zeichenzahlen unabhängig.

Auf lange Sicht bringt dieses Message-Zerlegen auf Einzelzeichenbasis 
nix ausser Ärger.

---
Code wird nicht übersichtlicher, wenn jede 2.te Zeile eine Leerzeile 
ist. Er wird nur in die Länge gezogen. Ditto für viele Leerzeilen 
hintereinander. 1 reicht vollkommen um anzuzeigen, dass ein neuer 
Gedanke, ein neuer Codeblock beginnt.

von Karl H. (kbuchegg)


Lesenswert?

In

volatile char kommando[5];

kannst du keinen String mit strlen(String)==5 unterbringen ("+CBC:"). 
Für einen String mit diesen Zeichen brauchst du ein Array der Länge 6, 
weil ja hinten noch das \0 Zeichen drann kommt.

von Karl H. (kbuchegg)


Lesenswert?

Selbiges hier

  char kapaz[3];


    kapaz[0] = uart_rx_buffer[uart_rx_read++];
    kapaz[1] = uart_rx_buffer[uart_rx_read++];
    kapaz[2] = uart_rx_buffer[uart_rx_read++];
    kapaz[3] = '\0';



ein kapaz[3] existiert schlicht und ergreifend nicht. Ein Array der 
Größe 3 hat die Elemente 0, 1, 2. Zähl nach, sind genau 3 Stück. Einen 
Index 3 hingegen kann es dann nicht mehr geben. Dazu müsste das Array 
die Länge 4 haben.

von Chris G. (Gast)


Lesenswert?

nun bin ich verblüfft :)

Vielen Dank Karl Heinz für deine schnelle Hilfe zu dieser Uhrzeit!

Ich wollte die komplette Nachricht im FIFO belassen und von dort aus 
zerlegen, weil (ja zugegeben nur) im Normalfall nur das vom Handy 
gesendet wird was ich vorher definitiv anfordere. Somit habe ich die 
Kontrolle darüber. Die längste Nachricht kann 200 Zeichen lang sein und 
somit müsste ich den FIFO und zusätzlich 200 Byte RAM zur Verfügung 
stellen, das wollte ich mir sparen...

Sollte ich wirklich "nur" auf den dummen Stringlänge + \0 reingefallen 
sein? Das werd ich sofort testen... also nur testen ob der Fehler dann 
weg ist, denn Recht hast du und das war schonmal ein grosser Baum der 
den Blick auf den Wald verdeckt hat :)

von Danny (Gast)


Lesenswert?

mh, schade... an den "zu kurzen" Arrays lags nicht...

noch ein Satz zu der zweistellig dargestellten, aber dreistellig 
ausgewerteten Zahl. Das ist nötig weil dort auch eine 100 stehen kann. 
Die von mir gemachte Wandlung mit atoi() hat in diesem Fall fehlerfrei 
funktioniert, daher habe ich es so gelassen...

dann werde ich nun Deinen Rat befolgen und komplette Nachrichten laden, 
ich schätze ich melde mich morgen früh nochmal :)

erst einmal vielen Dank und eine angenehme Nacht

von Karl H. (kbuchegg)


Lesenswert?

Danny schrieb:
> mh, schade... an den "zu kurzen" Arrays lags nicht...

nimm noch die fehlerhafte do-while Schleife mit dazu. IMHO läuft dir da 
der read Index zu weit.


Tip:
do-while Schleifen sind in C relativ selten. Die überwiegende Mehrheit 
sind normale while Schleifen. Deine letzte sollte nicht so lauten
1
    do{
2
      uart_rx_read++;
3
    
4
    }while(uart_rx_buffer[uart_rx_read] != '\0');
5
6
    uart_rx_read++;

sondern so
1
    // bis zum \0 vortasten
2
    while(uart_rx_buffer[uart_rx_read] != '\0') {
3
      uart_rx_read++;
4
    }
5
    // und über den \0 drüber
6
    uart_rx_read++;

Überleg dir einfach, was der Unterschied ist, wenn der Code an diese 
Stelle kommt, und uart_rx_buffer[uart_rx_read] bereits ein \0 Zeichen 
ist (weil du alle Zeichen die zur Message gehören schon sauber 
aufgearbeitet hast).

Deine restlichen uart_rx_read Erhöhungen im Code sind alle nicht 
wirklich nachvollziehbar. Ich kann beim besten Willen nicht sagen ob das 
alles koscher ist. (Unter anderem auch durch den unübersichtlichen 
Stil). Ich denke aber, du hast dich da kräftig verfranst. Nimm dir einen 
Zettel und spiel (du als Computer) das alles mal durch. ZU dieser Stunde 
mach ich auch schon Fehler, aber wenn ich das im Kopf durchspiele (mit 
deiner doumentierten Eingabe) dann greifst du da auf völlig falsche 
Bytes zu.

von Danny (Gast)


Lesenswert?

fall Du diese Erhöhung mit evt nicht koscher meinst:

// und über den \0 drüber
    uart_rx_read++;

Mein Ziel war: \0 ist erreicht, Schleife bricht ab - und dann muss ich 
den "Zeiger" auf den nächsten Beginn / nächste gültige Stelle erhöhen um 
ab dort wieder von neuem zu lesen (und das getriggert durch mein 
uart_rx_stack)...

von Danny (Gast)


Lesenswert?

Karl Heinz, noch ein zum grübeln :)

wenn mein Programm aus einem definitiv im uart_rx_puffer liegenden 
"+CBC: 0,80" zeitweise sogar ein "+BC: " macht, so glaube ich nicht an 
überlaufende Zeiger oder "ein zu weit schalten". Dann wäre es evt um 
eine Stelle verschoben, oder totaler Müll. Aber hier fehlt ja definitiv 
ein Zeichen, als wäre es rausgelöscht.

Kannst Du Dir irgendeine wie auch geartete Interrupt-Konstellation 
vorstellen die so etwas mit sich bringen kann?

von Karl H. (kbuchegg)


Lesenswert?

> im Normalfall nur das vom Handy gesendet wird was ich vorher
> definitiv anfordere. Somit habe ich die Kontrolle darüber.

OK.

NIchts desto trotz sind da ein paar Hilfsfunktionen angesagt.
So was
1
    if(uart_rx_buffer[++uart_rx_read] == '1'){
2
      
3
      handy.laedt = 1;
4
    }
5
    else{
6
7
      handy.laedt = 0;
8
    }
9
    
10
    uart_rx_read += 2;
11
12
    kapaz[0] = uart_rx_buffer[uart_rx_read++];
13
    kapaz[1] = uart_rx_buffer[uart_rx_read++];
14
    kapaz[2] = uart_rx_buffer[uart_rx_read++];
15
    kapaz[3] = '\0';

ist ausser fehleranfällig nur noch fehleranfällig.

Der Code könnte zb so aussehen:
1
    char kommando[6];
2
3
    for( i = 0; i < 5 && uart_rx_buffer[uart_rx_read] != '\0'; ++i )
4
      kommando[i] = uart_rx_buffer[uart_rx_read++];
5
6
    kommando[i] = '\0';
7
  
8
    strcat(uart_tx_buffer, kommando);  
9
10
    // an dieser Stelle
11
    // uart_rx_read ist der Index des Zeichens nach dem Kommando
12
    // oder "zeigt" auf das abschliessende \0
13
14
    if( strcmp( kommando, "+CBC:" ) ) {
15
      // den ' ' überlesen, falls er da ist
16
      if( uart_rx_buffer[uart_rx_read] == ' ' )
17
        uart_rx_read++;
18
19
      lade = read_int();
20
21
      // den ',' überlesen, falls er da ist
22
      if( uart_rx_buffer[uart_rx_read] == ',' )
23
        uart_rx_read++;
24
      handy_kapazitaet = read_int();
25
26
      // an dieser Stelle ist das Kommando zu Ende. Wenn alles
27
      // richtig ist, dann MUSS uart_rx_buffer[uart_rx_read] ein \0
28
      // Zeichen sein. Das testen wir besser mal
29
      if( uart_rx_buffer[uart_rx_read] == '\0' ) {
30
        if( lade )      
31
          handy.laedt = 1;
32
        else
33
          handy.laedt = 0;
34
    
35
        if(handy_kapazitaet < 60) {
36
          laden_ein
37
          led_1_ein
38
        }
39
        else if(handy_kapazitaet > 90) {
40
          laden_aus
41
          led_1_aus
42
        }
43
      }
44
    }
45
46
47
  ....
48
    // bis zum \0 vortasten
49
    while(uart_rx_buffer[uart_rx_read] != '\0') {
50
      uart_rx_read++;
51
    }
52
    // und über den \0 drüber auf den Anfang des nächsten Kommandos
53
    uart_rx_read++;


Das ist die Zielvorstellung. Damit das so geht, wie muss die Funktion 
read_int() aussehen? Was muss sie tun?
So muss ein Zeichen nach dem anderen vom UART Buffer lesen und zu einer 
Zahl zusammenfügen. uart_rx_read muss dabei auf den Anfang der Zahl 
verweisen und es werden so lange Ziffern zur Zahl 'hinzugefügt', solange 
im Text Ziffern vorkommen. Mit dem ersten Nicht-Ziffern Zeichen ist die 
Sache vorbei und uart_rx_read soll dann der Index des ersten Zeichens 
nach der Zahl sein. Steht überhaupt keien Zahl im Text, dann ist das 
Ergebnis 0 und uart_rx_read verändert seinen Wert überhaupt nicht (damit 
ein ev. \0 Zeichen nicht übersprungen wird)
1
int read_int( void )
2
{
3
  int result = 0;
4
5
  while( uart_rx_buffer[uart_rx_read] >= '0' &&
6
         uart_rx_buffer[uart_rx_read] <= '9' )
7
    result = 10 * result + (uart_rx_buffer[uart_rx_read++] - '0');
8
9
  return result;
10
}

von Karl H. (kbuchegg)


Lesenswert?

Edit: Es ist jetzt 01:21
Ich hab im Code noch ein paar Nachbesserungen gemacht, die kritisch 
sind. Wenn du dir vorher was davon geholt hast, hol ihn dir nochmal.

von Danny (Gast)


Lesenswert?

habe ich eben schon gesehen :)   vielen Dank


und soll ich Dir was sagen? Da Du mich nun mit der Nase draufgedrückt 
hast, habe ich nun definitv beide Fehlerstellen gefunden :)

Die wars:

kapaz[0] = uart_rx_buffer[uart_rx_read++];
kapaz[1] = uart_rx_buffer[uart_rx_read++];
kapaz[2] = uart_rx_buffer[uart_rx_read++];
kapaz[3] = '\0';

Ich habe nur an die Auswertung gedacht, die mit atoi() fehlerfrei war. 
Ausserdem habe ich gedacht, bei einer zweistelligen Zahl wäre es 
problemlos, es folgt schließlich ein \0 und damit ist mein Array ja 
terminiert. Das ich in dem Fall aber den uart_rx_read einmal zu viel 
inkrementiere hab ich nicht bedacht...


Die Stelle habe ich schnell testweise angepasst und freute mich, aber 
noch ein wenig zu früh, denn genau alle 16 Übertragungen hatte ich 
reproduzierbar wieder einen Fehler...

Und siehe da.... guck mal hier:

volatile char uart_rx_buffer[255] ="";
volatile uint8_t uart_rx_read =0;
volatile uint8_t uart_rx_write =0;
volatile uint8_t uart_rx_stack=0;

Zeiger gibt 256 "Möglichkeiten", Array bietet aber nur 255...


Ich glaub ich kann heut echt glücklich ins Bett.

Karl Heinz, vielen Dank für deine Hilfe mitten in der Nacht, ich werde 
deinen Rat befolgen und mein Programm entsprechend deiner Vorschläge 
anpassen - aber erst morgen :)

Angenehme Nacht! :)

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.