OK, die Überschrift klingt etwas wirr. Ich möchte von einem STM32 über
einen UART mit einem ESP8266 über AT-Befehle kommunizieren.
Wenn ich einen AT-Befehl gesendet habe, möchte ich wissen, ob die
Antwort OK oder ERROR ist. Da die Antwort keine feste Länge hat, hole
ich nacheinander Bytes und schaue, ob das Ende aus der gesuchten Sequenz
(z.B. "OK") besteht:
1
uint8_t*p=buffer;
2
intfound=0,count=0;
3
4
while(1){
5
if(HAL_UART_Receive(&huart7,p++,1,1000))
6
break;
7
if(++count>=2&&!strncmp(p-2,"OK",2)){
8
found=1;
9
break;
10
}
11
}
12
dlog(buffer);// log on other serial connection
13
if(found)
14
dlog("FOUND");
15
else
16
dlog("ABORT");
Falls die Schleife irgendwann mit Timeout abbricht, war die Sequenz
nicht enthalten.
Leider funktioniert das nicht so, wie es soll. Hier ist das Log:
1
AT check ... <-- meine Meldung, nach Abseten von "AT"
2
T <-- ESP über dlog(buffer), Anfang verpasst
3
<-- ESP
4
OK <-- ESP
5
FOUND <-- meine Meldung
6
set station ... <-- meine Meldung, nach Absetzen von "AT+CWMODE=1"
7
<-- ESP
8
<-- ESP
9
OK <-- ESP
10
ABORT <-- meine Meldung
Wieso wird das letzte OK nicht erkannt? Verhält sich der
HAL_UART_Receive anders als ich denke?
Ein erster Schritt zum Erfolg könnte daraus bestehen den
RX-Complete-Interrupt des verwendeten Mikrocontrollers zu verwenden um
einen Empfangspuffer zu füllen ohne etwas zu verpassen.
Wenn ich mich nicht irre schickt der ESP am Ende immer ein 0Ah,0Dh.
Diese Zeilenenden musst Du abfangen.
Also:
im RX interrupt immer byteweise in ein buffer schreiben und schauen ob
der aktuelle und der letzte byte ein 0Dh und ein 0Ah ist.
Achtung: Du benutzt diese s...ß HAL Treiber, ein RX interrupt wird hier
nur ausgelöst, wenn der angegeben buffer mit seiner angegeben länge
gefüllt ist. Daher die länge immer mit 1 angeben, aber das tust Du
schon.
Stefan P. schrieb:> Ein erster Schritt zum Erfolg könnte daraus bestehen den> RX-Complete-Interrupt des verwendeten Mikrocontrollers zu verwenden um> einen Empfangspuffer zu füllen ohne etwas zu verpassen.
Ich hatte es mit Interrupts versucht, aber dann muß ich mit der HAL eben
1
HAL_UART_Receive_IT(&huart7,p++,1)
schreiben. Der Interrupt wird bei Erreichen der angeforderten
Zeichenzahl ausgelöst und nicht, wenn der UART einige Zeit keine Zeichen
mehr empfangen hat.
Wie sollte das helfen? Der Nachteil ist ja sogar, dass es kein Timeout
mehr gibt.
Bülent C. schrieb:> im RX interrupt immer byteweise in ein buffer schreiben und schauen ob> der aktuelle und der letzte byte ein 0Dh und ein 0Ah ist.> Achtung: Du benutzt diese s...ß HAL Treiber, ein RX interrupt wird hier> nur ausgelöst, wenn der angegeben buffer mit seiner angegeben länge> gefüllt ist. Daher die länge immer mit 1 angeben, aber das tust Du> schon.
Aber dann müßte doch auch mein Code funktionieren, indem ich direkt nach
"OK" suche, oder?
(Nebenbei: Es ist DMA konfiguriert, aber für diesen Abschnitt verwende
ich Polling.)
lorns schrieb:> Bülent C. schrieb:> im RX interrupt immer byteweise in ein buffer schreiben und schauen ob> der aktuelle und der letzte byte ein 0Dh und ein 0Ah ist.> Achtung: Du benutzt diese s...ß HAL Treiber, ein RX interrupt wird hier> nur ausgelöst, wenn der angegeben buffer mit seiner angegeben länge> gefüllt ist. Daher die länge immer mit 1 angeben, aber das tust Du> schon.>> Aber dann müßte doch auch mein Code funktionieren, indem ich direkt nach> "OK" suche, oder?>> (Nebenbei: Es ist DMA konfiguriert, aber für diesen Abschnitt verwende> ich Polling.)
Dma macht man nur bei festen längen. Das ist hier nicht der Fall.
Du musst das Zeilenende abfangen, aber das schrieb ich ja oben bereits.
Bülent C. schrieb:> Du musst das Zeilenende abfangen, aber das schrieb ich ja oben bereits.
Es gibt viele Typen von Antworten, wo der ESP mehrere Zeilen als eine
Antwort schickt, z.B. die Liste der verfügbaren APs. Welches Zeilenende
genau soll der TO abfangen?
Als ich damals einen Parser für die ESP-AT-Schnittstelle schrieb, musste
ich frustriert feststellen, dass je nach Firmware-Version die Antworten
des ESP vollkommen anders strukturiert waren und das
Firmware-übergreifend nicht mehr in den Griff zu bekommen war.
Also hab ich eine eigene Firmware für den ESP geschrieben. Seitdem
klappt das reibungslos mit der Aufgabenteilung zwischen STM32 und
ESP8266, siehe auch:
WordClock mit WS2812
Bülent C. schrieb:> Dma macht man nur bei festen längen. Das ist hier nicht der Fall.
Das habe ich oben geschrieben.
> Du musst das Zeilenende abfangen, aber das schrieb ich ja oben bereits.
Ja, kann man machen, aber ich bin nur daran interessiert, ob irgendwo
OK vorkommt. Im ersten Beispiel funktioniert es, im zweiten nicht -
wieso?
Sähe Dein Code denn großartig anders aus? Du würdest die Zeichen doch
auch einzeln vom UART abholen?
lorns schrieb:> Bülent C. schrieb:>> Dma macht man nur bei festen längen. Das ist hier nicht der Fall.>> Das habe ich oben geschrieben.>>> Du musst das Zeilenende abfangen, aber das schrieb ich ja oben bereits.>> Ja, kann man machen, aber ich bin nur daran interessiert, ob irgendwo> OK vorkommt. Im ersten Beispiel funktioniert es, im zweiten nicht -> wieso?>> Sähe Dein Code denn großartig anders aus? Du würdest die Zeichen doch> auch einzeln vom UART abholen?
Ja, aber ich würde sie alle erstmal in ein Buffer tun, und dies solange
bis ich ein Zeilenende sehe, und erst dann schaue ich nach was drinne
ist.
Dir ist schon bewußt, dass deine Routine bei konstantem Datenstrom ohne
erkanntes OK irgendwann wild im Speicher rumschreibt? Ab wann was
passiert, hängt von der Größe von buffer ab.
OK, nach vielen, vielen Versuchen, hier endlich die Lösung:
1
HAL_Receive_DMA();
2
HAL_Transmit_DMA();
3
findOK(timeout);
4
HAL_UART_DMAStop();
5
// ...
6
HAL_UART_DMAResume();
7
HAL_Receive_DMA();
8
HAL_Transmit_DMA();
9
findOK(timeout);
10
HAL_UART_DMAStop();
11
// ...
HAL_DMA_Abort(), oder einfach einen neuen DMA-Request ohne Resume
abzusetzen funktioniert bei mir NICHT.
Wäre ich nie draufgekommen, wenn ich nicht zufällig den Code irgendwo
gesehen hätte.