Forum: Mikrocontroller und Digitale Elektronik STM32 UART und DMA


von Mat. K. (matthias_kornfield)


Lesenswert?

Hi
gibt es für dieses Problem eine Standard Lösung:

2 STM32 Boards kommunizieren über UART. Es geht um eine CMD/response 
Schnittstelle. CMD und Responses haben die gleiche Länge. Ein DMA 
transferiert genau ein CMD oder Response.

Sollte warum auch immer ein Byte zu wenig übertragen werden, 
transferiert ja ein DMA nicht mehr ein ganzes CMD/Response....
CMD/responses haben CRC und Delimitter. Das Problem um den es geht ist: 
alle folgenden CMD/Responses sind nicht mehr DMA aligned.
Wie wird das typischerweise verhindert?
Danke

von Cyblord -. (cyblord)


Lesenswert?

Mat. K. schrieb:
> Wie wird das typischerweise verhindert?

UART ohne DMA machen. Es müssen schon sehr viele Daten erwartet werden 
damit man mit DMA irgendwas gewinnt.

von Mat. K. (matthias_kornfield)


Lesenswert?

> UART ohne DMA machen. Es müssen schon sehr viele Daten erwartet werden
> damit man mit DMA irgendwas gewinnt.
Leider brauche ich den DMA aus performance Gründen.
Das ist leider keine Option.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Mat. K. schrieb:
> Sollte warum auch immer ein Byte zu wenig übertragen werden,

Per IDLE Interrupt kann der UART signalisieren dass vorzeitig Ende ist. 
Und einen Interrupt für Errors gibt es auch. Da kann man dann den DMA 
abwürgen/neu starten.

Weil der Sender ja auch DMA nutzt sollte es keine Lücken zwischen den 
einzelnen Bytes geben, sodass der IDLE Interrupt nicht versehentlich 
kommt.

Cyblord -. schrieb:
> Es müssen schon sehr viele Daten erwartet werden damit man mit DMA
> irgendwas gewinnt.

Der Overhead für ständiges Betreten/Verlassen der Interrupts kann ein 
Echtzeitverhalten durcheinander bringen und außerdem den 
Energieverbrauch nach oben treiben.

von Wastl (hartundweichware)


Lesenswert?

Cyblord -. schrieb:
> Es müssen schon sehr viele Daten erwartet werden
> damit man mit DMA irgendwas gewinnt.

Bei Transmit gewinnt man sehr wohl eine Menge. Denn anstatt
in der Transmit-Loop zu warten bis das Transmit-Register
wieder frei ist (Baudrate ist so niedrig dass sich der
Controller "langweilt") um das nächste Datum zu schreiben
kann man viele andere Dinge machen. Bei 921600 Baud (z.B.)
braucht ein Byte ca. 10 usec, da kann ein STM32 zwischendurch
sehr viel erledigen.

Bei Receive ohne DMA bekommt man einen Interrupt der sehr
schnell abgearbeitet ist, also kein spürbarer Zeitgewinn
mit DMA.

von Cyblord -. (cyblord)


Lesenswert?

Wastl schrieb:

> Bei Transmit gewinnt man sehr wohl eine Menge. Denn anstatt
> in der Transmit-Loop zu warten bis das Transmit-Register
> wieder frei ist (Baudrate ist so niedrig dass sich der
> Controller "langweilt") um das nächste Datum zu schreiben
> kann man viele andere Dinge machen. Bei 921600 Baud (z.B.)
> braucht ein Byte ca. 10 usec, da kann ein STM32 zwischendurch
> sehr viel erledigen.

Das geht mit Interrupts genau so, da brauchts keinen DMA.

Mit DMA spart man sich lediglich das schreiben des Bytes in den TX 
Register.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Mat. K. schrieb:
> Leider brauche ich den DMA aus performance Gründen.

Das würde ich gerne erklärt bekommen, denn das verstehe
ich nicht. Vielleicht hast du einen Sonderfall den ich bisher
nicht im Blickfeld hatte. Jedenfalls ist serielle Übertragung
im Bereich unter 1 MBit/sec arschlangsam im Verhältnis zu dem
was ein STM32 zu leisten vermag.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Cyblord -. schrieb:
> Mit DMA spart man sich lediglich das schreiben des Bytes in den TX
> Register.

Und den Overhead für den Interrupt. Der ist viel höher als der 
Registerzugriff. Warum sonst unterstützt der UART überhaupt DMA...?

von Cyblord -. (cyblord)


Lesenswert?

Wastl schrieb:
> Das würde ich gerne erklärt bekommen

Ich auch.
Von wie vielen Bytes reden wir.

von Cyblord -. (cyblord)


Lesenswert?

Niklas G. schrieb:

> Der Overhead für ständiges Betreten/Verlassen der Interrupts kann ein
> Echtzeitverhalten durcheinander bringen und außerdem den
> Energieverbrauch nach oben treiben.

KANN, aber der DMA hat eben auch Nachteile.

Und nicht vergessen: Der DMA teilt sich den gleichen Datenbus mit allen 
anderen Komponenten des Controllers beim STM32. Es wird nur die CPU 
nicht belastet. Der Bus kann aber blockieren.

von Wastl (hartundweichware)


Lesenswert?

Cyblord -. schrieb:
> Der Bus kann aber blockieren.

Tut er aber nicht da er zwischendrin immer wieder den Core
zum Zuge kommen lässt. So eine STM32 Hardware arbeitet
"interleaved", wie der Franzose zu sagen pflegt.

von Cyblord -. (cyblord)


Lesenswert?

Wastl schrieb:
> Cyblord -. schrieb:
>> Der Bus kann aber blockieren.
>
> Tut er aber nicht da er zwischendrin immer wieder den Core
> zum Zuge kommen lässt. So eine STM32 Hardware arbeitet
> "interleaved", wie der Franzose zu sagen pflegt.

Genau das meine ich. Er muss zwischendrin eben auch warten und läuft 
eben nicht, wie manche erwarten, völlig unabhängig vom Rest des 
Controllers.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Cyblord -. schrieb:
> KANN, aber der DMA hat eben auch Nachteile.

Ja, aber meistens kann man damit trotzdem gut arbeiten. Ich benutze den 
DMA oft mit dem UART ohne Probleme. Sogar bei Paketen mit variabler 
Länge. Ich würde jetzt nicht auf die Idee kommen das wieder zu lassen.

Cyblord -. schrieb:
> Der Bus kann aber blockieren.

Nur wenn der DMA-Controller wirklich bei jedem einzelnen Takt einen 
Zugriff macht, was beim UART absolut nicht der Fall ist, da wird die CPU 
nur bei jedem Byte mal für wenige Takte (je nach Busgeschwindigkeit) 
"blockiert", falls sie gerade überhaupt einen Zugriff macht - das tut 
sie aber auch nicht in jedem Takt. Die Instruktionen kommen meist direkt 
über einen dedizierten Bus vom Flash. Die CPU würde viel mehr Takte 
verschwenden wenn man es manuell im Interrupt macht.

Die einzigen kniffligen Probleme beim DMA treten bei CPUs mit Cache auf 
(Cortex-M7, Cortex-A) wo man Cache Maintenance Operations zwischen 
DMA-und CPU-Zugriff machen muss (außer bei SoCs wo der DMA selbst auf 
den Cache zugreifen kann). Aber auch das ist ein gelöstes Problem.

PS: Bei Low-Power-Anwendungen kann es interessant sein den Prozessor 
zwischenzeitlich auf einen sehr niedrigen Takt zu stellen und dann 
entweder das Voltage Scaling oder LPRun/LPSleep-Mode (nutzt den 
LowPower-Regulator) zu aktivieren, und ggf. noch zusätzlich den Flash 
abzuschalten (außer man sendet konstante Daten vom Flash). Der DMA läuft 
im (LP)Sleep-Mode noch und kann dann Daten senden/empfangen, aber der 
Gesamt-Verbrauch reduziert sich stark. Prinzipiell geht das auch wenn 
man Interrupts nutzt, aber das ständige Aufwachen verbraucht halt 
definitiv mehr.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Niklas G. schrieb:
> Nur wenn der DMA-Controller wirklich bei jedem einzelnen Takt einen
> Zugriff macht, was beim UART absolut nicht der Fall ist, da wird die CPU
> nur bei jedem Byte mal für wenige Takte (je nach Busgeschwindigkeit)
> "blockiert", falls sie gerade überhaupt einen Zugriff macht - das tut
> sie aber auch nicht in jedem Takt. Die Instruktionen kommen meist direkt
> über einen dedizierten Bus vom Flash. Die CPU würde viel mehr Takte
> verschwenden wenn man es manuell im Interrupt macht.

Na das halte ich doch für eine Beschreibung die sich manche
Leute mal richtig gut merken sollten.

Ich habe es schon geschafft einen String in einem Buffer zu
überschreiben während der vorherigen String (im gleichen
Buffer) noch über DMA gesendet wurde. Man hält das für einen
"Fehler" des Controllers wenn man sich den oben zitierten
Absatz nicht zu Herzen nimmt. Dabei ist es eben nur das
implementierte Interleaving sowie die Arbeitsgeschwindigkeit
des Cores die das möglich machen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wastl schrieb:
> Ich habe es schon geschafft einen String in einem Buffer zu
> überschreiben während der vorherigen String (im gleichen
> Buffer) noch über DMA gesendet wurde

Hmm? Hast du nicht auf den DMA Interrupt gewartet dass der Transfer 
fertig ist?

von Wastl (hartundweichware)


Lesenswert?

Niklas G. schrieb:
> Hmm? Hast du nicht auf den DMA Interrupt gewartet dass der Transfer
> fertig ist?

Als mir das schliesslich klar wurde: ja. Die Erklärung dazu
hatte ich ja bereits vorher geliefert.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wastl schrieb:
> Als mir das schliesslich klar wurde: ja

Hmm, also es sollte doch sowieso klar sein dass der UART viel langsamer 
als der Prozessor-Bus ist. Das DMA kann nur neue Bytes zum UART 
schaufeln wenn der Puffer frei ist, also nur alle paar Hundert/Tausend 
Takte. Das hat mit dem DMA eigentlich nicht viel zu tun, sondern liegt 
einfach am UART. Höchstens wenn man einen Memory-To-Memory Transfer 
macht kann das DMA mit maximalem Bustakt arbeiten, was dann eventuell 
tatsächlich die CPU blockiert bis der Transfer fertig ist (die CPU wäre 
natürlich auch blockiert wenn man stattdessen memcpy machen würde).

von Peter (pittyj)


Lesenswert?

Ich musste bei einem STMH743 mal größere Datenmengen über SPI bewegen.
Der bietet 3 Möglichkeiten: Polling, Interrupt und DMA Betrieb.
Ich habe alle 3 Sachen getestet.
Am performantesten war der reine Interrupt Betrieb. Mit DMA war es 
leicht langsamer als der reine Interrupt-Betrieb, den ich dann genommen 
habe.

Vielleicht sollte der TE einfach mal alles durchtesten, ob DMA wirklich 
schneller ist.
Und vielleicht auch mal auf Nachfragen antworten.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Peter schrieb:
> Am performantesten war der reine Interrupt Betrieb. Mit DMA war es
> leicht langsamer als der reine Interrupt-Betrieb, den ich dann genommen
> habe.

Hast du die SPI-Leitungen mal mit dem Oszilloskop analysiert? Waren da 
Pausen zwischen? Was genau hat da länger gedauert?

von Adam P. (adamap)


Lesenswert?

Ich habs so gelöst, dass ich auch per DMA empfange aber zyklisch prüfe 
ob per DMA etwas empfangen wurde.
Selbst wenn kein DMA Interrupt kommt, weil 1 Byte fehlt, schlägt mein 
Timeout zu und löscht den Vorgang und initialisiert neu.

von Hans-Georg L. (h-g-l)


Lesenswert?

Peter schrieb:
> Ich musste bei einem STMH743 mal größere Datenmengen über SPI bewegen.
> Der bietet 3 Möglichkeiten: Polling, Interrupt und DMA Betrieb.
> Ich habe alle 3 Sachen getestet.
> Am performantesten war der reine Interrupt Betrieb. Mit DMA war es
> leicht langsamer als der reine Interrupt-Betrieb, den ich dann genommen
> habe.

Hast du das mit der HAL gemacht ?

von Peter (pittyj)


Lesenswert?

Niklas G. schrieb:
> Peter schrieb:
>> Am performantesten war der reine Interrupt Betrieb. Mit DMA war es
>> leicht langsamer als der reine Interrupt-Betrieb, den ich dann genommen
>> habe.
>
> Hast du die SPI-Leitungen mal mit dem Oszilloskop analysiert? Waren da
> Pausen zwischen? Was genau hat da länger gedauert?

Der Gesamt-Overhead war kleiner. Ich habe mehr Daten pro Sekunde lesen 
können.

Hans-Georg L. schrieb:
> Hast du das mit der HAL gemacht ?
Ja, wo es ging die HAL benutzt. Funktioniert im Allgemeinen ganz gut, da 
mache ich dann keine direkten Registerzugriffe.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Peter schrieb:
> Ja, wo es ging die HAL benutzt

Dann war das wahrscheinlich der Overhead in der HAL. Ohne HAL dürfte der 
Durchsatz pro Sekunde identisch sein, aber bei Verwendung von DMA 
braucht es viel weniger CPU-Zeit und Energie.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Wastl schrieb:

> Bei Transmit gewinnt man sehr wohl eine Menge. Denn anstatt
> in der Transmit-Loop zu warten bis das Transmit-Register
> wieder frei ist (Baudrate ist so niedrig dass sich der
> Controller "langweilt") um das nächste Datum zu schreiben

Dazu braucht man noch kein DMA, sinnloses Polling kann man auch mit 
Interrupts alleine vermeiden.

Nö, DMA macht eigentlich erst bei sehr hohen Bitraten Sinn, denn statt 
eines Interrupts pro Byte gibt's dann halt nur einen pro Transfer-Block.

> Bei Receive ohne DMA bekommt man einen Interrupt der sehr
> schnell abgearbeitet ist, also kein spürbarer Zeitgewinn
> mit DMA.

Beim Empfangen gilt eigentlich dasselbe wie beim Senden. Polling braucht 
niemand, da nimmt man Interrupts. DMA bringt dieselben Vorteile wie beim 
Senden, halt eine geringere Interruptlast. Statt einmal pro Byte nur 
einmal pro Block.

Das Problem ist halt nur, dass man sich auf Empfängerseite mit der 
Behandlung möglicher Übertragungsfehler herumschlagen muss. Das ist 
trivial beim Polling, schon etwas schwieriger beim Einsatz von 
Interrupts und doch schon relativ kompliziert beim Einsatz von DMA. 
Fehlererkennung und Verwerfen des Puffers ist noch einfach. Der 
eigentliche Kitzel ist die Resynchronisation auf den Datenstrom des 
Senders nach dem Fehlerfall. Wie kompliziert (und ob überhaupt sinnvoll 
möglich) das ist, hängt von der Gestaltung des Protokolls oberhalb des 
UART-Levels ab. Praktisch immer ist es aber nötig, die Resynchronisation 
zunächst ohne DMA durchzuführen. Erst wenn anhand der so empfangenen 
Daten wieder ein Blockanfang des übergeordneten Protokolls zu erwarten 
ist, kann man in den DMA-Mode wechseln.

Man könnte also so sagen: DMA für den Empfänger ergibt nur einen Sinn, 
wenn das übergeordnete Protokoll halt den Versand von Blöcken fester 
Größe mit einer Pause einer gewissen Minimalgröße vorsieht. Für 
kontinuierliche Datenströme ohne innere Struktur ist es hingegen wenig 
bis nicht brauchbar.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Ob S. schrieb:
> wenn das übergeordnete Protokoll halt den Versand von Blöcken fester
> Größe mit einer Pause einer gewissen Minimalgröße vorsieht.

Wenn die Pause ein Frame lang ist, sodass der UART den IDLE Interrupt 
auslöst, ist das kein Problem. Auch wenn die Frame-Länge variabel ist!

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Niklas G. schrieb:
> Ob S. schrieb:
>> wenn das übergeordnete Protokoll halt den Versand von Blöcken fester
>> Größe mit einer Pause einer gewissen Minimalgröße vorsieht.
>
> Wenn die Pause ein Frame lang ist, sodass der UART den IDLE Interrupt
> auslöst, ist das kein Problem. Auch wenn die Frame-Länge variabel ist!

Nicht ganz. Dieser Ansatz funktioniert nur, wenn die "Frames" garantiert 
immer kurzer sind als die DMA-Länge. Wird diese Grenze gerissen, kommt 
richtig heftiger Müll raus, nämlich Fehler, die u.U. nicht mal erkannt 
werden. Um sie wenigstens zuverlässig erkennen zu können, müssen nämlich 
zwei ISRs korrekt zusammenarbeiten.

Die HAL-Routinen bieten das nicht.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Ob S. schrieb:
> Wird diese Grenze gerissen, kommt richtig heftiger Müll raus

Wieso? Man muss nur im DMA-Interrupt das DMA schnell neu starten. 
Glücklicherweise haben die UARTs der neueren Controller einen FIFO von 
bis zu 8 Bytes, da hat man viel Zeit um das neu einzustellen.

Ob S. schrieb:
> Die HAL-Routinen bieten das nicht.

Doch, man muss halt im Callback den DMA neu starten. Kann aber sein dass 
die HAL zu langsam ist.

von Harald A. (embedded)


Lesenswert?

Mat. K. schrieb:
> gibt es für dieses Problem eine Standard Lösung:

> Sollte warum auch immer ein Byte zu wenig übertragen werden,

> Wie wird das typischerweise verhindert?

Falls die Möglichkeit besteht zwischen den Paketen beiderseits ein Break 
generieren. Entweder kann der STM das direkt oder ein Byte 0 mit halber 
Baudrate senden (die Empfänger-Baudrate wird nie verändert).

Daraufhin wird beim jeweiligen Empfänger ein Framing-Error Interrupt 
ausgelöst, in dem dann der DMA-Empfang des folgenden Paketes vorbereitet 
wird. In diesem Framing-Error Int kann man dann noch schauen, ob das 
empfangene Byte auch tatsächlich 0 war.
Kommt das folgende Paket in erwarteter Länge gibt es am Ende des DMAs 
die Verarbeitung ansonsten passiert einfach nichts, das Paket ist dann 
verloren (da nicht gültig). Dreh- und Angelpunkt ist immer der nächste 
Break-Interrupt, der rettet alles wieder.
Damit wird das Interface sehr robust gegen Störungen aller Art. Selbst 
nach einem Trennen der Verbindung fängt sich das System anhand der 
Sync-Marke sofort wieder. Der Overhead für dieses Prinzip ist sehr 
gering. Man braucht kaum Zwangspausen zwischen den Paketen und alles 
findet in Interrupts statt, das ist ein zusätzlicher Interrupt pro 
Seite. Auf dem Interface selbst hat man nur das Break (im Prinzip 2 
Bytes Länge und etwas Zeit für Verarbeitung und Aufsetzen des DMAs)

: Bearbeitet durch User
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.