Moin,
die Frage ist schon seht oft im Netz aufgetaucht und wurde immer recht
unzureichend beantwortet. Ich möchte das Thema gern noch einmal
aufgreifen.
Umgebung:
STM32 CortexM
System Workbench
CubeMX
HAL-lib
Ich benötige ein gutes Konzept, um UART-Zeichen ohne größere
Prozessorlast zu empfangen. Problem ist dabei nicht ausschließlich die
durchschnittliche CPU-Zeit die bei einem reinem Interruptverfahren
"verloren" geht, es geht auch um die langen Ausführungszeiten der
Interrupts durch intensiven HAL-Code, obwohl das eine Problem natürlich
mit dem anderen Stark korreliert.
Ein Beispiel aus der Praxis: Um Implementierungsaufände zu sparen wurden
keine Sleep-Handling eingesetzt, es war ausreichend den Prozessor bei 2
MHz zu takten. Zwei UARTs mit FIFO im Interrupt-Betrieb wurden
eingesetzt. Sind beide UARTs auf 9600 Baud eingestellt, kommt es zu
Zeichenverlust auf den UARTs. Grund ist, dass der STM (jedenfalls den
ich verwende) nur ein Zeichen RX "FIFO" besitzt. Zwei UART-Interrupts
gefüllt mit HAL-Code führen bei dieser Konstellation schon zu
Zeichenverlust.
Die Idee liegt natürlich auf der Hand -> DMA.
Der DMA muss aber ebenfalls irgendwann im Interrupt aufgefrischt werden.
Fällt diese Zeit bei den zwei UARTs wieder genau aufeinander ->
Zeichenverlust und diesmal wahrscheinlich weniger deterministisch.
Die meisten Leute die im Netz gefragt hatten, hatte andere Probleme beim
DMA-Empfang... "Woher weiß ich vorher wie viele Zeichen über die UART
kommen? Wie soll ich denn dann mein DMA einstellen?"
Das Problem ist recht einfach gelöst. Die Startadresse des DMAs ist
bekannt und laut Datenblatt ist auch die aktuelle Schreibadresse während
der DMA läuft valide. Ein DMA-FIFO kann also funktionieren.
Um keine Interrupts beim RX auslösen zu müssen, gibt es ja die
Ringerbuffer Option. Die ist ja ganz gut und schön, aber damit kann ich
nicht sicher stellen ob meine FIFO übergelaufen ist. Wenn das
Hauptprogramm hängt und der Ringbuffer überläuft bekomme ich keine
Rückmeldung.
Ich hatte die Idee mit dem Eventsystem irgendwie bei jedem DMA-Byte
einen freien Timer als Counter zu benutzen und hochzuzählen. Damit
könnte ich mit Ringbufferbetrieb Testen ob es einen Überlauf gab und das
ohne Interrupts. Ich weiß nur nicht ob das funktioniert wie ich es mir
vorstelle...
Weitere Idee ist ein Scatter-Gatter DMA-Verfahren, dass einfach die STM
UART um ein paar mehr FIFO-Zeichen erweitert.
Was meint ihr? Einfach auf eine Überlauferkennung verzichten? Okay,
hängt natürlich stark vom Einsatzzweck ab...
VG
Klaus
Hier wurden schon mehrere Kriege geführt, da die altgebackenen die DMA
per UART nicht akzeptieren...
> Was meint ihr?
Ich meine bei 9600 Baud stimmt was bei dir im Programm nicht. Das ist
keine Geschwindigkeit für einen ARM. Du müssest eventuell die Interrupt
Priorität beider UART ändern und den fifo buffer erhöhen.
> Einfach auf eine Überlauferkennung verzichten? Okay,> hängt natürlich stark vom Einsatzzweck ab...
UART per DMA ist einfach, wenn du weisst wie viele Daten ankommen. Dies
ist aber nicht sicher! Was passiert, wenn es zu wenige/viele Daten
ankommen?! Aber auf die schnelle sicher hilfreich.
ST hat eine Appnote mit ein paar Vorschlägen wie man überprüft, ob die
Übertragung zu Ende ist. Z.B. mit den systick timer kann man jede ms
prüfen wie weit in den (circular) Buffer geschrieben wurde....
Diese Diskussion wäre interessant gewesen, wenn du >1 Mbaud transfers
hättest, sonst vermute ich eher ein Fehler im Programm.
Was hat das mit ARM oder nicht ARM zu tun? Das Verhältnis CPU-Speed zu
UART-Speed ist entscheidend.
Wenn der nur mit 2 MHz schleicht, dann sind das nur 2083 Takte/Befehle
pro UART Byte mit 9600 8N1 und dann schau dir mal den HAL-Quellcode in
den Interrupts an!
Ich erinnere mich gemessen zu haben, dass die CPU alleine zu 1/4 mit
empfangen + zweite UART weiterleiten ausgelastet war.
Priorität ändern? Dann sehe ich auf der Debug-UART nur noch quatsch und
wunder mich...
Im Systick den Überlauffall abprüfen könnte natürlich funktionieren.
Hast du einen Link zu der APP-Note?
VG
Basti
Was hat das mit ARM oder nicht ARM zu tun? Das Verhältnis CPU-Speed zu
UART-Speed ist entscheidend.
Wenn der nur mit 2 MHz schleicht, dann sind das nur 2083 Takte/Befehle
pro UART Byte mit 9600 8N1 und dann schau dir mal den HAL-Quellcode in
den Interrupts an!
Ich erinnere mich gemessen zu haben, dass die CPU alleine zu 1/4 mit
empfangen + zweite UART weiterleiten ausgelastet war.
Priorität ändern? Dann sehe ich auf der Debug-UART nur noch quatsch und
wunder mich...
Im Systick den Überlauffall abprüfen könnte natürlich funktionieren.
Hast du einen Link zu der APP-Note?
VG
Klaus
Ehrlich gesagt zwing dich niemand die HAL zu nutzen oder auch dein µC
mit 2mhz zu betreiben und wenn das deine Vorgabe ist, dann hättest du es
auch erwähnen können.
> Hast du einen Link zu der APP-Note?
Google einfach: stm32 dma uart application note
Keiner weiß welchen stm32 du hast.
Hallo Klaus,
bei 2MHz Taktfrequenz solltest Du in der Tat deinen Code optimieren,
dazu gehört fast notwendigerweise, den HAL zu strippen, da ist overhead
ohne Ende drin.
Ersetze auch deinen Debug UART durch ITM Ausgaben, dann hast Du
wenigstens keinen großen Heisenberg Effekt durch die Debuggerei.
DMA geht beim Empfangen, hat aber sehr viel versteckte Fallstricke. Du
solltest Dich beim Aufsetzen des DMA nicht darauf verlassen, was das
Protokoll als erwartete Zeichenanzahl liefert, da sonst bei Framing
Errors o.ä. Zeichen fehlen und damit mglw. ein aufgesetzter DMA receive
nicht wie erwartet zurückkommt. Dein Lösungsansatz mit dem Auslesen der
Ringbufferadresse verstehe ich nicht; damit kannst Du zwar im ISR
feststellen, wieviel Zeichen tatsächlich momentan kopiert wurden, aber
was Du ja eigentlich willst, ist die Anzahl der Interrupts minimieren,
oder? Und genau dazu willst Du ja eben vorher wissen, auf wievel
Zeichen Du wartest...
Die Abfragerei über SysTick etc. erscheint mir auf den ersten Blick eher
als Krücke. In 20 Jahren Kommunikationsentwicklung habe ich sowas noch
nie benötigt.
DMA ist i.W. auf der Transferseite bei größeren Paketen interessant und
kann die Tx Interrupts je nach Protokoll drastisch minimieren, was damit
dann auch wieder den receive ISRs mehr Zeit gibt (so das Protokoll
entweder Voll Duplex ist oder Du parallel auf mehreren UARTs arbeitest).
DMA bringt erst dann etwas, wenn man Blöcke bearbeitet. Mit hinreichend
grossen DMA Puffer lässt man den DMA im Ringmodus laufen und schaut
häufig genug auf den DMA Count. Hat sich der DMA Count geaendert, sind
neue Zeichen da, die man verarbeitet, Danach merkt man sich den neuen
DMA Zählerstand. Geht also im Prinzip ohne Interrupt. Wenn es hinreichen
oft Pausen in den Eingangsdaten gibt, kann man sich auch einen Idle
Interrupt geben lassen, um dann mit der Verarbeitung anzufangen.
Klaus schrieb:> Umgebung:> STM32 CortexM> System Workbench> CubeMX> HAL-lib
Ich meine, daß genau DAS der Fehler ist, den du da machst. Guck dir
mal einen typischen UART-Interrupt-handler an:
1
__irqvoidUSART1_IRQHandler(void)
2
{charc;
3
inti,j;
4
5
if(USART1_ISR&((1<<5)|(1<<3)))// RX: Zeichen empfangen oder Overrun
{USART1_CR1&=~(3<<6);// nö, TXEIE und TCIE ausschalten
23
}
24
}
25
}
Der erledigt beides: Empfangen und Senden - und wie man sieht, ist er
ausgesprochen kurz. Was willst du da mit DMA verbessern? Du schaffst mit
DMA nur eines: mehr Verwaltungsaufwand und mehr Komplikationen. Aber
keine Erleichterung der Situation.
Aber um die Sache wie oben gezeigt eben besser zu lösen, müßtest du das
aufgedunsene ST-Zeugs schlicht und einfach weglassen - und die meisten
Leute, die mir hier in diesem Forum aufgefallen sind, können das nicht,
weil sie einfach mental zu unselbständig sind.
W.S.
Ich nutze einen F103 mit 72 MHz. 2 USARTs schreiben/senden per Interrupt
in 2 Puffer mit 9600 Baud und 230,4 KBaud. In der main werden zyklisch
diverse Sensoren per SPI/I2C abgefragt. Mein Controller langweilt sich
dabei...
mfg
Klaus schrieb:> Ich hatte die Idee mit dem Eventsystem irgendwie bei jedem DMA-Byte> einen freien Timer als Counter zu benutzen und hochzuzählen. Damit> könnte ich mit Ringbufferbetrieb Testen ob es einen Überlauf gab und das> ohne Interrupts. Ich weiß nur nicht ob das funktioniert wie ich es mir> vorstelle...
Was spricht dagegen, statt des Timers direkt den DMA_CNDTR zu verwenden?
Oder blockt Dich da der HAL?
Ringpuffer groß genug machen und hinreichend oft nachschauen.
2MHz? Welche Maschine?
Wenn es Dir um die Energiebilanz geht - es ist oft effizienter, die MCU
kurz schnell laufen zu lassen und dann wieder tief Schlafen zu schicken.
Wobei 2MHz für die bisher bekannte Aufgabenstellung kein Problem sein
sollten. Alles in allem ist dieser Thread wieder mal ein Spitzenargument
gegen HAL in Produktivsystemen.
HAL + Datasheet + Reference Manual -> Anleitung zum selber schreiben
Moin,
> Was spricht dagegen, statt des Timers direkt den DMA_CNDTR zu verwenden?> Oder blockt Dich da der HAL?> Ringpuffer groß genug machen und hinreichend oft nachschauen.
Ja, dass wurde schon öfter genannt, wenn man einen Interrupt hat, der
hinreichend oft genug schaut ob ein Überlauf eingetreten sein könnte,
dann funktioniert das. Mit einer Main-Loop mit unbekannter
Bearbeitungszeit müsste man wenigstens ein Rückmeldung bekommen, dass es
auf dem UART-Bus gerade zu Problemen gekommen sein könnte. Funktioniert
leider so nicht. Wenn es also keine besser Lösung für die von mir
festgelegten Umgebungsbedinungen gibt, dann würde ich es mal so
versuchen.
Oder habe ich was wesentliches Übersehen? Schön wäre also eine weitere
Zählvariable die angibt wie oft der Rinbuffer DMA schon
durch-/übergelaufen ist. Dann könnte man beim RX-FIFO-READ ganz einfach
abprüfen ob etwas verloren gegangen ist.
Das die HAL natürlich nicht die schnellste und effizienteste ist, sollte
ja klar sein...
Dafür soll die HAL viel zu viele Anwendungen erschlagen (RTOS etc.).
Die Vorgaben der neuen HAL kommen ja soweit ich weiß aus den Dogmas von
ARM selbst. Ich bin selbst kein riesen Fan der HAL, aber in einem
Betrieb wo es mehr als ein Programmier gibt, ist es wenig zielführend
wenn jeder seine eigenes Süppchen kocht. Gerade wenn man von STMF0 bis
STMF7 alles bedient.
@STMler Das Konzept aus der APPNOTE finde ich komisch bis schlecht. Aber
danke für den Link.
@Felix F. Klingt als wenn du der CPU-Flüsterer bist... woher kommt
dieses Gefühl das deine CPU sich langweilt? Vielleicht steht auch alles
auf der Kippe und im richtigen Moment knallt es?
Immerhin hast du schon mal 3125 Takte pro Zeichen bei der schnellen UART
Zeit, um ein Zeichen abzuholen. Da du nur eine schnelle UART hast,
könntest du den Interrupt höher priorisieren und damit den Interrupt
auch Nested aufrufen lassen.
@W.S. Es geht manchmal einfach nicht ums können...
VG
Klaus
>> Das die HAL natürlich nicht die schnellste und effizienteste ist, sollte> ja klar sein...> Dafür soll die HAL viel zu viele Anwendungen erschlagen (RTOS etc.).> Die Vorgaben der neuen HAL kommen ja soweit ich weiß aus den Dogmas von> ARM selbst. Ich bin selbst kein riesen Fan der HAL, aber in einem> Betrieb wo es mehr als ein Programmier gibt, ist es wenig zielführend> wenn jeder seine eigenes Süppchen kocht. Gerade wenn man von STMF0 bis> STMF7 alles bedient.>
Nein nein, die "HAL" von ARM ist die CMSIS, die ARM pusht, um
herstellerübergreifend kompatibel zu sein. HALs sind genau umgekehrt
Abstraktionen, die dazu dienen sollen, Prozessorfamilien *eines
Herstellers* möglichst interoperabel zu machen. Dass die beiden Layer
manchmal der Formel "plus + plus = minus" folgen, weiss Jeder, der sich
mal durch Beispielapps durchgeackert hat, in denen Beides nebenläufig
vorkommt...