Hallo zusammen,
ich habe ein kleines Problem mit dem USART-DMA-Modus.
Dazu erstmal der Init-Code:
UART_HandleTypeDef g_UsART3_Handle;
DMA_HandleTypeDef g_hdma_usart3_rx;
DMA_HandleTypeDef g_hdma_usart3_tx;
g_UsART3_Handle.Instance = USART3;
g_UsART3_Handle.Init.BaudRate = 19200;
g_UsART3_Handle.Init.WordLength = UART_WORDLENGTH_8B;
g_UsART3_Handle.Init.StopBits = UART_STOPBITS_1;
g_UsART3_Handle.Init.Parity = UART_PARITY_NONE;
g_UsART3_Handle.Init.Mode = UART_MODE_TX_RX;
g_UsART3_Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
g_UsART3_Handle.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&g_UsART3_Handle);
g_hdma_usart3_rx.Instance = DMA1_Stream1;
g_hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
g_hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
g_hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
g_hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
g_hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
g_hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
g_hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
g_hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
g_hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&g_hdma_usart3_rx);
__HAL_LINKDMA(&g_UsART3_Handle,hdmarx,g_hdma_usart3_rx);
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
HAL_UART_Receive_DMA(&g_UsART3_Handle, &g_usartRX, g_usartRXlen);
Im DMA Full-Callback sollen dann die Daten abgefragt werden.
Das funktioniert auch soweit.
Das Problem ist nun, dass ich Daten-Frames mit einer definierten Länge
übertragen möchte (Frame-Layout damit die Daten leichter zu verarbeiten
sind). Es kann jedoch vorkommen, dass durch ein Übertragungsfehler Bytes
verloren gehen und somit der DMA aus dem tritt kommt. D.h. im DMA stehen
die Daten-Bytes nicht mehr an der richtigen Stelle.
Die Frage ist nun, wie ich den DMA-Stream "Löschen" bzw zurücksetzen
kann, damit dieser bei einem fehlerhaften Frame wieder die Daten an die
richtige Stelle schreibt.
Danke schon mal für hilfreiche Ideen.
In ihre definierte Packet soll auch einen CRC checksum implementiert
sein. Damit kan jedes Packet ueberprueft werden. Wen Falsch, soll das
neu gesendet werden. Zweitens sollen sie eine time_out für die DMA
forsehen. Damit weisst du wen da ein Zeichen fehlt, den ende
DMA-transfer kommt nur nach das alle Zeichen von ein Packet enpfangen
sind.
Keine DMA ist mit einer Prophetschaltung ausgerüstet, die weiss, wann
nun ein fehlerhafter Frame ankommt. DMA bedeutet ja extra, das es nicht
über die CPU läuft und deswegen auch nicht geprüft wird.
Du solltest also eher die gute alte USART_RX_Complete ISR benutzen, die
dir einen Buffer füllt und prüft, ob das alles so sein kann. Wenns die
Prüfung besteht, gibts eine OK Flagge, wenn nicht, Fehlerchen ausgeben
und neuen Frame anfordern.
Und wenns besonders fix gehen soll, kannst du ja den CCRam benutzen,
immerhin sind da ungenutzte 64kByte (beim F407 zumindest).
Das Problem ist hier, dass sobald der Buffer voll ist, erst dann wird
ein Interrupt ausgefühlt. Man kann auch per Protokoll überprüfen, was
fehlt und diese Daten anfordern.
Leider kenne deine Libraray nicht gut, da ich mit der Standart Lib
arbeite.
Mein Vorschlag wäre:
1
g_hdma_usart3_rx.Init.Mode=DMA_Mode_Normal;
2
3
//Pseudo Code, DMA Channel muss erst ausgemacht werden,
4
//damit neue Bufferlänge Wirkung hat!
5
DMA_UART_RX(uint8_tnum2read)
6
{
7
DMA_Cmd(ChannelRx,DISABLE);
8
DMA_SetCurrDataCounter(x,x,num2read);
9
DMA_Cmd(ChannelRx,ENABLE);
10
}
Sry, aber ich denke kaum jemand nutzt die HAL? Bibliothek.
mfg
Für mich stellt sich die Frage, ob sich für die paar Bytes der ganze DMA
Aufwand überhaupt lohnt. Das ist doch mit einer ISR viel leichter und
schneller gelöst.
Wären das 1000 oder 10000 Bytes, sähe das evtl. anders aus, obwohl ich
mit ADC und I2S in der ISR Behandlung gute Erfahrungen gemacht habe und
immer genügend Rechenleistung in Reserve hatte (bei Audio mit 48kHz
Samplerate).
Auch ein Restart der DMA im Fehlerfall birgt immer das Risiko, falsch
auf einen Frame einzurasten oder ein Byte zu verpassen.
Bin auch kein HALer sondern StdLibler und kann deswegen auch nicht
beurteilen, ob evtl. die HAL das Problem ist.
Matthias S. schrieb:> Für mich stellt sich die Frage, ob sich für die paar Bytes der ganze DMA> Aufwand überhaupt lohnt.
Ein klares JA, aber nur wenn man den Vorteil der DMA auch einsetz (ISR).
Beim pollen nutze ich es auch immer, da der Code sich stark minimiert.
Matthias S. schrieb:> Auch ein Restart der DMA im Fehlerfall birgt immer das Risiko, falsch> auf einen Frame einzurasten oder ein Byte zu verpassen.
Bei einen konstanten Buffer ist das größte Problem ein overflow. Nehmen
wir an beim Initialisieren des UART sendet der Tx ein paar sinnlose
bytes. Dann werden die Daten gesendet [header]data[terminator]+paar
bytes.
Genau diese Aufgabe gilt es jetzt zu lösen. Fährt man die gleiche
Schiene, dann muss der overflow erkannt werden und einen restart der
Daten anfordern.
Andere Möglichkeit wäre ein FIFO zu implementieren mit einen großzügigen
Buffer. Oder auch wie zuvor geschrieben, einen Timer erstellen der
guckt, fertig oder nicht mit der Übertragung?
Man kann es auch ohne DMA lösen, aber mit sehr viel Mehrcode.
Matthias S. schrieb:> Bin auch kein HALer sondern StdLibler und kann deswegen auch nicht> beurteilen, ob evtl. die HAL das Problem ist.
Willkommen im Club. :)
aSma>> schrieb:> Man kann es auch ohne DMA lösen, aber mit sehr viel Mehrcode.
Wieso das? Eine simple RX complete ISR ist doch kein Mehrcode. Alleine
die Fehlerbehandlung der DMA ist grösser als die ganze ISR.
Nico schrieb:> ich habe ein kleines Problem mit dem USART-DMA-Modus.
Das sehe ich anders: Du hast Probleme mit dem Durchsteigen durch dein
eigenes Vorhaben, kurzum falsch angedacht.
Wozu soll bitte sehr denn ein DMA-Betrieb für sowas wie einen USART gut
sein?
Glaubst du, damit besser oder schneller zu werden? Allein wenn ich mir
deine Initialisierungsorgie anschaue, bezweifle ich dies.
Als nächstes frag ich mich, was du denn tun willst, wenn gerade dann,
wenn dein DMA-Puffer voll ist und die DMA fertig ist, weitere Zeichen
hereingerauscht kommen? Ignorieren? Ungewollt verlieren?
Nee, bei USART's macht man das anders: Zeichenempfang in einen
Ringpuffer und Zeichenausgabe aus einem Ringpuffer. Beides natürlich per
Interrupt, so daß die eigentlichen Programmschichten, die den gewünschen
Algorithmus praktizieren, lediglich mit Puffern zu tun haben, aus denen
und in die man zeichenweise den I/O-Strom fließen läßt. Dieses puffernde
Verhalten eines guten seriellen Treibers kann man per DMA eben niemals
nachbilden, denn dazu ist CPU-"Intelligenz" vonnöten.
Und es möge hier keiner behaupten, daß das zugehörige Interruptprogramm
aufwendig oder umfänglich sei. Das ist Quatsch.
W.S.
Nico schrieb:> Die Frage ist nun, wie ich den DMA-Stream "Löschen" bzw zurücksetzen> kann, damit dieser bei einem fehlerhaften Frame wieder die Daten an die> richtige Stelle schreibt.
Der f4 kann IIRC einen Timeout-Interrupt auslösen. Timeout auf z.b. 5
Zeichen festlegen. Wenn der Timeout anschlägt bevor der DMA fertig ist
dann hats wo ein Zeichen gefressen.
Modbus-RTU macht Telegram-Erkennung per Timeout, ist sicher nicht die
schlechteste Geschichte. Wenn Mist ankommen kann dann braucht man so
oder so einen zeitbasierten Watchdog, weil ein Protokoll-Parser ganz
gleich welcher Art sich dauerhaft verhaspeln kann.
W.S. schrieb:> Dieses puffernde> Verhalten eines guten seriellen Treibers kann man per DMA eben niemals> nachbilden, denn dazu ist CPU-"Intelligenz" vonnöten.
Um die Intelligenz muss sich der Programmierer kümmern. Prizipiell kann
man schon die DMA nehmen, wenn man genau weiß wieviele Bytes man
empfängt. Eine Möglichkeit um overflow zu entgehen, könnte der
Half-Transfer Interrupt genutz werden. Beispielsweise: Man will 20bytes
empfangen. Den Buffer setzt man auf 40 Bytes. Mit ein wenig Phantasie
lässt sich schon was machen.
W.S. schrieb:> Und es möge hier keiner behaupten, daß das zugehörige Interruptprogramm> aufwendig oder umfänglich sei. Das ist Quatsch.
Wenn man halt die Möglichkeit hat die CPU zu entlassen, warum sollte man
es nicht auch anstreben? Es gibt viele Wege nach ROM. Man muss nur den
richtigen aussuchen. Über das Mittelmeer, wie die Asylanten dauert
einfach zu lange. Ich nehme das Flugzeug.
aSma>> schrieb:> Wenn man halt die Möglichkeit hat die CPU zu entlassen, warum sollte man> es nicht auch anstreben?
Ich muss aber bei einem F4XX nicht die CPU entlasten, um 4 Bytes vom
USART zu empfangen. Das ist sicher optimieren an der falschen Stelle,
abgesehen von der, wie wir ja sehen, Fehleranfälligkeit.
Eine DMA ist sicher prima, wenn ich genau weiss, wieviel und wohin es
gehen soll. Beim Mehrkanal ADC gibt es ja sogar nur diese Option.
Aber nicht für 4 Bytes über die serielle.
aSma>> schrieb:> Um die Intelligenz muss sich der Programmierer kümmern. Prizipiell kann> man schon die DMA nehmen, wenn man genau weiß wieviele Bytes man> empfängt.
Eben genau diese Annahme ist der Knackpunkt. Sie ist nämlich IMMER
falsch. Bei jeder seriellen Verbindung muß man damit rechnen, daß
selbige unvollständig ist oder gestört oder sonstwas. Die Kommunikation
findet nämlich immer mit einem anderen Ding statt, welches schlichtweg
unabhängigig ist vom eigenen Programm.
Folglich darf man niemals damit rechnen, an irgend einer Stelle im
eigenen Programm (oder zu einem bestimmten Zeitpunkt) genau soundso
viele Bytes zu empfangen. auf solche Annahmen darf man sein Programm
niemals aufbauen, sonst erleidet man Schiffbruch.
Also ist es schlichtweg sinnlos, eine DMA auf eine bestimmte Anzahl
Bytes für einen bestimmten Puffer zu programmieren und dann quasi blind
losrennen zu lassen. Stattdessen muß man eben die Zeichen in der o.g.
Interrupt-Routine empfangen und in einen hinreichend großen Ringpuffer
packen. Die Grundroutine hingegen muß allzeit prüfen, ob was angekommen
ist, muß es abholen und parsen und in einen weiteren (nicht-Ring-)Puffer
schreiben. Wenn das aktuelle Zeichen nicht dem entspricht, was man an
dieser Stelle erwartet, dann muß man eben das Bieherige verwerfen und
nach der nächsten Synchronisier-Gelegenheit Ausschau halten. Also wieder
auf Zeichen warten, auf Synchronisier-Zeichen oder -Zeichenfolge warten
und dann weitermachen. All dies geht immer einzelzeichenweise und
niemals blockweise aus oben genannten Gründen.
W.S.
Matthias S. schrieb:> Ich muss aber bei einem F4XX nicht die CPU entlasten, um 4 Bytes vom> USART zu empfangen. Das ist sicher optimieren an der falschen Stelle,> abgesehen von der, wie wir ja sehen, Fehleranfälligkeit.> Eine DMA ist sicher prima, wenn ich genau weiss, wieviel und wohin es> gehen soll. Beim Mehrkanal ADC gibt es ja sogar nur diese Option.>> Aber nicht für 4 Bytes über die serielle.
Ja, gut nehmen wir an, dass man super lange Leitung hat und baud von
9600 ausgesetzt ist und kriegt 4096 bytes. Der TE hat nichts darüber
erzählt. Er wollte nur wissen wie man den DMA counter rücksetzt.
W.S. schrieb:> Eben genau diese Annahme ist der Knackpunkt. Sie ist nämlich IMMER> falsch. Bei jeder seriellen Verbindung muß man damit rechnen, daß> selbige unvollständig ist oder gestört oder sonstwas. Die Kommunikation> findet nämlich immer mit einem anderen Ding statt, welches schlichtweg> unabhängigig ist vom eigenen Programm.
Genau das ist die Annahme von AVR Programmierer. Man muss mal auch den
horizont erweitern können! Bei Störung, Fehler, Verzögerung etc. kann
man immer tätig werden. Ich verstehe schon, wenn man einmal ein Konzept
ausgearbeitet hat, dann kann man dieses auch weiter nutzen.
Ich habe gleich mit stm32 angefangen zu programmieren, deshalb bin ich
vielleicht mehr offen für alles. Mein Beispiel zuvor mit Halb transfer
Interrupt war vielleicht unglücklich gewählt. Aber die Ideologie ist
nicht verkehrt, wenn man mit ein wenig Gehirnschmalz sich ein echtes
FIFO mit DMA basteln will.
Wobei der M3 Cortex kein echtes Fifo bestizt, vielleicht der F4? Habe
ich schon vergessen.
W.S. schrieb:> Also ist es schlichtweg sinnlos, eine DMA auf eine bestimmte Anzahl> Bytes für einen bestimmten Puffer zu programmieren und dann quasi blind> losrennen zu lassen. Stattdessen muß man eben die Zeichen in der o.g.> Interrupt-Routine empfangen und in einen hinreichend großen Ringpuffer> packen.
Kommt immer wieder auf den Einsatzort an!
Nehmen wir mal das Beispiel: Ich möchte eine Echtzeit von 1ms mit 5Mbaud
erstellen. Es sollen dabei 256bytes übertragen werden. Ich würde dabei
nie auf die Idee kommen normalen Interrupt zu nehmen, sondern ich würde
DMA einfach pollen. Ist doch kein Problem. Ein timeout programmieren,
falls was schiefgegangen ist und basta. Zum Beispiel als
Kommunikationschnittstelle mit den PC. Ist mein Regler erstellt worden,
dann kommt UART eh wieder weg.
mfg
aSma>> schrieb:> Ja, gut nehmen wir an, dass man super lange Leitung hat und baud von> 9600 ausgesetzt ist und kriegt 4096 bytes. Der TE hat nichts darüber> erzählt.
Ich verstehe von deinem Satz nur die Hälfte, aber die 4 Bytes hat der TE
doch genannt:
Nico schrieb:> Ein Frame soll bspw aus 4 Datenbytes (x -> S.u.) bestehen. Es kann nun> vorkommen, dass beim Senden eines Frames nicht 4 sondern nur 3> Datenbytes ankommen (Frame 1)W.S. schrieb:> Also ist es schlichtweg sinnlos, eine DMA auf eine bestimmte Anzahl> Bytes für einen bestimmten Puffer zu programmieren und dann quasi blind> losrennen zu lassen.
Zustimmung.
Hallo zusammen,
danke nochmals für eure anregungen. Allerdings geht die Dikusion hier an
der eigentlichen Frage vorbei.
Mir ging es lediglich darum zu erfahren, ob und wie ich den DMA
zurücksetzen bzw. löschen kann. Habe dazu jetzt auch ein ansatz. Sollte
dieser funktionieren werde ich meine Lößung posten.
Ein letzter Satz zur Sinhaftigkeit der Nutzung eines DMA zur übertragung
einer festen Frame-länge:
Angenommen bei 100000 übertragungen treten 10 Fehler auf. In diesem Fall
muss der µC 10x den Fehler (wie auch immer) behandeln. Bei den
restlichen 99990 übertragungen hat der µC keine zusätzliche Arbeit und
das Datenframe kann direkt verarbeitet werden. Lohnt sich aus meiner
Sicht also auf jeden Fall.
Grüße
Nico schrieb:> Mir ging es lediglich darum zu erfahren, ob und wie ich den DMA> zurücksetzen bzw. löschen kann. Habe dazu jetzt auch ein ansatz. Sollte> dieser funktionieren werde ich meine Lößung posten.
Siehe:
aSma>> schrieb:> Datum: 14.04.2016 14:58Nico schrieb:> Außerdem die alten bewährten Methoden (auf Terminierendes zeichen im> rxIr zu warten)werden irgendwann ja langweilig ;)
Hauptsache du hast nur einen Bruchteil von der Diskussion wahrgenommen.
mfg
>Bei den>restlichen 99990 übertragungen hat der µC keine zusätzliche Arbeit
stimmt nicht. Der Frame muss immer auf Korrektheit geprüft werden. Das
geht norm. nicht ganz ohne CPU.
Und eine DMA muss auch nicht "ausser Tritt" kommen, wenn ein
Übertragungsfehler (auf der Übertragunsstrecke) aufgetreten ist.
Lediglich der gesamte Frame enthält dann Fehler (was ja später
kontrolliert wird).
Wenn man schon nach jeder einzelnen Teilübertragung eine
Fehler-Überprüfung machen wollte, wäre die Nutzung von DMA unnötig und
umständlich.
Nico schrieb:> Ein letzter Satz zur Sinhaftigkeit der Nutzung eines DMA zur übertragung> einer festen Frame-länge:> Angenommen bei 100000 übertragungen treten 10 Fehler auf. In diesem Fall> muss der µC 10x den Fehler (wie auch immer) behandeln. Bei den> restlichen 99990 übertragungen hat der µC keine zusätzliche Arbeit und> das Datenframe kann direkt verarbeitet werden. Lohnt sich aus meiner> Sicht also auf jeden Fall.
Ich verstehe dein Argument nicht.
Also, du hast per DMA 99990 fehlerfreie Übertragungen gemacht, nun steht
der Kram im RAM und dann? Was dann? Braucht es zur weiteren Verarbeitung
keine CPU?
Nee, die CPU-Leistung ist immer gefordert, weil es ja keinerlei Vorteile
bringt, die Daten erstmal vom UART in den RAM zu befördern und sich
dabei zu denken "hach, was hab ich jetzt die CPU entlastet".
Anschließend muß die CPU den Kram aus dem RAM holen anstatt aus dem
Empfangsregister des UART und weiterverarbeiten - wo ist da der
Entlastungseffekt? Ich seh da eher das Gegenteil: du mußt dich nicht nur
um den UART kümmern, sondern obendrein noch um die DMA. Oder setzt die
sich selbst auf?
W.S.
>Anschließend muß die CPU den Kram aus dem RAM holen anstatt aus dem>Empfangsregister des UART und weiterverarbeiten - wo ist da der>Entlastungseffekt?
Mit DMA kann die CPU Daten "in einem Rutsch" in den DMA-Puffer schreiben
oder daraus lesen. Ohne DMA müsste die CPU ggfs bereits nach jedem Byte,
Wort usw (je nach FIFO-Tiefe) tätig werden; was ein riessen Unterschied
ist.
Allerdings ist es oft so, dass -je nach Architektur- bei aktiver DMA der
CPU Cyclen "gestohlen" werden.
MCUA schrieb:> Mit DMA kann die CPU Daten "in einem Rutsch" in den DMA-Puffer schreiben> oder daraus lesen. Ohne DMA müsste die CPU ggfs bereits nach jedem Byte,> Wort usw (je nach FIFO-Tiefe) tätig werden; was ein riessen Unterschied> ist.
Du schreibst wirr. Entweder die CPU bewegt die Daten oder die DMA.
Aber verarbeitet werden sie immer nur per CPU.
Und nochwas: selbst bei 115000 Baud kommen die Byte mit nur so etwa 10
kHz hereingerauscht. Das ist grottenlangsam im Vergleich zu dem, was ein
heutiger µC so drauf hat. Das Begrenzende ist tatsächlich das
Weiterverarbeiten und nicht der Empfang nebs Puffern.
W.S.
Hi Nico,
Nico schrieb:> Die Frage ist nun, wie ich den DMA-Stream "Löschen" bzw zurücksetzen> kann, damit dieser bei einem fehlerhaften Frame wieder die Daten an die> richtige Stelle schreibt.
ich habe gerade die gleiche Aufgabe. Für's Senden ist DMA wirklich
problemlos. Beim Empfangen kommst Du dann eigentlich nicht um ein
Timeout herum (HAL_UART_DMAStop).
Wenn Du Daten zyklisch bekommen würdest und mit der Latenz von einem
Packet leben könntest, dann könntest Du auf den Timer verzichten und
müsstest die empfangenen Daten nur prüfen und defekte entsprechen
verwerfen.
mfg Torsten
>Du schreibst wirr.
Wer hat denn das hier
>Anschließend muß die CPU den Kram aus dem RAM holen anstatt aus dem>Empfangsregister des UART und weiterverarbeiten - wo ist da der>Entlastungseffekt?
geschrieben?
>Entweder die CPU bewegt die Daten oder die DMA.
Muss nicht. Es kann sein, dass die CPU zuerst (an einem(!)) Daten zum
DMA-Puffer schreibt, und der transferiert es dann weiter (dann ohne
CPU).
Ausserdem düfte jedem hier klar sein, dass CPU Und DMA Daten bewegen.
Scheinbar kennst du die Vorteile (*) von DMA nicht, sonst hättest du das
so nicht geschrieben.
(*)theoretische und praktische unterscheiden sich, wie schon erwähnt
>Das Begrenzende ist tatsächlich das>Weiterverarbeiten und nicht der Empfang nebs Puffern.
Doch.
Bei höheren Bitraten wäre das Begrenzende definitv die CPU(-Last), weil
die nach jedem Byte/Wort tätig werden müsste.
W.S. schrieb:> Das Begrenzende ist tatsächlich das> Weiterverarbeiten und nicht der Empfang nebs Puffern.
ISR ist nicht gratis. Für jedes Zeichen ISR enter/leave machen summiert
sich, MCU braucht u.U. auch mehr Strom bzw. muss jedesmal aus Sleep
aufwachen.
Bei DMA-Empfang gibts nur einen ISR-Aufruf bei Ende und/oder Timeout.
Torsten R. schrieb:> Wenn Du Daten zyklisch bekommen würdest und mit der Latenz von einem> Packet leben könntest, dann könntest Du auf den Timer verzichten und> müsstest die empfangenen Daten nur prüfen und defekte entsprechen> verwerfen.
Timeout-Interrupt (RTOF) des UARTS verwenden (so vorhanden).