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
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.
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.
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.
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 :)
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
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.
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)...
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?
> 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:
// 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
elseif(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)
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.
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! :)