Forum: Compiler & IDEs STM32-code behaves weird


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Shinner (Gast)


Lesenswert?

Hallo,

ich habe hier ein seltsames Problem mit STM32-Code innerhalb der 
STM32CubeIDE. Der Code selbst ist mit CubeMX erzeugt. Ein Beispiel für 
die Probleme:
1
if (bufferPos<bufferSize)
2
{
3
   if (HAL_UART_Transmit(&huart2,&feedback_buffer[bufferPos],1,0)==HAL_OK)
4
   {
5
      bufferPos++;
6
   }
7
}
8
else bufferPos=0;

HAL_UART_Transmit() ist eine von CubeMX erzeugte Funktion zum Senden von 
Daten via UART, die Verbindung zur MCU ist mit Hilfe eines Segger JLink 
hergestellt.

Mein Problem: wenn ich den Code laufen lasse oder über die Funktion 
HAL_UART_Transmit() hinweg steppe, dann liefert die einen Rückgabewert 
!=HAL_OK zurück, sie schlägt also fehl.

Wenn ich jetzt allerdings per Singlestepping in die Funktion hineingehe, 
um nachzusehen, wo das Problem liegt, dann klappt dort drin alles 
problemlos und es wird HAL_OK zurückgegeben.

Ähnlich seltsames Verhalten findet sich auch an anderen Stellen. An den 
Compileroptimierungen habe ich schon herumgedreht, das ändert nichts.

Irgend eine Idee, was hier schiefläuft?

von Tassilo H. (tassilo_h)


Lesenswert?

Der letzte Parameter (Timeout) ist 0. Damit wartet die Funktion nicht, 
bis der UART bereit ist. Das Senden des ersten Zeichens klappt, beim 
zweiten oder dritten Zeichen ist der UART-Sendebuffer noch voll und das 
Senden schlägt fehl. Wenn du im Einzelschritt durchgehst, ist genug Zeit 
zwischen den Aufrufen um das Zeichen zu senden.

von Shinner (Gast)


Lesenswert?

Die Idee ist ja auch, dass er eben nicht wartet - wenn der UART beim 
Aufruf schon bereit ist, soll gesendet werden, wenn nicht, eben erst 
beim nächsten Durchlauf...

von Tassilo H. (tassilo_h)


Lesenswert?

Wenn ich mir den Code von HAL_UART_Transmit ansehe
(kurz gegoogelt, 
https://www.disca.upv.es/aperles/arm_cortex_m3/llibre/st/STM32F439xx_User_Manual/stm32f4xx__hal__uart_8c_source.html 
)

dann macht der (bei Bufferlänge 1) im Wesentlichen:
1
Ist huart->gState == HAL_UART_STATE_READY? Wenn nicht, dann mit Fehler abbrechen
2
huart->gState == busy
3
warten mit dem Timeout, bis das TXE-Flag des UART (Transmitter empty) gesetzt wird. Wenn Timeout, dann mit Fehler abbrechen
4
Zeichen in UART schreiben
5
huart->gState == ready
6
fertig

Beim Wiederholten raschen Aufruf hintereinander mit Timeout 0 ist aber 
der Transmitter nicht empty, weil das vorhergehende Zeichen noch drin 
ist. Damit setzt die Funktion zwar huart->gState auf busy, aber nie mehr 
auf ready und alle künftigen Aufrufe schlagen fehl. Irgendwie nicht ganz 
ausgegoren, ich denke Timeout sollte mindestens die zu erwartende 
Übertragungszeit eines Zeichens sein.

von Guest (Gast)


Lesenswert?

Shinner schrieb:
> Die Idee ist ja auch, dass er eben nicht wartet - wenn der UART beim
> Aufruf schon bereit ist, soll gesendet werden, wenn nicht, eben erst
> beim nächsten Durchlauf...

HAL_UART_Transmit ist blocking und wird immer warten, bis sie fertig mit 
senden ist. Da Die Funktion davon ausgeht das sie immer fertig ist, wenn 
sie am Ende ankommt, geht sie natürlich beim Aufruf davon aus das etwas 
nicht stimmt wenn die Peripherie noch Busy ist oder wenn sie intern in 
einen Timeout läuft.

Wenn du nicht willst das die Funktion wartet nutze HAL_UART_Transmit_IT 
und mache das Handling selbst. Oder noch einfacher nutze einen DMA, dann 
musst du dich gar nicht mit den einzelnen Zeichen rumschlagen.

von Harry L. (mysth)


Lesenswert?

Guest schrieb:
> Oder noch einfacher nutze einen DMA, dann
> musst du dich gar nicht mit den einzelnen Zeichen rumschlagen.

DMA ist für UART maximal ungeeignet, da i.d.R. nicht klar ist, wie viele 
Zeichen noch folgen.

Sowas macht man via Interrupt - ohne Wenn und Aber!

von FOp (Gast)


Lesenswert?

Harry L. schrieb:
> DMA ist für UART maximal ungeeignet, da i.d.R. nicht klar ist, wie viele
> Zeichen noch folgen.

Ääääh : Er sendet ! Wenn jemand weiss, wie viele Zeichen gesendet 
werden, dann er.

Und übrigens : die DMA von STM32 beherrscht Ringpuffer.

von Harry L. (mysth)


Lesenswert?

FOp schrieb:
> Und übrigens : die DMA von STM32 beherrscht Ringpuffer.

Jaja...alle Jahre wieder kommt das Thema auf, und irgendwelche Noobs 
behaupten, daß das mit DMA ja alles sooo viel besser ginge - nur 
wirklich funktionierenden Code, der auch noch effizienter läuft als die 
Interrupt-Methode hat man nie gesehen....langweilt einfach nur noch.

Beim Senden grosser Datenblöcke "kann" man DMA nutzen, aber beim 
Empfangen? - besser nicht!

UART ist nun mal -aus Sicht eines modernen µC- so schnarchlahm, daß DMA 
praktisch keine messbaren Vorteile bietet; dafür aber im Handling 
erhebliche Nachteile hat.

von Kevin M. (arduinolover)


Lesenswert?

Harry L. schrieb:
> Beim Senden grosser Datenblöcke "kann" man DMA nutzen, aber beim
> Empfangen? - besser nicht!

Da outet sich jemand der nicht programmieren kann :)

von W.S. (Gast)


Lesenswert?

Harry L. schrieb:
> Sowas macht man via Interrupt - ohne Wenn und Aber!

Du rufst vergeblich - jedenfalls in diesem Forum.
Ich hab das den Leuten schon seit Urzeiten gesagt und auch mal einen 
passenden Lowlevel-Treiber für die STM32 gepostet. Es nützt nix. 
Allenfalls wird man deshalb angepöbelt. Die Leute, die hier nach Hilfe 
rufen, WOLLEN es genau so, wie sie es posten und wie es eben nicht geht.

W.S.

von W.S. (Gast)


Lesenswert?

Kevin M. schrieb:
> Da outet sich jemand der nicht programmieren kann

Wie gerade eben gesagt: jeder Hinweis darauf, wie es geht und was man 
lieber bleiben lassen sollte, wird mit Pöbeln beantwortet.

W.S.

von J. S. (jojos)


Lesenswert?

Es gibt nicht ‚den einen‘ low level Treiber und es geht auch mit DMA. 
Link mit Code und Beschreibung von TM habe ich schon mehrfach gepostet.

von jo mei (Gast)


Lesenswert?

UART Daten mit DMA empfangen ist maximal umständlich und bringt
keinen Vorteil. Die Daten müssen ja sowieso "geholt" werden. Ob
aus dem DMA Buffer oder aus dem UART-Register ist wohl nicht
kriegsentscheidend. Dabei dürfte der Verwaltungsaufwand bei
DMA sogar noch grösser sein als das Interrupt-Handling beim
normalen Empfangen. DMA bringt beim Empfangen schon deswegen
nichts weil die Daten meist deutlich langsamer daherkommen als
der Controller fähig ist Daten zu verarbeiten.

von jo mei (Gast)


Lesenswert?

Shinner schrieb:
> Ein Beispiel für die Probleme:

Wir haben dich ja schon an anderer Stelle mit skurrilen
Problemen und Beratungsresistenz bzw. Faulheit erlebt.
Vielleicht geht das jetzt hier weiter .... wenn ich schon
von den Problemen höre. Ein Beispiel, es kommen noch
viele solche?

(Beitrag "STM32 empfängt keine SPDIF-Daten")

von (Gast)


Lesenswert?

Bei schnellen Datenraten und mehreren UARTs gleichzeitig kanns mit 
Interrupt schon eng werden. Empfang per DMA in einen Ringpuffer ist kein 
Problem, und Senden der Antwort per DMA ist auch "fire and forget", da 
gibts nicht viel zu verwalten. UART kann bei Empfangspause auch 
Interrupt auslösen wenn man darauf reagieren will/muss. Spart jede Menge 
an ISR enter/exit, das ist nicht gratis. Netto hat man mehr CPU Zeit für 
andere Dinge zur Verfügung.

von Max G. (l0wside) Benutzerseite


Lesenswert?

Du wirst doch nicht W.S. kritisieren wollen, der alleine weiß, wie es 
geht.

Mir fielen diverse Varianten ein, wie man DMA auch beim Empfangen per 
UART nutzen kann. Das kann mal sinnvoll sein und mal nicht. Aber wie 
gesagt, es gibt Forenteilnehmer, die einfach immer Recht haben. W.S. 
gehört dazu.

von Steve van de Grens (roehrmond)


Lesenswert?

Shinner schrieb:
> Die Idee ist ja auch, dass er eben nicht wartet - wenn der UART beim
> Aufruf schon bereit ist, soll gesendet werden, wenn nicht, eben erst
> beim nächsten Durchlauf...

Dann ist de Fehlercode ja von dir gewollt.

von Andreas B. (myratz)


Lesenswert?

> DMA ist für UART maximal ungeeignet, da i.d.R. nicht klar ist, wie viele
> Zeichen noch folgen.
>
> Sowas macht man via Interrupt - ohne Wenn und Aber!

Entschuldigung aber das ist Unsinn. Höhere Baudraten gehen ohne DMA gar 
nicht vernünftig, außer man macht mit der MCU nichts anderes.

Man muss allerdings wissen wie das mit dem DMA funktioniert. Der DMA 
macht den Transfer vom RAM zum Peripheral, d.h. wenn der DMA Done 
interrupt kommt, ist er mit dem Transfer fertig, das heißt nicht dass 
der UART fertig ist mit dem Senden.

Damit das sauber funktioniert muss in der DMA Done ISR gecheckt werden, 
ob der UART mit dem Senden fertig ist, wenn nicht muss der TXC Interrupt 
enabled werden und erst wenn der fertig gekommen ist, ist die 
Übertragung beendet.

Bei dem STM Microcontroller den ich verwendet haben (STM32WLE) kommt 
noch dazu, dass er nach dem Aufwachen aus dem Step2 Mode seine 
Peripheral Registerwerte für den UART und den DMA „vergessen“ hat und 
diese nach dem wake-up wieder hergestellt werden müssen.

Gruß
Andreas

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.