Forum: Mikrocontroller und Digitale Elektronik STM32Cube HAL UART Einstieg


von STM32-Einsteiger (Gast)


Lesenswert?

Hallo und Frohe Weihnachten!

Ich versuche gerade mich in STMCubeMX & Co einzuarbeiten und optimal 
bzw. entsprechend der dahinter liegenden Philosophie zu nutzen.

Man hat ja oft folgende Tasks in der while-Schleife in main():
1. Empfange etwas per UART (variable Länge)
2. Verarbeite die empfangenen Daten
3. Sende irgendetwas zurück (oder an eine andere Schnittstelle)
4. Andere wichtige Dinge

Der Empfang mit HAL_Uart_Receive() macht hier keinen Sinn, da es 
blockiert, bis Daten kommen.
Dann fällt 4. hinten runter.

Beim Empfang mit HAL_Uart_Receive_IT() wird direkt in den Puffer 
geschrieben, aber ich weiß nicht
- ob überhaupt etwas empfangen wurde,
- an welche Stelle im Puffer gerade geschrieben wird,
- ob die Sendung beendet wurde (z.B. Zeilenende).
Dazu müsste ich ja vorher bereits wissen, wie viele Zeichen empfangen 
werden sollen.

Wie löst man das am besten und innerhalb der Philosophie der HAL?

A: In main() mit HAL_UART_GetState prüfen, ob HAL_UART_STATE_BUSY_RX 
nicht mehr gesetzt ist.
Dann erst RxPuffer lesen/kopieren und  HAL_Uart_Receive_IT() neu 
anwerfen.
=> Ich muss immer noch wissen, wie viele Bytes da kommen sollen. Sonst 
werden empfangene Daten nicht gelesen, bevor der Puffer voll ist.
Immerhin wird der Rest der while Schleife (4.) ausgeführt.

B: Empfangspuffer der Größe 1, und HAL_USART_RxCpltCallback() schaufelt 
jedes empfangene Byte in einen Ringpuffer und startet 
HAL_Uart_Receive_IT() neu.
Wird ein "end of transfer" Zeichen erkannt, wird ein globales Flag 
gesetzt.
=> Das hieße ja nichts anderes als "Die HAL Implementierung des Puffers 
ist Käse. Mach Dir Deine eigene."
Und wirklich schlank ist das dann auch nicht, was da alles während der 
Interrupt-Routine ausgeführt würde...

C: Vergiss HAL, nutze libopencm3, ChibiOS, ...

Oder denke ich zu kompliziert?

von W.S. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6085897:
> Oder denke ich zu kompliziert?

Nee, Variante C zeigt in die richtige Richtung. Schreibe dir 
Lowlevel-Treiber für deine UART's, die den Datenverkehr per Interrupt 
erledigen und die Daten zwischenspeichern. Und die ein gut zu 
benutzendes Interface für die höheren Schichten deiner Firmware haben, 
so daß du dich außerhalb des Treibers nicht um dessen 
Hardware-Angelegenheiten kümmern mußt.

Lies mal da, um einen Eindruck zu kriegen, wie sowas geht:
https://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP

Im Prinzip hast du ja in main immer eine Grundschleife, in der sich der 
µC dreht, wenn es sonst nix zu tun gibt. Also etwa so:

immerzu:
   mach dies
   mach das
   wenn X dann mach Y
   usw.
   goto immerzu

Guck dir im Beispiel dazu mal in cmd.c die Funktion Talk(word wo) an. 
Die testet mit if (RxAvail(wo)), ob ein Zeichen auf "wo" (UART1 oder 
UART2 oder sonst ein serieller Kanal) eingetrudelt ist und wenn ja, dann 
wird das Zeichen abgeholt und verarbeitet. Alternativ wird einfach die 
Funktion beendet. So im Prinzip wird nichtblockierend geschrieben, so 
daß man eben nicht auf irgendwas wartend auf der Stelle trampelt, 
sondern zwischenzeitlich was anderes mit seiner Prozessorzeit machen 
kann.

W.S.

von STM32-Einsteiger (Gast)


Lesenswert?

W.S. schrieb:
> Guck dir im Beispiel dazu mal in cmd.c die Funktion Talk(word wo) an.
> Die testet mit if (RxAvail(wo)), ob ein Zeichen auf "wo" (UART1 oder
> UART2 oder sonst ein serieller Kanal) eingetrudelt ist und wenn ja, dann
> wird das Zeichen abgeholt und verarbeitet.

Hallo W.S.
und danke für das Code-Beispiel.

Meine eigenen Routinen sehen so ähnlich aus. Nur wie löst man das am 
besten mit und innerhalb der STM32Cube HAL?
Irgendetwas muss sich ja irgendjemand dabei gedacht haben, als er das 
Zeugs geschrieben hat. Und sehr wahrscheinlich war er dabei auch nicht 
alleine sondern Teil eines Teams von (embedded) Software-Entwicklern.
Ich kann mir jedenfalls nicht vorstellen, dass STM diese Aufgabe einem 
Hardwareentwickler übertragen hat, weil der zufällig etwas BASIC konnte 
und deshalb bei seinen Kollegen als "der Programmierer" galt. ;-)

Wenn ich Dein Beispiel noch einmal auf die HAL übertrage, dann ähnelt es
B: Empfangspuffer für HAL_Uart_Receive_IT() der Größe 1, und 
HAL_USART_RxCpltCallback() schaufelt jedes empfangene Byte in einen 
größeren Ringpuffer und startet HAL_Uart_Receive_IT() neu.

Die Hauptschleife schaut dann nach, ob etwas im Ringpuffer ist.

OK - aber damit "neutralisiere" ich den RxBuffer der HAL, lege meinen 
eigenen an und schreibe damit einen Teil der Funktionen des HAL 
IRQHandlers neu.
Was irgendwie zart andeutet, dass der HAL Puffer und IRQHandler eine 
Fehlkonstruktion sind.
Und den Umweg UART->DR -> HAL Puffer -> Ringbuffer kann der Compiler 
noch nicht einmal wegoptimieren.

Hmmm - ich könnte den WritePointer des Ringpuffers direkt an 
HAL_Uart_Receive_IT() übergeben (nach Check, dass der Puffer noch Platz 
hat). Das eliminiert einen überflüssigen Puffer.
Aber trotzdem muss für jedes Byte dann immer noch die ganze Mimik mit 
Interrupts an/aus/wieder an durchlaufen werden. Und wenn ich 
HAL_Uart_Receive_IT() nicht in der HAL_USART_RxCpltCallback() sondern 
erst in der Hauptschleife wieder anwerfe, könnten Daten verloren gehen.

von pegel (Gast)


Lesenswert?

Was willst Du übertragen?
Eine Textzeile mit Zeilenende Zeichen, oder beliebige Daten?

Der erste Fall ist ganz einfach:
-in HAL_UART_RxCpltCallback auf Endzeichen testen, falls nicht, Zeichen 
in den Puffer, wenn Zeilenende -> Funktion machwasmitdenDaten
-braucht keine ständige Kontrolle des Puffer Inhalts, nicht einmal einen 
Ringpuffer
-in der main.c mit wenigen Zeilen erledigt

Auf Wunsch zeige ich dir meine Version.

von Stefan F. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6086194:
> Nur wie löst man das am
> besten mit und innerhalb der STM32Cube HAL?
> Irgendetwas muss sich ja irgendjemand dabei gedacht haben, als er das
> Zeugs geschrieben hat.

Das ist eben das Problem mit der HAL, sie ist nur sehr knapp 
dokumentiert.

von pegel (Gast)


Lesenswert?

Dafür liefern sie aber zu jeder Funktion gute Beispiele mit.

von STM32-Einsteiger (Gast)


Lesenswert?

pegel schrieb:
> Der erste Fall ist ganz einfach:
> -in HAL_UART_RxCpltCallback auf Endzeichen testen, falls nicht, Zeichen
> in den Puffer, wenn Zeilenende -> Funktion machwasmitdenDaten
> -braucht keine ständige Kontrolle des Puffer Inhalts, nicht einmal einen
> Ringpuffer
> -in der main.c mit wenigen Zeilen erledigt

OK.
Die Funktion machwasmitdenDaten() würde ich gerne in der Hauptschleife 
lassen. Die Callback Funktion hätte ja so schon einiges zu tun und hält 
den IRQHandler auf.

Da HAL_UART_RxCpltCallback() nur bei RxXferCount == 0 aufgerufen wird, 
muss also byteweise empfangen werden mit
HAL_UART_Receive_IT(huart1, buffer, 1)

In der HAL_UART_RxCpltCallback() könnte man dann den buffer Zeiger 
inkrementieren und mit HAL_UART_Receive_IT(huart1, buffer, 1) das 
nächste Byte holen lassen. Es sei denn man hat eol. Dann müsste man den 
buffer erst irgendwo sichern, die Hauptschleife informieren,  den Zeiger 
auf Anfang vom buffer zurück setzen und dann HAL_UART_Receive_IT(huart1, 
buffer, 1) wieder anwerfen. - Also alles wieder rückgängig machen, was 
der HAL IRQHandler gerade erst gemacht hat, bevor er 
HAL_UART_RxCpltCallback() aufruft. (Interrupts an/aus usw.)
Das "fühlt" sich ineffizient an.

pegel schrieb:
> Auf Wunsch zeige ich dir meine Version.
Na, aber immer doch. Es ist doch Weihnachten, da darf man sich was 
wünschen. ;-)

von STM32-Einsteiger (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das ist eben das Problem mit der HAL, sie ist nur sehr knapp
> dokumentiert.
1165 Seiten würde ich nicht knapp nennen. ;-)
Die neuen Funktionen Register/Unregister der Callbacks sind noch nicht 
einmal drin. Dabei habe ich die Doku erst vor zwei Tagen 
heruntergeladen.

pegel schrieb:
> Dafür liefern sie aber zu jeder Funktion gute Beispiele mit.
Beispiele ja, aber viel mit blocking delays.
Warum warte ich in einem Beispiel für non-blocking Transfer anschließend 
mit
1
   while (UartReady != SET)
2
  {
3
  }
darauf, dass der Kram auch ankommt?
Und sollte der Empfänger mehr Bytes erwarten als er bekommt, wartet er 
(blockierend) ewig.
(STM32F103RB-Nucleo/Examples/UART/UART_TwoBoards_ComIT/)

von Stefan F. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6086306:
> 1165 Seiten würde ich nicht knapp nennen. ;-)

Für das was sie enthält ist das viel zu wenig Doku. Wenn das PDF 20.000 
Seiten hätte, würde es Anfänger abschrecken. Mir wäre das egal, die 
dürfen bei Arduino bleiben, wenn sie wollen.

von pegel (Gast)


Angehängte Dateien:

Lesenswert?

Im Anhang meine Version für ein Nucleo-F746 SART3.

Eine Text-Zeile wird empfangen und bei Zeilenende als Kleinbuchstaben 
zurück geschickt.

von pegel (Gast)


Lesenswert?

USART3 sollte es heissen.

von Harry L. (mysth)


Angehängte Dateien:

Lesenswert?

Ich hab dir mal ein funktionierendes Beispiel mit HAL angehängt.

von Horst (Gast)


Lesenswert?

Die HAL ist nicht knapp dokumentiert. Es ist nur nicht so einfach das 
richtige Dokument zu finden:

https://www.st.com/resource/en/user_manual/dm00154093.pdf

von W.S. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6086194:
> Nur wie löst man das am
> besten mit und innerhalb der STM32Cube HAL?
> Irgendetwas muss sich ja irgendjemand dabei gedacht haben, als er das
> Zeugs geschrieben hat. Und sehr wahrscheinlich war er dabei auch nicht
> alleine sondern Teil eines Teams von (embedded) Software-Entwicklern.

Erstens: man löst das Problem, indem man Cube und HAL einfach aus dieser 
Sache heraushält. Der LL Treiber serial.c im Beispiel braucht lediglich 
die HW-Registeradressen laut Refmanual und sonst nix - naja außer der 
Erklärung was ein Byte ist und so.

Alternativ Cube und HAL komplett rausschmeißen. Man erweist sich mir 
diesem Zeugs auf lange Sicht nur einen Bärendienst.

Zweitens: Was sich die Leute dabei gedacht haben? Antwort: HAL wird 
gebraucht, damit Cube unumgänglich ist. Und Cube wird gebraucht, um dich 
mit nem Ring durch die Nase an ST's Galeere festzunageln. Mal drastisch 
ausgedrückt.

Ja, man kann sowas dezenter sagen, aber schlußendlich ist es dasselbe. 
Mit sowas landen dann Bezüge auf herstellerspezifisches Zeugs selbst in 
main.c und wer sich auf sowas einläßt, hat sich damit selbst 
festgenagelt.

ST ist mit sowas nicht allein, bei Infineon ist es Dave, NXP hat auch 
seine eigenen Nasenringe vorrätig - es ist in jedem Falle dieselbe 
Strategie: Anwender von der bei Cortexen ziemlich einheitlichen HW 
fernhalten und stattdessen an eine herstellerspezifische Softwareschicht 
gewöhnen, die zwar keinerlei HW-Abstraktion bietet, dafür aber eine 
feste Kundenbindung bewirkt.

Und selbst bei reinen Softwarebuden trifft mal auf sowas. Guck dir mal 
das an:
Beitrag "Makefiles auf Linux für STM32"
Da haben sich Leute auf Chibi eingelassen - mit dem Ergebnis, daß ihr 
Projekt für wirklich alle Leute nutzlos ist, die nicht die exakt gleiche 
IDE/Toolchain benutzen. Sieht Portabilität so etwa aus? Wohl nicht.

W.S.

von W.S. (Gast)


Lesenswert?

Harry L. schrieb:
> Ich hab dir mal ein funktionierendes Beispiel mit HAL angehängt.

Was soll das? Das sind 4.3 Megabyte an Zeugs und es ist kein wirklich 
funktionierendes Beispiel. Da fehlt es wirklich an allen Ecken und 
Enden.


In meinem Beispiel von ca. 460 K ist eine komplette Firmware dabei, mit 
Kommandoprozessor und Kommunikation per Seriell und USB, mit 
Event-Verwaltung, System-Uhr und selbst den zugehörigen 
Leiterplatten-Dateien für zwei alternative Hardware-Versionen.

W.S.

Beitrag #6086614 wurde von einem Moderator gelöscht.
Beitrag #6086634 wurde von einem Moderator gelöscht.
von Johannes S. (Gast)


Lesenswert?

Ja, völlig überbewertet diese Portabilität. USB mit HAL läuft zwar auf 
L0..H7, aber ich gebe meinen Registern lieber meine eigenen Namen und 
mache auf die Software anderer einen großen Haufen. Das ist Fortschritt, 
nur so komme ich weiter.
Speicher wird ja immer knapper und teurer bei aktuellen μC, da ist 
Sparsamkeit die oberste Maxime. OS sind totaler Mist und bei μC völlig 
überdimensioniert.
Prost.
Hersteller‘hörigkeit‘: ja, verpönt. Damals, bei den 8051er Derivaten. 
Weil der eine war schnell, der andere hatte viele serielle, ein wieder 
anderer I2C.
Heute: ST hat alles. NXP auch und viele andere auch. Überhaupt kein 
Grund mehr die Hunde aus verschiedenen Dörfern zusammenzusuchen.

Beitrag #6086644 wurde von einem Moderator gelöscht.
von Johannes S. (Gast)


Lesenswert?

Guest schrieb im Beitrag #6086644:
> Die bekommen eher mehr als weniger Speicher und billiger werden sie auch
> :O

Echt jetzt? Habe ich in den letzten 30 Jahren tatsächlich was verpasst?

von Dieter (Gast)


Lesenswert?

Johannes S. schrieb:
> Echt jetzt? Habe ich in den letzten 30 Jahren tatsächlich was verpasst?

Anscheinend :D
Ich meine µC mit 1MB RAM und 2MB Flash sind jetzt echt keine Seltenheit 
und kosten nicht gerade ein Vermögen. Selbst wenn man mal etwas tiefer 
greift und mal 192 oder 320 KB RAM annimmt. Wenn ich da an so manchen 
Atmega mit 2KB denke ;)

von Richard (Gast)


Lesenswert?

Dieter schrieb:
> Wenn ich da
> an so manchen Atmega mit 2KB denke ;)

Denk ruhig. Der leistet damit -effizient programmiert- oft das Gleiche!

von Guest (Gast)


Lesenswert?

Richard schrieb:
> Denk ruhig. Der leistet damit -effizient programmiert- oft das Gleiche!

Geht jetzt hier wieder ein Glaubenskrieg los? Effizient programmieren 
ist nicht nur ein möglichst kleines Programm zu haben. Es kommt doch 
immer auf die Anwendung an. Wenn ich große Datenmengen verarbeite 
brauche ich halt mehr Speicher das ist eben so und die meisten µC die es 
mittlerweile gibt haben doch wirklich genug davon. Das ist doch das 
gleiche zu sagen ich Bitbange jetzt mein UART weil ich das schon immer 
so gemacht habe obwohl mein µC 4 UART besitzt. Warum soll man sich den 
Stress antun auf jedes Byte achten zu müssen wenn der µC für 
unwesentlich mehr Geld sowieso genug Speicher hat. Ich meine ok, wenn du 
jetzt Millionenstückzahlen produzierst bei denen es sich lohnt 50 Cent 
zu sparen lass ich mir das noch gefallen aber grade für den Hobby Mensch 
oder fürs Prototyping ist das wirklich kein Grund mehr.

Das ist das gleiche mit der HAL natürlich kann ich darauf beharren das 
alles selber zu machen besser ist. Und das mag auch Teilweise weniger 
Overhead haben. Aber sind wir mal ehrlich, wenn man nur mit STM arbeitet 
und alle 2 Wochen was anderes auf dem Tisch hat und irgendeine neue 
Hardware des µC verwendet dann ist es grade für die Entwicklung wirklich 
praktisch. Und auch als Hobby Programmierer ist das sehr dankbar, da man 
fürs erste, schnell Ergebnisse erzielt. Besser und alles auf LL ebene 
selber machen kann man im Anschluss immer noch.

Im Übrigen bietet ST für so ziemlich alle Peripherie Beispiele für die 
HAL.

von Richard (Gast)


Lesenswert?

Guest schrieb:
> Und auch als Hobby Programmierer ist das sehr dankbar, da man
> fürs erste, schnell Ergebnisse erzielt.

Um schnelle Ergebnisse zu erzielen wollen Abstrahier-Monster wie die HAL 
in ihrer Philosophie erst einmal selbst durchdrungen werden. Man sieht 
es ja an Hilfe-Rufen wie die des TO hier. Und stellt sich der Erfolg 
"fürs erste" ein sieht man dann auf den zweiten Blick, wieviel 
umständlicher und aufgeblasener die HAL Lösung letztlich ist. Unter dem 
Strich hätte man lieber gleich lernen sollen, die MCU samt Peripherie zu 
verstehen und direkt anzusprechen. Dann plötzlich hätte oh Wunder der 
Aufgabe überraschenderweise oft auch ein ATMega gelangt...

von Martin (Gast)


Lesenswert?

Richard schrieb:
> Man sieht
> es ja an Hilfe-Rufen wie die des TO hier.

Ganz ehrlich es gibt nichts Einfacheres als mit der HAL_Uart_Recive_IT 
irgendwas zu empfangen. Im Callback schiebt man das empfangene Byte in 
einen Puffer und benutzt einen scheiß Pointer um die Position zu 
markieren. Das Ende der Nachricht kann man dann entweder durch Prüfen 
eines bestimmten Patterns, durch eine vorgegebene Länge, oder durch 
einen Empfangstimeout mit einem Timer. Danach kann man den String 
verarbeiten.

von Transmitbuffer (Gast)


Lesenswert?

Um wieder aufs Thema zurückzukommen, die HAL ist an
dieser (und auch an anderen Stellen) leider murks.

Lustigerweise durchläuft fast jeder, der mit den STMs
und UART arbeiten will bzw. muss folgenden Zyklus:

-"Ich möchte Daten unbestimmter Länge empfangen, aber nicht
ewig blocken wenn doch nicht alles da ist"
-Versuch mit dem HAL im blocking mode
-Versuch mit der HAL im interrupt mode und 1 byte buffer
-Aufgeben und selber schreiben im IRQ-Handler

Hier sieht man ein brauchbares Beispiel:
https://blog.the78mole.de/stm32-uart-continuous-receive-with-interrupt/

been there. done that.

von Guest (Gast)


Lesenswert?

Transmitbuffer schrieb:
> Um wieder aufs Thema zurückzukommen, die HAL ist an
> dieser (und auch an anderen Stellen) leider murks.

Was ist daran murks. Man scheibt seinen IRQ Handler im receive Callback 
und schiebt seine Daten in den Puffer. Wenn man das Terminationszeichen 
empfängt oder was weiß ich man als Endbedingung haben möchte, setzt man 
eine Flag und verarbeitet den String. Das sind, wenn man es ganz billig 
anstellt vielleicht 5 Zeilen Code plus das was die String Verarbeitung 
am Ende braucht.

von STM32-Einsteiger (Gast)


Lesenswert?

@pegel: Danke für das Code-Beispiel.
Ich hatte Dich also richtig verstanden.

Harry L. schrieb:
> Ich hab dir mal ein funktionierendes Beispiel mit HAL angehängt.

Auch an Dich ein Dankeschön.
Auch bei Dir passiert allerdings einiges in der ISR (bzw. im Callback, 
was ja dasselbe ist), und ich habe mal gelernt, dass man die ISR so kurz 
wie möglich halten soll.
Nun muss man ja an altem Gelernten nicht religiös festhalten, und die 
Cortex-M haben ja auch einen State "Active and pending" für Exceptions, 
d.h. es ist (fast) egal, ob dieser Code in der ISR oder in main 
ausgeführt wird. Immerhin weiß man in der ISR wenigestens, dass 
tatsächlich ein Byte angekommen ist.
Aber irgendwie bleibt ein "schlechtes Gewissen". ;-)

Martin schrieb:
> Ganz ehrlich es gibt nichts Einfacheres als mit der HAL_Uart_Recive_IT
> irgendwas zu empfangen. Im Callback schiebt man das empfangene Byte in
> einen Puffer und benutzt einen scheiß Pointer um die Position zu
> markieren.
Also meine Lösung B. Den HAL Puffer "neutralisieren", weil der Mist ist.

Transmitbuffer schrieb:
> die HAL ist an
> dieser (und auch an anderen Stellen) leider murks.
>
> Lustigerweise durchläuft fast jeder, der mit den STMs
> und UART arbeiten will bzw. muss folgenden Zyklus:
Vielen Dank für die Bestätigung.
Ich hatte schon die Befürchtung, ich übersehe etwas Wesentliches...

von STM32-Einsteiger (Gast)


Lesenswert?

Zu dem Streit "Abstraktion oder nicht", den ich hier unabsichtlich vom 
Zaun gebrochen habe:
Natürlich ist Portabilität schön. Wenn ich feststelle, dass dem F0, den 
ich gewählt habe, doch irgendetwas fehlt, was der F1 hätte, dann freue 
ich mich, wenn ich so einfach wechseln kann. Portabilität habe ich aber 
mit libopencm3 oer ChibiOS auch.
Natürlich ist es auch schön, wenn ich mir mit der Maus ein fertig 
initialisiertes, lauffähiges System zusammenklicken und einfach anfangen 
kann, ohne erst sämtliche RMs und PMs auswendig gelernt zu haben.

Man kann sich aber mit ein paar Klicks auch kräftig in den Fuß schießen 
und weiß dann nicht, was eigentlich passiert ist.

So habe ich mich gestern beinahe ausgesperrt:
Ich habe einen der SWD-Pins versehentlich deaktiviert und zwar gleich 
wieder aktiviert - aber da waren beide nur noch gelb, da jetzt Debug = 
No Debug in "SYS Mode and Configuration" gewählt war.
Ich hab's nicht gesehen oder nicht gleich begriffen - und schon lies 
sich der kleine Kerl nur noch mit Klimmzügen über OpenOCD programmieren.

War aber lehrreich... ;-)

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Harry L. schrieb im Beitrag #6086634:
> W.S. schrieb:
>> Harry L. schrieb:
>>> Ich hab dir mal ein funktionierendes Beispiel mit HAL angehängt.
>>
>> Was soll das? Das sind 4.3 Megabyte an Zeugs und es ist kein wirklich
>> funktionierendes Beispiel. Da fehlt es wirklich an allen Ecken und
>> Enden.
>
> You made my day...
>
> Du bist einfach ein verbitterter aler Sack, der schon lange die
> Verbindung zuR Lebenswirklichkeit verloremn hat, und daher hab ch hab
> nicht das geringste Interesse daran, mit dir zu diskutieren.

Danke für die Zusammenfassung Harry, dann muss ich das diesmal nicht 
machen ;)

Im Vergleich zu seiner 80er Jahre Programmierung mit Magic Numbers und 
für UART 1-5 denselben Code kopieren.
Dagegen ist der HAL doch echt ein Segen!

Aber in der Realität sollste man sich irgendwo dzwischen aufhalten.

: Bearbeitet durch User
von Gans Gebraten (Gast)


Lesenswert?

Mw E. schrieb:
>> Du bist einfach ein verbitterter aler Sack, der schon lange die
>> Verbindung zuR Lebenswirklichkeit verloremn hat, und daher hab ch hab
>> nicht das geringste Interesse daran, mit dir zu diskutieren.
>
> Danke für die Zusammenfassung Harry, dann muss ich das diesmal nicht
> machen ;)

Ah, von den Stinkstiefeln gibt es einen rechten und einen linken. 
Trotzdem hinkt der Läufer.

Transmitbuffer schrieb:
> Hier sieht man ein brauchbares Beispiel:
> https://blog.the78mole.de/stm32-uart-continuous-receive-with-interrupt/

Wie krank ist das denn? Einen IRQ-Handler allein mit HAL-Funktionen zu 
schreiben? Da schreibt man doch gleich direkt die paar Zugriffe auf 
USARTx-Register. Die eierlegende Wollmilchsau hat diverse Gendefekte!

von pegel (Gast)


Lesenswert?

Immer die gleich HAL gut oder pfui Diskussion.
Immer die gleiche Antwort dazu:

Man kann auch mischen!
Es verbietet einem niemand seine Register Befehle im HAL Grundgerüst zu 
benutzen.
Wenn man dann möchte, kann man auch alle HAL Funktionen bis auf 
Registerebene einsehen und direkt "klauen".

Also, wo genau ist das Problem?

von Gans Gebraten (Gast)


Lesenswert?

pegel schrieb:
> Man kann auch mischen!
> Es verbietet einem niemand seine Register Befehle im HAL Grundgerüst zu
> benutzen.

Volle Zustimmung!
Und das STM32 Grundgerüst von CubeMX zu nehmen, um die Takterzeugung 
transparent einzustellen, finde ich ebenfalls gut.

Wenn das jemand anders sieht ist das allerdings kein Grund, persönlich 
beleidigend zu werden; auch nicht von angemeldeten Trollen.

von W.S. (Gast)


Lesenswert?

pegel schrieb:
> Also, wo genau ist das Problem?

Das Problem besteht darin, daß gar viele Leute gern Programmierer sein 
wollen, obwohl sie mental dafür zu klein sind.

Also lernen sie irgend etwas auswendig, was man ihnen mal vorgesetzt hat 
und halten sich eisern genau daran - ohne die Sinnhaftigkeit jemals zu 
hinterfragen. Das sind genau die richtigen Kunden für Cobe, HAL, 
XPresso, Dave und wie die alle heißen mögen.

Es sind aber auch die gutgläubigen Anfänger, die da meinen, daß 
derartige Hersteller-Libs tatsächlich eine Hardware-Abstraktion seien 
und daß diese zu ihrem Wohl geschrieben seien. Nein, sind sie nicht.


Und sie sind nicht einmal einfacher oder gar benutzerfreundlicher:

Martin schrieb:
> Ganz ehrlich es gibt nichts Einfacheres als mit der HAL_Uart_Recive_IT
> irgendwas zu empfangen. Im Callback schiebt man das empfangene Byte in
> einen Puffer und benutzt einen scheiß Pointer um die Position zu
> markieren. Das Ende der Nachricht kann man dann entweder durch Prüfen
> eines bestimmten Patterns, durch eine vorgegebene Länge, oder durch
> einen Empfangstimeout mit einem Timer. Danach kann man den String
> verarbeiten.

Ach ja? Also ne Callback-Funktion, einen "scheiss Pointer", Pattern 
prüfen, mit vorgegebenen Längen und Empfangstimeout arbeiten?

Sowas nennst du "nichts Einfacheres"? Und du meinst, sowas sei eine 
Hardware-Abstraktion?

Lies mal das:
1
  char c;
2
3
  if (RxAvail(UART1))
4
  { c = GetChar(UART1);
5
    Char_Out(c, UART1);
6
  }

Bei sowas ist ne echte HW-Abstraktion dahinter. Das ist Code, der bei 
Vorhandensein ordentlicher LL-Treiber auf so ziemlich allen Plattformen 
läuft, für die es einen C Compiler gibt.

Nochwas:
Gerade bei UART's mit vorgegebenen Längen oder Timeouts arbeiten zu 
wollen, ist eine ausgesprochen dämliche Idee. Es ist nämlich immer ein 
Spiel zwischen zwei unabhängigen Geräten.

W.S.

von STM32-Einsteiger (Gast)


Lesenswert?

Guest schrieb:
> Was ist daran murks. Man scheibt seinen IRQ Handler im receive Callback
> und schiebt seine Daten in den Puffer.

Murks ist daran,
- dass ich einen eigenen IRQ Handler schreiben muss, obwohl die HAL 
schon einen implementiert.
- dass ich einen Teil dessen, was der HAL IRQHandler macht, anschließend 
wieder neutralisieren muss.
- dass aber der HAL IRQHandler trotzdem ausgeführt wird und ich ihn 
nicht einfach überladen kann (solange ich CubeMX benutze).

Hier haben sich ein paar Leute (auch bei STM) ein paar mehr Gedanken zu 
dem Problem gemacht:
https://stm32f4-discovery.net/2017/07/stm32-tutorial-efficiently-receive-uart-data-using-dma/
https://github.com/MaJerle/stm32-usart-dma-rx-tx
https://github.com/akospasztor/stm32-dma-uart


pegel schrieb:
> Man kann auch mischen!
> Es verbietet einem niemand seine Register Befehle im HAL Grundgerüst zu
> benutzen.
Ja, kann man. Man kann den autogenerierten Code ja einfach als 
Ausgangsbasis nehmen und dann verwenden oder auch nicht. Oder einfach 
ändern.
Solange man CubeMX benutzt, unterliegt man aber gewissen 
Einschränkungen. Daher wollte ich einfach wissen, wie man das Problem 
UART Empfang variabler Länge am besten und innerhalb der Philosophie 
der HAL und CubeMX löst.

Gans Gebraten schrieb:
> Wenn das jemand anders sieht ist das allerdings kein Grund, persönlich
> beleidigend zu werden
Volle Zustimmung. Zumal es bei so einer offenen Frage ja nicht um "Ja 
oder Nein" geht sondern darum, verschiedene Sichtweisen zu einem 
Problem zu sammeln. Davon profitieren doch alle Forumsteilnehmer viel 
mehr als von  "Meine Meinung ist die einzig Richtige. Es darf nur eine 
geben!"
Am Ende kann sich jeder das aussuchen, was zu seinem Projekt und zu 
seinen Bedürfnissen am besten passt.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Gerade bei UART's mit vorgegebenen Längen oder Timeouts arbeiten zu
> wollen, ist eine ausgesprochen dämliche Idee. Es ist nämlich immer ein
> Spiel zwischen zwei unabhängigen Geräten.

Achsooo! Die Leute von Modbus haben keine Ahnung von dem was sie tuen, 
weil sie Timeouts definiert haben!

W.S. schrieb:
> Das Problem besteht darin, daß gar viele Leute gern Programmierer sein
> wollen, obwohl sie mental dafür zu klein sind.

Meinste dich selbst?

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6085897:
> C: Vergiss HAL, nutze libopencm3, ChibiOS, ...

genau. Ich würde es so machen wie im Anhang. Ohne Codegenerator, ohne 
Unmengen generierter Kommentarblöcke die man nicht anrühren darf.
Auf welchem Target es laufen soll sage ich im compile Aufruf, ohne alles 
wieder neu konfigurieren zu müssen.

von Stefan F. (Gast)


Lesenswert?

Guest schrieb:
> Das sind, wenn man es ganz billig
> anstellt vielleicht 5 Zeilen Code plus das was die String Verarbeitung
> am Ende braucht.

Ja, mit HAL. Und ohne wären es auch nicht wesentlich mehr Zeilen.

von Stefan F. (Gast)


Lesenswert?

Mw E. schrieb:
> Die Leute von Modbus haben keine Ahnung von dem was sie tuen,
> weil sie Timeouts definiert haben!

Du verwechselst da eine konkrete Anwendung mit dem generischen (sein 
sollenden) Ansatz der HAL.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Bei W.S. weis man das immer nich so was er meint ;)

von Uwe (Gast)


Lesenswert?

Mw E. schrieb:
> Bei W.S. weis man das immer nich so was er meint ;)

I.d.R. das Richtige!

von J. V. (janvi)


Lesenswert?

> Was ist daran murks

es ist zumindest soviel Murks dass sie nur beim STM32 so gemacht wurde 
und beim STM8 ganz anders (um Resourcen zu sparen)

von Florian (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal eine HAL-konforme Lösung aus einem laufenden Projekt:

Ich nutze DMA im zyklischen Modus zum Empfangen (und im Single Shot 
Modus zum Senden), konfiguriert in CubeMX mit Priorität auf Empfangen. 
Um das nach der Konfiguration in Gang zu stoßen reicht ein Aufruf von:
1
HAL_UART_Receive_DMA(&huart2, uart_rx_buffer, UART_RX_BUFFER_SIZE);
Durch Auslesen des DMA_CNDTRx Registers lässt sich die Position des 
zuletzt empfangenen Datenbytes im Buffer bestimmen (Buffergröße minus 
DMA_CNDTR, da dieses herunter zählt). Wer möchte kann da einen 
Ringbuffer (Siehe Anhang) drüber stülpen, DMA_CNDTR liefert wie gesagt 
sofort den Head-Index, während der Tail-Index beim Auslesen erhöht wird:
1
// RBUF_modOptimized -> Modulo Operator optimiert für Cortex-M0 und kleine Werte
2
uart_rx_ringbuffer.head = RBUF_modOptimized(UART_RX_BUFFER_SIZE - hdma_usart2_rx.Instance->CNDTR, UART_RX_BUFFER_SIZE);
3
[...]
4
int32_t available = RBUF_getReadableSize(&uart_rx_ringbuffer);
5
[...]
6
// Wenn nötig Daten aus dem Ringbuffer auslesen um einen Überlauf zu verhindern...
Das ist natürlich nicht mehr an den USART Interrupt gekoppelt, dafür 
läuft das Ganze vor sich hin ohne die CPU bei einzelnen empfangenen 
Bytes zu beanspruchen, man kann sich aussuchen wann und wie oft man den 
Zähler überprüft, halt den Anforderungen entsprechend so oft, dass 
nichts verloren geht. Das kann auch in einem Timer Interrupt gemacht 
werden, die Intervalldauer kann man gut anhand der Buffergröße und der 
UART Geschwindigkeit dimensionieren. Wer mit der zusätzlichen Latenz 
(zwei bis dreistelliger us-Bereich, je nach Abfrage-Intervall) leben 
kann, für den wäre dieser Ansatz eine Lösung.

Ich würde dem TO wirklich empfehlen das Reference Manual dazu zu nehmen 
wenn er denn schon die HAL nutzt. Das ist wichtig um zu verstehen was da 
intern abgeht, man sollte es auch ohne HAL machen können und vor allem 
verstehen was jeder Aufruf für Operationen ausführt, ansonsten ist das 
ein gefährlicher Blindflug, egal welche Abstraktionsebene man nun nutzt.

von Florian (Gast)


Angehängte Dateien:

Lesenswert?

Kleiner Nachtrag, die Verzögerung die durch das nicht Interrupt basierte 
Abarbeiten entsteht ist in meinem Fall ~28 us bei 48 MHz Takt. Der 
Windows VSerial-Treiber fügt da nochmal eine Millisekunde Latenz hinzu, 
da sieht man mal wo die Performance flöten geht...

von STM32-Einsteiger (Gast)


Lesenswert?

Hallo Florian,

danke für Deine Lösung.
Inzwischen habe ich hier noch eine weitere Möglichkeit gefunden, die 
ebenfalls mit der HAL kompatibel ist und von CubeMX beim Update nicht 
platt gemacht wird:
https://blog.ghmit.com.au/2019/07/simple-dynamic-length-uart-comms-with.html
https://stm32f4-discovery.net/2017/07/stm32-tutorial-efficiently-receive-uart-data-using-dma/
https://github.com/MaJerle/stm32-usart-dma-rx-tx
https://github.com/akospasztor/stm32-dma-uart

Ich hatte mich schon geärgert, dass es den idle Interrupt zwar gibt aber 
er in der ganzen HAL nicht benutzt wird und dass man an den 
HAL_UART_IRQHandler nicht dran kann, aber meine Aussage von oben:
STM32-Einsteiger schrieb im Beitrag #6086902:
> - dass aber der HAL IRQHandler trotzdem ausgeführt wird und ich ihn
> nicht einfach überladen kann (solange ich CubeMX benutze).
war falsch.
Tatsächlich kommt man eine Ebene vorher an alles heran, nämlich im 
USART1_IRQHandler. Dort könnte man den HAL_UART_IRQHandler sogar ganz 
ersetzen, zumindest aber ergänzen.

von Johannes S. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6090147:
> und dass man an den
> HAL_UART_IRQHandler nicht dran kann,

warum nicht? Vectortable ins Ram kopieren und mit NVIC_SetVector() mit 
eigenem überschreiben.

von Harry L. (mysth)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6090147:
> Ich hatte mich schon geärgert, dass es den idle Interrupt zwar gibt aber
> er in der ganzen HAL nicht benutzt wird und dass man an den
> HAL_UART_IRQHandler nicht dran kann, aber meine Aussage von oben:
> STM32-Einsteiger schrieb im Beitrag #6086902:
>> - dass aber der HAL IRQHandler trotzdem ausgeführt wird und ich ihn
>> nicht einfach überladen kann (solange ich CubeMX benutze).
> war falsch.
> Tatsächlich kommt man eine Ebene vorher an alles heran, nämlich im
> USART1_IRQHandler. Dort könnte man den HAL_UART_IRQHandler sogar ganz
> ersetzen, zumindest aber ergänzen.

Das ist doch vollkommener Blödsinn!
Genau dafür gibt es die ganzen Callback-Funktionen, und die sind alle 
weak deklariert.
D.h.: man kann die mit eigenen Funktionen überschreiben.

Ich hab dir doch oben ein Beispiel mit HAL angehängt in dem genau das 
passiert:
Beitrag "Re: STM32Cube HAL UART Einstieg"

Da sieht man doch, wie das funktioniert.

Ausserdem solltest du das HAL-Referenz-Manual mal genauer studieren!

: Bearbeitet durch User
von STM32-Einsteiger (Gast)


Lesenswert?

Harry L. schrieb:
> Das ist doch vollkommener Blödsinn!
> Genau dafür gibt es die ganzen Callback-Funktionen, und die sind alle
> weak deklariert.

Ja, genau. Die Callback-Funktion. Da gibt es genau eine, die hier 
interessant ist: HAL_UART_RxCpltCallback.
Wann die aufgerufen wird, bestimmen der HAL_UART_IRQHandler und 
UART_Receive_IT. Wenn Dir das nicht passt, kannst Du das nicht ändern, 
denn die sind nicht weak.
Deshalb rufst Du ja  HAL_UART_Receive_IT immer mit Puffergröße 1 auf, 
damit für jedes einzelne popelige Byte der Interrupt getriggert wird und 
Du es mit Deiner langen Callbackfunktion bearbeiten kannst.
Das kann man ja machen. Genau das war ja oben auch meine erste Idee B.

Es geht aber auch anders. Nämlich mit DMA und ohne jedes Byte einzeln 
dreimal anzufassen.

Das kann man doch in aller Ruhe und mit Freude zur Kenntnis nehmen, ganz 
ohne "Blödsinn" und andere Agressionen.

von pegel (Gast)


Lesenswert?

Dafür musst Du dann eben den Inhalt des Puffers und auch noch dessen 
Länge prüfen.
Es kommt eben darauf an was man übertragen will. Hatten wir schon.

von Harry L. (mysth)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6090277:
> Deshalb rufst Du ja  HAL_UART_Receive_IT immer mit Puffergröße 1 auf,
> damit für jedes einzelne popelige Byte der Interrupt getriggert wird

Ja, wie denn sonst, wenn ich nicht wissen kann, wann und wie viele Bytes 
kommen?

STM32-Einsteiger schrieb im Beitrag #6090277:
> Du es mit Deiner langen Callbackfunktion bearbeiten kannst.
> Das kann man ja machen.

Code lesen und verstehen sind 2 Dinge.
Letzteres scheint dir nicht zu liegen...
Was ist an der Funktion "lang"?

STM32-Einsteiger schrieb im Beitrag #6090277:
> Es geht aber auch anders. Nämlich mit DMA und ohne jedes Byte einzeln
> dreimal anzufassen.

Achso!
Und der DMA erkennt auch, wann die Eingabe zu Ende ist usw....

Na, dann viel Spaß!
Du mußst noch sehr, sehr viel lernen!

von W.S. (Gast)


Lesenswert?

STM32-Einsteiger schrieb im Beitrag #6090277:
> Es geht aber auch anders. Nämlich mit DMA und ohne jedes Byte einzeln
> dreimal anzufassen.

Unfug. Versuche doch bitte, wenigstens EINMAL logisch und von Grund 
auf zu denken.

Du weißt hoffentlich, daß ein DMA nichts anderes kann, als Daten von A 
nach B zu transportieren.

So ein DMA kann keine Entscheidungen fällen, kann keine Antworten über 
einen asynchronen Datenfluß geben und kann keinerlei Sendefluß 
steuern. Sowas alles mußt du in jedem Falle selbst mit der CPU 
erledigen.

Genau deshalb ist das Verwenden von DMA beim USART herzlich sinnlos. Du 
muß trotz DMA jedes Byte zweimal anfassen.

Für die Empfangsrichtung:
Da mußt du in der ISR das Byte aus dem Rx-Register abholen und in deinen 
Ringpuffer stopfen. Dabei mußt du auch drauf achten, was du tun willst, 
falls kein Platz mehr im Ringpuffer ist. Das Ausdenken der Reaktion auf 
sowas kann dir niemand abnehmen.

Außerhalb der ISR mußt du möglichst einfach feststellen können, ob ein 
Empfangsbyte vorliegt oder nicht. Die Bytes kommen nämlich nicht 
planbar, sondern asynchron und unplanbar herein. Und wenn was vorliegt, 
mußt du es auch möglichst einfach abholen können.

Für die Senderichtung:
Da mußt du in der ISR feststellen, ob es noch was zu senden gibt oder 
nicht. Wenn ja dann senden, wenn nicht dann mußt du den Senderequest der 
Hardware auf eine möglichst sinnvolle Weise abstellen - aber so, daß man 
ihn von außerhalb der ISR ohne Konurrenzprobleme wieder aktivieren kann.

Außerhalb der ISR mußt du:
- feststellen können, ob noch Platz ist im Ringpuffer.
- Daten in den Ringpuffer schreiben und den HW-Senderequest aktivieren 
können
- sinnvoll reagieren können, wenn der Sendepuffer noch voll ist. (das 
ist fast immer der Fall, da der µC regelmäßig schneller ist als eine 
Serielle).

Das alles mußt du im Lowlevel-Treiber tun, ganz egal, ob du nun 
obendrein auch noch DMA benutzen willst oder nicht. Und deshalb ist der 
Overhead durch DMA eben so nützlich wie ein Kropf - nämlich garnicht.

Was bleibt, ist die Frage, warum bloß du so insistierend auf HAL 
bestehen willst. Dieses Zeugs erledigt keine einzige der o.g. Aufgaben 
für dich, sondern erschwert selbige nur.

W.S.

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Was bleibt, ist die Frage, warum bloß du so insistierend auf HAL
> bestehen willst.

Die HAL verleitet zu der Annahme, dass man das Referenzhandbuch (mit den
Registern und Beschreibung der I/O Funktionen) nicht braucht, weil sie
ja alles abstrahiert. Wenn man sich als Anfänger mit den über 1000 Seite
Doku herum schlagen muss, ist man damit erstmal genug belastet.

Allerdings ist der Abstraktionsgrad der HAL nicht so hoch wie bei
Arduino und weit von Betriebssystemen wie Linux/Windows/MacOS entfernt.

Will sagen: Man muss zusätzlich zur HAL eben doch die Funktionseinheiten
detailliert kennen, sonst kann man die HAL nicht korrekt anwenden.

Solange ich nur eine einzige STM32 Mikrocontroller Serie (z.B. STM32F1)
verwende, muss ich mich dann schon fragen, wo dann der Mehrwert der HAL
ist. Warum soll ich zuerst den Chip und seine Register lernen, und dann
oben drauf nochmal so viele Seiten zur HAl lesen? Macht auf den ersten
Blick keinen Sinn.

Zwei Gründe sprechen allerdings für die HAL:

a) Sie enthält umfangreichen Code für einige Sachen, die man als
Einzelkämpfer sonst nicht hinbekommen würde: zum Beispiel USB
b) Wenn man auf eine andere STM32 Serie umsteigt, kann man weite Teile
des Quelltextes unverändert übernehmen.

Für a gibt es Alternativen (W.S. und Niklas Gürtler sei Dank), und b
trifft auf viele Bastler schlicht nicht zu - mich zum Beispiel. Mir
reicht die STM32F1/F3 Serie für alles, was ich machen will. Wenn die
nicht mehr lieferbar sind, bin ich längst zu alt für was auch immer
danach kommt (schätze ich).

Jedem Anfänger empfehle ich allerdings, wenigstens einen STM32 erstmal
ohne HAL zu erforschen, um dessen Grundlagen kennen zu lernen. Das ist
Voraussetzung, um die HAL zu verstehen.

von Florian (Gast)


Lesenswert?

Pauschal zu sagen "DMA und UART ist sinnlos" ist schon ne gewagte These: 
Bei geringen Geschwindigkeiten und/oder sporadischem Empfang von 
Nachrichten stimmt das Gesagte natürlich, Interrupt-getriebenes UART 
deckt die meisten Anwendungsfälle ab.

Das geht aber alles den Bach runter wenn man das UART Interface mal ein 
wenig ausreizt, da ist DMA ganz schnell vorne was CPU-Entlastung und 
Performance angeht, denn es erlaubt einem das Füllen der Buffer und das 
Auswerten der Daten vollständig parallel laufen zu lassen. Die 
zeitkritische Operation (Empfangenes Byte in Buffer kopieren) wird vom 
DMA Controller übernommen und es sinkt die Chance Daten zu verlieren 
wenn man keine Flusskontrolle nutzt.

Außerdem spart man sich den Interrupt-Eintritts+Austritts-Overhead von 
ca 22~26 Zyklen plus die Zeit die man braucht um das Byte zu kopieren 
und die Flags zu löschen, dass können schon mal ~50 Zyklen sein. Bei 
einem 48 MHz Cortex-M0 sind das ~1us Overhead pro Empfangenen Byte. 
Empfängst du jetzt mit 921600 bit/s (inklusive Start/Stopp Bit) dann 
kannst du alle ~11us ein Byte rein bekommen. Es geht also knapp 10% der 
verfügbaren Rechenzeit nur auf das Entgegennehmen der Daten drauf, mit 
DMA kann man sich das sparen. Würde man das UART Interface mit mehr als 
einem MBit/s betreiben, wird der Effekt dann nochmal verstärkt.

von W.S. (Gast)


Lesenswert?

Florian schrieb:
> Das geht aber alles den Bach runter wenn man das UART Interface mal ein
> wenig ausreizt, da ist DMA ganz schnell vorne was CPU-Entlastung und
> Performance angeht

Stimmt doch garnicht, denn man muß die Daten ja ohnehin bereitstellen 
und obendein auch noch den DMA einrichten. Und bei UART's mit Fifo (NXP) 
kann man bis zu 16 Bytes in einem ISR-Lauf in die HW schaffen.

Wir reden hier von einem UART und nicht von einer SPI-Schnittstelle, die 
bei manchen Displays zum Übertragen der anzuzeigenden Display-Inhalte 
benutzt wird.

Bei so einem Display steht von vornherein fest, wieviel Bytes für die 
Anzeige transferiert werden müssen und all die Zeichenvorgänge auf den 
Canvas im µC sind bereits vorbei. Für sowas von vornherein Feststehendes 
geht dann auch DMA.

Bei einem UART haben wir hingegen völlig asynchronen Verkehr in beiden 
Richtungen. Das ist das Kern-Kennzeichen des UART's - und ein knappes 
MBit/s ist für nen UART auch nicht die wirklich passende Verwendung:

Florian schrieb:
> Empfängst du jetzt mit 921600 bit/s (inklusive Start/Stopp Bit) dann
> kannst du alle ~11us ein Byte rein bekommen.

Und kann man das dann auch alle 11 µs auswerten? hehe?
Es ist doch eine völlige Fehlannahme, sich ausschließlich auf das 
Übertragen zu konzentrieren und dabei ganz zu vergessen, daß Sendedaten 
auch erzeugt und Empfangsdaten ausgewertet sein wollen. Machen das heuer 
die Heinzelmännchen oder eben doch der µC, der dafür so seine Zeit 
braucht?

W.S.

von Robert (Gast)


Lesenswert?

Die Entscheidung DMA vs. Interrupt hängt recht empfindlich vom 
Anwendungsfall ab. (Unvorhersehbar empfangene) Daten im DMA-Puffer 
können durchaus schwieriger und zeitaufwendiger auszuwerten sein und es 
wird in jedem Falle eine bestimmte Mindest-Speichermenge für den Puffer 
verlangt. Im Interrupt kann man hingegen auf bestimmte Daten SOFORT 
reagieren, gleich auswerten und verwerfen. Das kann allen Overheads 
zum Trotz effizienter sein. Man braucht im Extremfall dazu nicht mal 
einen Puffer. Fürs Senden ist allerdings immer DMA -so vorhanden- zu 
bevorzugen.

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Machen das heuer die Heinzelmännchen

Nein, das machen die vielen hundert Megaherz.

von Robert (Gast)


Lesenswert?

W.S. schrieb:
> Florian schrieb:
> Das geht aber alles den Bach runter wenn man das UART Interface mal ein
> wenig ausreizt, da ist DMA ganz schnell vorne was CPU-Entlastung und
> Performance angeht
>
> Stimmt doch garnicht, denn man muß die Daten ja ohnehin bereitstellen
> und obendein auch noch den DMA einrichten.

Natürlich stimmt das. Die UART im Interrupt-Modus betrieben kann die MCU 
gerade bei viel Speed und ein paar Anweisungen zuviel im Handler 
schnellstens in die Knie zwingen- und man wundert sich über 
undurchsichtige Fehlerbilder, die auf zu schmale verbliebene 
Zeitscheibchen für andere (Interrupt)Funktionen zurückgehen. Die 
Daten-Bereitstellung und DMA Einrichtung sind hingegen schnell erledigt 
und im Performance-Bedarf kaum der Rede wert.

von Florian (Gast)


Lesenswert?

> Stimmt doch garnicht, denn man muß die Daten ja ohnehin bereitstellen
> und obendein auch noch den DMA einrichten.
Das macht man einmal, während des Setups, danach läuft das Ganze ohne 
weitere Einflussnahme vor sich hin bis der Controller resettet wird, 
dafür gibt's doch den zyklischen Betriebsmodus.

> Bei so einem Display steht von vornherein fest, wieviel Bytes für die
> Anzeige transferiert werden müssen und all die Zeichenvorgänge auf den
> Canvas im µC sind bereits vorbei. Für sowas von vornherein Feststehendes
> geht dann auch DMA.
Bist du zu starsinning zu checken dass man auch mit DMA auf 
unterschiedlich große zufällige Nachrichten eingehen kann? Das wurde 
doch schon x-mal beschrieben und lässt sich problemlos umsetzen wenn die 
DMA Peripherie die entsprechenden Schnittstellen offenlegt.

> Und kann man das dann auch alle 11 µs auswerten? hehe?
> Es ist doch eine völlige Fehlannahme, sich ausschließlich auf das
> Übertragen zu konzentrieren und dabei ganz zu vergessen, daß Sendedaten
> auch erzeugt und Empfangsdaten ausgewertet sein wollen. Machen das heuer
> die Heinzelmännchen oder eben doch der µC, der dafür so seine Zeit
> braucht?
In meinem realen Anwendungsfall kommt es häufig vor, dass ein kleiner 
Header ausgewertet werden muss und dann kommt eine ewig lange Payload 
welche vom Controller nicht angefasst werden muss/soll, bis sie komplett 
eingetroffen ist, dann aber möglichst schnell verarbeitet werden soll. 
Da die DMA Hardware keine CPU-Zeit klaut während die Bytes reinpurzeln 
kann sich der ARM-Kern voll auf die Weiterverarbeitung der Payload 
konzentrieren, auch wenn das nächste Paket sofort im Anschluss kommt. 
Und das Kopieren in den Arbeitsbuffer, die Verschlüsselung und der 
Versand dürfen gerne mehr als 10us dauern da der Buffer das auffängt.

Wenn ich dafür Interrupts einsetzen würde ginge das auch, nur würde ich 
dann zu Fuß genau das machen was der DMA-Controller in Hardware und 
entkoppelt von dem ARM-Kern macht.

Es gibt natürlich auch Einsatzzwecke bei denen es keinen Sinn macht DMA 
zu nutzen, wurde aber auch schon durchgekaut und ist mir bekannt.

von W.S. (Gast)


Lesenswert?

Florian schrieb:
> Bist du zu starsinning zu checken dass man auch mit DMA auf
> unterschiedlich große zufällige Nachrichten eingehen kann? Das wurde
> doch schon x-mal beschrieben und..

..und du meinst, hier beleidigend werden zu müssen.

Nein, es ist nicht so, wie du schreibst. EInen DMA muß man zuerst 
einrichten und dann kann es an das Transferieren gehen. Das ist das 
exakte Gegenteil dessen, was eine asynchrone Schnittstelle leisten soll.

Dort kommen nicht nur die Empfangsdaten unvorhersehbar, sondern auch die 
Sendedaten kommen ebenso unvorhersehbar für den Treiber aus 
irgendwelchen Teilen der restlichen Firmware oder gar über andere Ports 
von woanders her.

Man muß sich also bei einem UART immer darauf einrichten, daß die Daten 
in beiden Richtungen immer in Anzahl und Zeitpunkt unvorhersehbar 
hereinkommen. Das schließt ein zuvoriges Setzen der Übertragungsmenge 
bei einem DMA aus.

Klaro?

W.S.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Florian schrieb:
>> Bist du zu starsinning zu checken dass man auch mit DMA auf
>> unterschiedlich große zufällige Nachrichten eingehen kann? Das wurde
>> doch schon x-mal beschrieben und..
>
> ..und du meinst, hier beleidigend werden zu müssen.

Wie oft sollen wirs dir denn noch sagen?
Da du hier andauernd mit dem selben Stuss kommst und nach freundlichen 
Diskussionen dank deiner borniertheit nach einiger Zeit dann wieder 
anfängst bläst der Gegenwind eben langsam stärker.
Zudem ist "starrsinnig" keine Beleidigung, sondern eine passende 
Zustandsbeschreibung.

Inzwischen hat dir hier im Forum eine 2 stellige Anzahl an Mitgliedern 
verklickert, dass du nur Müll schreibst und keine Ahnung von C hast.
Inzwischen fangen sogar einige an einen "W.S. Disclaimer" unter ihre 
Posts zu schreiben, damit du die nicht volltextest!

So wie es Florian beschrieben hat machen das übrigens zB 
Bluetoothstacks.
(HCI über UART).
Da kommen durchaus Baudraten von 1 bis 4 Mbaud über den UART mit HW 
Handshake zusammen und die Daten willste dir auf der unteren Ebene nicht 
ansehen, sondern 1zu1 zum BT Stack schaufeln.

Die Länge ließt man aus dem Header (mehr guckt man sich nicht an), der 
ist trotz unterschiedlichen Pakettypen immer der Gleiche mit der 
Längenangabe an derselben Stelle.
Also sachste dem DMA, dass er 5 Zeichen lesen soll, dann ließte die 
Länge aus und dann ließte nochmal 500+ Bytes per DMA.

Bei manchen MCUs kann der DMA sogar die UART Handshakeleitung direkt 
betreiben, auch ne schöne Sache.

Aber mach mal deinen UART schön weiter mit IRQs, wie in den 80ern in 
denen du mit deinen Magic Numbers und 3 mal denselben Code für 3 UARTS 
kopieren statt structs zu nutzen stecken geblieben bist!

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Dort kommen nicht nur die Empfangsdaten unvorhersehbar, sondern auch die
> Sendedaten kommen ebenso unvorhersehbar für den Treiber aus
> irgendwelchen Teilen der restlichen Firmware oder gar über andere Ports
> von woanders her.

Du hast wirklich Alzheimer oder?
In einem Thread wo du selbst auch deinen Müll mal wieder zum Besten 
gegeben hast hat doch genau jemand sowas gebaut für den TX.
"Unvorhersehbare" Sendedaten aus der FIFO per DMA senden.
Wenn ein DMA Transfer fertig ist guckt er eben ob noch was in der FIFO 
ist oder nicht un sendet den nächsten Block.

Wenn nur 1 Byte in der FIFO ist gibts eben einen  1 Byte DMA.
Aber das tut icht weh, das ist dann genauso "aufwändig" wie ein IRQ 
Transfer.

von Stefan F. (Gast)


Lesenswert?

Mw E. schrieb:
> Wie oft sollen wirs dir denn noch sagen?

Lass ihn doch und spare Dir die Energie für Bettsport auf.

Das gilt auch für die anderen Streithähne hier.

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.