Hallo,
ich versuche derzeit mittels der DMA Daten (16x Bytes) über die UART3 zu
empfangen.
Ohne DMA funktioniert es, aber jetzt sitze ich schon eine Zeit lang dies
mit der DMA zu erledigen.
Ich stelle mir die Vorgehensweise so vor: Es werden an UART3-RX 16 Bytes
gesendet. Sobald alle 16 Bytes empfangen und in ein Array gespeichert
wurden, wird ein Interrupt ausgelöst und in diese Funktion gesprungen:
DMA1_Channel3_IRQHandler
Leider wird aber nie in die ISR gesprungen. Daten werden aber an RX
übertragen (mit Oszi gemessen bzw. wie bereits erwähnt: ohne DMA
funktioniert das Empfangen)
Hier mein Code für die DMA:
1
uint8_treceive[16]={};// array in which received 8bit values are stored
2
3
voidDMA_UART3_receive_values()
4
{
5
6
// DMA1 Channel 3 - UART3 RX
7
DMA_Cmd(DMA1_Channel3,DISABLE);// USART3_TX on Channel 2
Frank M. schrieb:> Musst Du hier nicht eher ein DMA_Flag abfragen?
Danke für den Hinweis. Werde es überarbeiten.
Aber mein Problem ist, dass nicht einmal in die Funktion void
DMA1_Channel3_IRQHandler(void) gesprungen wird...
har schrieb:> Aber mein Problem ist, dass nicht einmal in die Funktion void> DMA1_Channel3_IRQHandler(void) gesprungen wird...
Das hier:
NVIC_EnableIRQ(DMA1_Channel3_IRQn);
kommt mir arg wenig vor. Ich kenne da eher so etwas in der Form:
Im Internet findest Du auch jede Menge Beispielcode, wenn Du nach
stm32 dma usart receive
googelst.
EDIT:
Den Aufruf von NVIC_PriorityGroupConfig() brauchst Du wohl nicht
unbedingt.
Hallo har,
ein Wort der "Warnung:" Ich mache Serial Comms täglich auf etlichen
Plattformen und mit vielen vielen Protokollen, aber die Kombination RX
und DMA ist eines der Dinge, von denen ich die Finger lasse. Zunächst
mal klappt das nur bei Protokollen mit deterministischer (statischer)
Paketgrösse (bei variabel grossen Paketen musst Du eh den Header
Zeichen- oder chunkweise lesen, bis die Paketgrösse decodiert ist und Du
einen DMA aufsetzen kannst; bei Framingprotokollen ohne Kenntnis der
Paketgrösse geht eh nichts mit DMA), und dann hast Du ein Problem, wenn
mal ein Zeichen im Paket verloren geht und dein DMA nicht mehr
zurückkommt... bei den "gängigen" Baudraten (9600-57600) langweilt sich
ein normal schneller Prozessor eh zu Tode, selbst wenn für jedes Zeichen
ein Interrupt ausgelöst wird UND eine Dauerlast auf der Schnittstelle
ist.
Ausserdem setzt so eine Architektur in jedem Fall Protokollkenntnisse im
ISR voraus; das kann in manchen Anwendungen ok sein, ist aber oft nicht
wünschenswert.
Auf der Tx Seite kann DMA aber je nach Protokoll durchaus Vorteile
bringen.
Just my EUR 0,02, als Übung hat es aber sicherlich seinen Wert...
Das stimmt nicht, auch das auskommentierte nicht.
Du musst die Adresse des ersten Byte im Array angeben.
"receive[0]" ist nur der Wert im ersten Array element.
Du brauchst den Pointer, also entweder "&receive[0]" oder einfach
"receive".
Speicheradressen sind beim Cortex-M 32bit breit, um die Compiler-Warnung
wegzubekommen musst du also zu uint32_t casten.
Wenn du zu uint8_t castest (wie im Kommentar) schneidest du die oberen
24bit der Adresse weg.
Also entweder
Hallo Ruediger Asche,
ich gebe dir grundsätzlich Recht und bin auch deiner Meinung.
Ursprünglich habe ich das Empfangen ohne DMA durchgeführt.
Nun habe ich aber die Anforderung, dass die Kommunikation mit 9MBaud
erfolgen soll.
Wenn ich das ohne DMA, aber mir Interrupts mache, gibt es folgenden
Nachteil:
ein Datenpaket benötigt für die Übertragung nur 1,11us. Die Abarbeitung
der zugehörigen ISR benötigt jedoch 3us (mit Oszi gemessen).
Das heißt, ich empfange das erste Byte, aber alle weiteren werden
"versäumt".
Das Gute ist, dass das Protokoll immer gleich aussieht (natürlich nur so
lange, bis kein Fehler auftritt).
Deswegen möchte ich das Empfangen mittels DMA probieren, um zu sehen, ob
dies mit der hohen Baudrate möglich wäre.
grundschüler schrieb:> du hast vermutlich den falschen irq-handler. Die dma muss vom> uart-rx-irq gestartet werden.
Ich denke, der irq-handler sollte passen... (wenn ich mich nicht irre!)
har schrieb:> Hallo Ruediger Asche,>> ich gebe dir grundsätzlich Recht und bin auch deiner Meinung.> Ursprünglich habe ich das Empfangen ohne DMA durchgeführt.> Nun habe ich aber die Anforderung, dass die Kommunikation mit 9MBaud> erfolgen soll.> Wenn ich das ohne DMA, aber mir Interrupts mache, gibt es folgenden> Nachteil:> ein Datenpaket benötigt für die Übertragung nur 1,11us. Die Abarbeitung> der zugehörigen ISR benötigt jedoch 3us (mit Oszi gemessen).> Das heißt, ich empfange das erste Byte, aber alle weiteren werden> "versäumt".>
ok, 9M ist natürlich eine Andere Hausnummer, deswegen hatte ich meine
erste Antwort auch auf "normale" Baudraten beschränkt.
> Deswegen möchte ich das Empfangen mittels DMA probieren, um zu sehen, ob> dies mit der hohen Baudrate möglich wäre.>
was ist das physikalische Medium/Leitungslänge hinter dem UART? Bei so
hohen Baudraten ist natürlich auch die Stöfungsanfälligkeit höher und
damit die Wahrscheinlichkeit, Zeichen zu verlieren, und m.W. nach ist
das mit DMA nicht gut in den Griff zu kriegen.
har schrieb:> ein Datenpaket benötigt für die Übertragung nur 1,11us. Die Abarbeitung> der zugehörigen ISR benötigt jedoch 3us (mit Oszi gemessen).
Du meinst ein Byte (START-8bits-STOP) oder ?
Auf jeden Fall steht das:
Ruediger A. schrieb:> Paketgrösse geht eh nichts mit DMA), und dann hast Du ein Problem, wenn> mal ein Zeichen im Paket verloren geht und dein DMA nicht mehr> zurückkommt...
Das musst du dann mit einem Timer abfangen...
Bei 16Byt und ISR auf RxD, bleibst du in der zugehörigen ISR etwa
20us (mit Hin- und Rücksprung), das ist erträglich, oder ?
- Welchen Controller mit wie viel MHz verwendest du?
- Der Cortex M3 (und vmtl auch seine Nachfolger) haben so eine Art Tail
Chaining. Wenn der nächste INT bereits ansteht, kann der PC fast direkt
damit weitermachen (Register push/pop etc. wird übersprungen).
- Wieso bleibst du nach dem Empfang des 1. Bytes nicht in der ISR und
liest alle Bytes von der Nachricht ein. Zur Sicherheit noch ein Timeout
(Kein Byte innerhalb der letzten 4µs?) einbauen, damit die ISR auch
garantiert wieder verlassen wird.
mfg
Ruediger A. schrieb:> was ist das physikalische Medium/Leitungslänge hinter dem UART? Bei so> hohen Baudraten ist natürlich auch die Stöfungsanfälligkeit höher und> damit die Wahrscheinlichkeit, Zeichen zu verlieren, und m.W. nach ist> das mit DMA nicht gut in den Griff zu kriegen.
jetzt zum Probieren mit einem kurzen Kabel einfach TX von UART1 mit RX
von UART3 verbunden!
Marc Vesely schrieb:
> Du meinst ein Byte (START-8bits-STOP) oder ?
genau!
Felix F. schrieb:
> Welchen Controller mit wie viel MHz verwendest du?
STM32F303 mit 72MHz
> Der Cortex M3 (und vmtl auch seine Nachfolger) haben so eine Art Tail> Chaining. Wenn der nächste INT bereits ansteht, kann der PC fast direkt> damit weitermachen (Register push/pop etc. wird übersprungen)
OK, leider habe ich davon keine Ahnung!
> Wieso bleibst du nach dem Empfang des 1. Bytes nicht in der ISR und> liest alle Bytes von der Nachricht ein. Zur Sicherheit noch ein Timeout> (Kein Byte innerhalb der letzten 4µs?) einbauen, damit die ISR auch> garantiert wieder verlassen wird.
gute Idee, habe ich auch versucht. Nach dem Auslösen des 1. Interrupts
wollte ich mittels einer for-Schleife alle Bytes in ein Array speichern.
Leider hat das nicht geklappt. Es wurde das 1. Byte 16x in das Array
gespeichert!
har schrieb:> OK, leider habe ich davon keine Ahnung!
Musst du auch nicht. Das macht der Controller selber. Wollte es bloß
erwähnt haben.
har schrieb:> gute Idee, habe ich auch versucht. Nach dem Auslösen des 1. Interrupts> wollte ich mittels einer for-Schleife alle Bytes in ein Array speichern.> Leider hat das nicht geklappt. Es wurde das 1. Byte 16x in das Array> gespeichert!
Du musst mit dem Auslesen auch warten, bis das nächste Byte kommt (alle
1,1µs).
Der Controller arbeitet seine Befehle im ns-Bereich ab, dementsprechend
ist die Schleife durch bis das 2 Byte da ist.
mfg
Felix F. schrieb:> Du musst mit dem Auslesen auch warten, bis das nächste Byte kommt (alle> 1,1µs).
Oder entsprechende bits abfragen...
Sobald DR-Register gelesen wird, wird RXNE Flag automatisch
zurückgesetzt.
Danach warten, bis es wieder gesetzt wird oder (falls Zeit abgelaufen
ist), der Timer-INT feuert.
16 Mal wiederholen.
Marc V. schrieb:> Felix F. schrieb:>> Du musst mit dem Auslesen auch warten, bis das nächste Byte kommt (alle>> 1,1µs).>> Oder entsprechende bits abfragen...>> Sobald DR-Register gelesen wird, wird RXNE Flag automatisch> zurückgesetzt.> Danach warten, bis es wieder gesetzt wird oder (falls Zeit abgelaufen> ist), der Timer-INT feuert.> 16 Mal wiederholen.
Eher suboptimal, da Pollen im ISR wirklich keinerlei Zeit für
irgendetwas Anders lässt... bei 72MHz ist ein Taktzyklus 1,38 ns, d.h.
erst wenn 1000 Zyklen im ISR verbraten werden, steht bei 1,1us das
nächste Zeichen wieder an. Ich würde vermutlich eher nach Lesen des
Zeichens sofort gucken, ob wieder ein Zeichen bereit steht (aber nicht
durch Lesen des DR, sondern durch Abfragen des Interrupt Status) und
wenn nicht den ISR verlassen.
Ruediger A. schrieb:> irgendetwas Anders lässt... bei 72MHz ist ein Taktzyklus 1,38 ns, d.h.> erst wenn 1000 Zyklen im ISR verbraten werden, steht bei 1,1us das> nächste Zeichen wieder an.
Bei mir sind es 13,8ns und 80 Zyklen, aber egal, tut nichts zur Sache.
> Zeichens sofort gucken, ob wieder ein Zeichen bereit steht (aber nicht> durch Lesen des DR, sondern durch Abfragen des Interrupt Status) und> wenn nicht den ISR verlassen.
Nicht DR lesen, sondern RXNE abfragen, etwa so:
1
while((USART1->SR&USART_SR_RXNE)==0);
Und ISR wird verlassen wenn 16 Zeichen empfangen sind oder wenn
Timer INT (eingestellt auf 100us z.B.) gefeuert hat.
Ruediger A. schrieb:> Marc V. schrieb:>> Felix F. schrieb:>>> Du musst mit dem Auslesen auch warten, bis das nächste Byte kommt (alle>>> 1,1µs).>>>> Oder entsprechende bits abfragen...>>>> Sobald DR-Register gelesen wird, wird RXNE Flag automatisch>> zurückgesetzt.>> Danach warten, bis es wieder gesetzt wird oder (falls Zeit abgelaufen>> ist), der Timer-INT feuert.>> 16 Mal wiederholen.>> Eher suboptimal, da Pollen im ISR wirklich keinerlei Zeit für> irgendetwas Anders lässt... bei 72MHz ist ein Taktzyklus 1,38 ns, d.h.> erst wenn 1000 Zyklen im ISR verbraten werden, steht bei 1,1us das> nächste Zeichen wieder an. Ich würde vermutlich eher nach Lesen des> Zeichens sofort gucken, ob wieder ein Zeichen bereit steht (aber nicht> durch Lesen des DR, sondern durch Abfragen des Interrupt Status) und> wenn nicht den ISR verlassen.
Das war nur eine mögliche Lösung für sein aktuelles Problem. Ich
persönlich würde das aber völlig angehen.
Das Problem ist seine ISR, die benötigt angeblich ~3µs. Das sind normal
knapp 300 Befehle. Für was?? Eine RX-ISR für UART liest bei mir nur das
empfangen Byte aus und legt es in einen Puffer. Das sollte unter 100
Zyklen gehen. Dann kann ich auch locker 9MBaud einlesen.
mfg
Felix F. schrieb:> Das Problem ist seine ISR, die benötigt angeblich ~3µs. Das sind normal> knapp 3000 Befehle. Für was?? Eine RX-ISR für UART liest bei mir nur das
Nein, 10 Mal weniger. Du rechnest dauernd mit 720MHz, anstatt mit 72.
P.S.
Wenn die Zeichen wirklich mit 9Mbit hintereinander ankommen, dann ist
eher ein verlassen der ISR suboptimal...
Marc V. schrieb:> Felix F. schrieb:>> Das Problem ist seine ISR, die benötigt angeblich ~3µs. Das sind normal>> knapp 3000 Befehle. Für was?? Eine RX-ISR für UART liest bei mir nur das>> Nein, 10 Mal weniger. Du rechnest dauernd mit 720MHz, anstatt mit 72.>> P.S.> Wenn die Zeichen wirklich mit 9Mbit hintereinander ankommen, dann ist> eher ein verlassen der ISR suboptimal...
Bin von dem Beitrag vor mir ausgegangen.
Wenn ich aber Daten mit 9MBaud einlese, heißt das, dass ich normal
ziemlich viel einlese. Von daher würde ich einen schnelleren Controller
(M4) verwenden. Dann kann ich neben einlesen auch noch was vernünftiges
mit den Daten anstellen.
mfg
Felix F. schrieb:> Wenn ich aber Daten mit 9MBaud einlese, heißt das, dass ich normal> ziemlich viel einlese.
Tja, dazu hat sich der TO nicht geäußert.
Es kann genauso gut sein, dass die Pakete 2 Mal pro Sekunde mit
9Mbit ankommen...
Marc V. schrieb:> Felix F. schrieb:>> Wenn ich aber Daten mit 9MBaud einlese, heißt das, dass ich normal>> ziemlich viel einlese.>> Tja, dazu hat sich der TO nicht geäußert.>> Es kann genauso gut sein, dass die Pakete 2 Mal pro Sekunde mit> 9Mbit ankommen...
Dann bietet es sich umso mehr an, in der ISR die Daten lediglich in
einen Puffer zu schreiben. Zum bearbeiten hat er dann ja genug Zeit ;)
mfg
Marc V. schrieb:> Ruediger A. schrieb:>> irgendetwas Anders lässt... bei 72MHz ist ein Taktzyklus 1,38 ns, d.h.>> erst wenn 1000 Zyklen im ISR verbraten werden, steht bei 1,1us das>> nächste Zeichen wieder an.>> Bei mir sind es 13,8ns und 80 Zyklen, aber egal, tut nichts zur Sache.>
Sorry, hast Recht, Rechenfehler meinerseits. 80 Zyklen sind schnell
verbraten, vor Allem wenn man mit HALs o.ä. arbeitet. 160 aber auch, und
damit ist in der Tat die Gefahr, Zeichen durch zu langsames Wegschaffen
zu verlieren, nicht unbeträchtlich (selbst im ISR). Man braucht nur "zu
spät" nach Eintreffen des ISR dazu zu kommen, das erste Zeichen
auszulesen, und dann kann das Zweite schon überschrieben sein.
Ironischerweise ist aber vermutlich der kritische Pfad, der hier einen
schnelleren Prozessor nahelegen würde, gar nicht so kritisch, denn hier
sind "nur" 16 byte sehr eng aufeinander. Was der Rest macht, müsste man
wissen. Wenn es ein konstanter Datenstrom ist, dann ja, schnellerer
Prozessor indiziert, aber wenn das Ganze ein Master-Slave Protokoll ist,
bei dem die schnellen 16 byte nur auf (mglw. recht weit
auseinanderliegende) Pollinganfragen kommen, muss man vielleicht gar
nicht so schnell laufen, ohne den Gesamtdurchsatz zu gefährden. Das sind
Systemdesignfragen.
BTW, ein Cortex M4 ist nicht notwendigerweise schneller als ein M3. der
Kern schreibt keine Taktfrequenz vor. Mglw. hilft aber auch schon ein
Prozessor mit UART FIFO Support.
Edit: Oops, geht echt schnell hier, Marc und Felix haben auch schon
teilweise gleichlautend geantwortet...
Ruediger A. schrieb:> bei dem die schnellen 16 byte nur auf (mglw. recht weit> auseinanderliegende) Pollinganfragen kommen, muss man vielleicht gar> nicht so schnell laufen, ohne den Gesamtdurchsatz zu gefährden.
Ja, stimme ich voll zu.
Aber vielleicht ist es auf der anderen Seite Zeitkritisch, deswegen
die 9Mb.
> Das sind Systemdesignfragen.
Genau, da sollte man vielleicht ansetzen und die ganze Sache nochmal
durchdenken.
Danke euch allen für die zahlreichen Tipps!
Zu meinem Problem (warum nie in den DMA Interrupt Handler gesprungen
wird) hat mir folgender Hinweis geholfen:
Christoph S. schrieb:> DMA_InitStruct.DMA_MemoryBaseAddr = receive[0]; //(uint8_t)&receive[0];> Das stimmt nicht, auch das auskommentierte nicht.>> Du musst die Adresse des ersten Byte im Array angeben.> "receive[0]" ist nur der Wert im ersten Array element.> Du brauchst den Pointer, also entweder "&receive[0]" oder einfach> "receive".>> Speicheradressen sind beim Cortex-M 32bit breit, um die Compiler-Warnung> wegzubekommen musst du also zu uint32_t casten.> Wenn du zu uint8_t castest (wie im Kommentar) schneidest du die oberen> 24bit der Adresse weg.>> Also entwederDMA_InitStruct.DMA_MemoryBaseAddr => (uint32_t)receive;oderDMA_InitStruct.DMA_MemoryBaseAddr => (uint32_t)&receive[0];
Als ich das ausgebessert habe, funktioniert jetzt das Empfangen mittels
DMA! Danke!!
Aber leider noch nicht perfekt, denn:
In das erste Array (receive[0]) wird nichts geschriebren (es bleibt 0).
Das erste empfangene Byte wird in receive[1] geschrieben. Dadurch geht
mein letztes Byte verloren (da das Array zu Ende ist).
Welche Kleinigkeit in meiner Konfiguration müsste ich ändern, damit das
erste Byte in das erste Element (receive[0]) geschrieben wird?
har schrieb:> Ursprünglich habe ich das Empfangen ohne DMA durchgeführt.> Nun habe ich aber die Anforderung, dass die Kommunikation mit 9MBaud> erfolgen soll.
Da fragt man sich eher, wer oder was da nicht richtig tickt.
Erstens: über einen UART mit 9 MBaud übertragen zu wollen.
Zweitens: wer da auf solche Idee gekommen ist
Drittens: wann denn die nötige Auswertung dieser Daten erfolgen soll,
wenn schon die ISR zum schieren Empfangen zu langsam ist
Mir sieht das eher nach einer Sportübung aus als nach einem durchdachten
soliden Systementwurf. Also, was soll das ganze überhaupt werden? Nur zu
sagen "Ich habe die Anforderung.." ist ein bissel arg wenig.
W.S.
Liebes Forum,
wie bereits erwähnt, funktioniert nun das Empfangen mittels DMA. Nur das
folgende Problem ist noch vorhanden:
In das erste Array (receive[0]) wird nichts geschriebren (es bleibt 0).
Das erste empfangene Byte wird erst in receive[1] geschrieben.
Wenn ich statt:
dann wird wiederum das erste Byte in receive[0] geschrieben.
Es wird mittels UART1 gesendet und mit UART3 empfangen.
ABER: Wenn ich mit UART3 sende UND empfange, dann passt die Einstellung:
Die Konfiguration ist aber die selbe.
Warum fängt das speichern der Bytes im ersten Fall verzögert an? Zum
Verbinden des TX und RX Pin verwende ich in beiden Fällen ein kurzes
Kabel...
har schrieb:> Warum fängt das speichern der Bytes im ersten Fall verzögert an? Zum> Verbinden des TX und RX Pin verwende ich in beiden Fällen ein kurzes> Kabel...
Hat man dir schon mal gesagt: Dein Konzept ist Kacke.
Oftmals wird ein byte z.B. bei der Initialisierung losgeschickt. Schon
bricht deine Welt zusammen und du versuchst dieses Missgeschickt mit
einer "-1" zu retten.
W.S. schrieb:> Da fragt man sich eher, wer oder was da nicht richtig tickt.> Erstens: über einen UART mit 9 MBaud übertragen zu wollen.> Zweitens: wer da auf solche Idee gekommen ist> Drittens: wann denn die nötige Auswertung dieser Daten erfolgen soll,> wenn schon die ISR zum schieren Empfangen zu langsam ist
Wenn du schon Hilfe erwartest, dann wäre es respektvollerweise nicht
schlecht mal auf die gestellten Fragen zu antworten.
Übrigens du brauchst ein Ringbuffer mit Protokoll. ST hat eine Appnote
zu DMA und uart.