Forum: Mikrocontroller und Digitale Elektronik Mit STM32 Byte-Sequenz aus UART-Strom herausfischen?


von lorns (Gast)


Lesenswert?

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
int found = 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?

von Stefan P. (form)


Lesenswert?

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.

von Bülent C. (mirki)


Lesenswert?

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.

von lorns (Gast)


Lesenswert?

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.

von Curby23523 N. (Gast)


Lesenswert?

Bülent C. schrieb:
> Achtung: Du benutzt diese s...ß HAL Treiber

So siehts aus ;).

von lorns (Gast)


Lesenswert?

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.)

von STM32 User (Gast)


Lesenswert?

strncmp  verlangt 0-terminierte Strings.

von Auch Einer (Gast)


Lesenswert?

STM32 User schrieb im Beitrag #5426352:
> strncmp  verlangt 0-terminierte Strings.

Käse.

Einfach mal eine C Referenz lesen.

von Bülent C. (mirki)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

von lorns (Gast)


Lesenswert?

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?

von Bülent C. (mirki)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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.

von lorns (Gast)


Lesenswert?

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.

von lorns (Gast)


Lesenswert?

Funktioniert doch nicht, der Code liest immer wieder das erste OK.

Ich suche also immer noch ein funktionierendes Beispiel.

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.