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 | __irq void USART1_IRQHandler (void) |
2 | { char c; |
3 | int i, j; |
4 | |
5 | if (USART1_ISR & ((1<<5)|(1<<3))) // RX: Zeichen empfangen oder Overrun |
6 | { c = USART1_RDR; |
7 | i = U1Buf.InWP; |
8 | j = (i+1) & (IBLEN-1); |
9 | if (j!=U1Buf.InRP) |
10 | { U1Buf.InBuf[i] = c; |
11 | U1Buf.InWP = j; |
12 | }
|
13 | }
|
14 | |
15 | if (USART1_ISR & (1<<7)) // TX: Sendepuffer leer geworden |
16 | { i = U1Buf.OutRP; |
17 | if (i!=U1Buf.OutWP) // ob es was zu senden gibt |
18 | { USART1_TDR = U1Buf.OutBuf[i]; |
19 | U1Buf.OutRP = (i+1) & (OBLEN-1); |
20 | }
|
21 | else
|
22 | { 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.
Kanack schrieb: > 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.... http://www.st.com/content/ccc/resource/technical/document/application_note/d6/03/cb/dd/03/54/49/d6/CD00256689.pdf/files/CD00256689.pdf/jcr:content/translations/en.CD00256689.pdf
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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.