Hallo Zusammen,
Ich habe aktuell ein Problem mit dem USART. Ich versuche Daten über
USART1 mit dem DMA zu empfangen und in einer Variable abzuspeichern.
Die Variabel befindet sich in einer Struktur, welche wie folgt aussieht:
1
typedefstruct
2
{
3
uint8_tmb_rx_buffer[BUFFER_MAX_LEN];
4
uint8_tmb_tx_buffer[BUFFER_MAX_LEN];
5
uint8_tmb_cmd_available;
6
}st_modbus;
Den DMA Starten tu ich mit der folgenden Funktion im Init:
Nun das Problem:
Ich komme nie in die Funktion. Auch wenn ich im Interrupt handler einen
Break Point setzte kommt er nie dort durch.
Hat da jemand eine Idee was ich flasch mache?
Moot S. schrieb:> Hat da jemand eine Idee was ich flasch mache?
Zunächst mal dass du nicht den ganzen Code zeigst. Und dann
kann man rätselraten ob du Interrupts an diversen Ecken und
Enden enablest, sonst wirst du auch keinen Interrupt-
Handler angesprungen bekommen .....
Was mich auch zufrieden stellen würde, wäre wenn der Interrupt
funktionieren würde ohne DMA. Das Problem da ist, dass der IRQ-Handler
aufgerufen wird aber der Callback nicht.
Wenn die cb Funktion durch viele Bedingungen nicht aufgerufen wird,
schreib eine Eigene und rufe die im IRQ-Handler auf.
Deshalb gibt es dort einen USER CODE Bereich.
Es ist keine Pflicht nur die vorgegebenen HAL Funktionen zu verwenden.
Ich habe nun meine Callback Funktion in den Interrupt des DMA platziert.
Den Code auf Github habe ich nun geupdated.
Die Funktion wird aufgerufen, aber danach stehen keine Daten in meinem
RX-Buffer.
Moot S. schrieb:> Die Funktion wird aufgerufen, aber danach stehen keine Daten in meinem> RX-Buffer.
Wie bedauerlich.
Wieso kommst du nicht von selbst auf die Idee, das Ganze tatsächlich
verstehen zu wollen oder dir ein sinnvolles Interface zwischen einem
UART-Treiber und dem Rest der Firmware auszusuchen? Sowas gibt es ja
seit Adam&Eva, da brauchst du es nur zu verstehen und mußt es dir nicht
selbst ausdenken.
Und nein, ich geb dir hier keinen zum x-ten Mal wiederholten Ratschlag,
denn dazu gibt es in diesem Forum schon genug. Da mußt du nur selbst
ein wenig lesen.
W.S.
W.S. schrieb:> Und nein, ich geb dir hier keinen zum x-ten Mal wiederholten Ratschlag,
Danke. Möchte keiner mehr hören und die rants gegen STMCube/HAL erst
recht nicht.
ok, der F411 hat keinen CCM, im Linkerfile gibt es auch nur die eine
SRAM Section, das ist es also auch nicht.
Stimmen die seriellen Parameter? Die ISR macht auch eine ausführliche
Fehlerbehandlung und evtl. werden die Daten wegen falschem Format nicht
gespeichert. Das wäre auch eine Erklärung warum der RxComplete Callback
nicht aufgerufen wurde. Dieser generierte HAL Code funktioniert
mittlerweile sehr gut, typische Fehler sind nicht den kompletten Code zu
benutzen (ISR vergessen), Callbacks in C++ Modulen ohne extern, Buffer
im nicht für DMA zugänglichen Speicher.
JS schrieb:> liegt der RxBuffer evtl. im CCM?
Nein, ist beim github Projekt im Linker Script nicht angegeben.
Wenn ich Interrupt und Variable höre, muss ich automatisch an volatile
denken.
JS schrieb:> ok, der F411 hat keinen CCM, im Linkerfile gibt es auch nur die eine> SRAM Section, das ist es also auch nicht.> Stimmen die seriellen Parameter? Die ISR macht auch eine ausführliche> Fehlerbehandlung und evtl. werden die Daten wegen falschem Format nicht> gespeichert. Das wäre auch eine Erklärung warum der RxComplete Callback> nicht aufgerufen wurde. Dieser generierte HAL Code funktioniert> mittlerweile sehr gut, typische Fehler sind nicht den kompletten Code zu> benutzen (ISR vergessen), Callbacks in C++ Modulen ohne extern, Buffer> im nicht für DMA zugänglichen Speicher.
Ja Parameter stimmen alle. Der Buffer liegt in einem Struct, auf das
sollte der DMA mit dem Struktur Pointer zugriff haben.
pegel schrieb:> Wenn ich Interrupt und Variable höre, muss ich automatisch an volatile> denken.
Ja dachte ich auch mal, hatte aber nichts gebraucht, gibt nur ein
warning:
warning: passing argument 2 of 'HAL_UART_Receive_DMA' discards
'volatile' qualifier from pointer target type [-Wdiscarded-qualifiers]
Moot S. schrieb:> Ja dachte ich auch mal, hatte aber nichts gebraucht, gibt nur ein> warning:
Macht ja auch keinen Sinn, die DMA-Engine schreibt das ganz an
CPU&Compiler vorbei in den RAM, der Compiler hat also gar keine
Möglichkeit, da über volatile&co irgendwas an der DMA-Arbeitsweise zu
ändern.
Beim Auslesen der Daten im C-Code hingegen kann es einen Unterschied
machen, muss aber schon ein ganz "spezielles" Konstrukt sein.
z.B. wenn man den DMA-Buffer statt Interrupt per Polling überwachen
will.
Kevin M. schrieb:> Du musst neben dem DMA Interrupt auch den global Interrupt des benutzten> USART aktivieren, sonst wird das nicht funktionieren.
.....
Oder hast du die mittlerweile aktiviert? War in dem Cube File jedenfalls
nicht der Fall.
Kevin M. schrieb:> Oder hast du die mittlerweile aktiviert? War in dem Cube File jedenfalls> nicht der Fall.
Ja habe ich aktiviert. Habe auch denk Code im GitHub aktualisiert
JS schrieb:> Danke. Möchte keiner mehr hören und die rants gegen STMCube/HAL erst> recht nicht.
Ja, das sehe ich. Um Hilfe rufen und sich zugleich die Augen und Ohren
zuhalten. Naja, auch damit kann man den Traffic hier erhöhen, wie man
gerade sieht.
W.S.
Ich konnte das Problem lösen.
Leider habe ich mich auf dem MCU etwas verlesen, anstelle von
STM32F411RCT6 habe ich STM32F411RET6 gelesen.
Dies entdeckte ich, als mein Beispiel auf dem Nucleo mit dem RET6
funktionierte aber auf meiner eigenen Schaltung mit dem RCT6 nicht.
Naja nun funktioniert mein vorgehen, danke für eure Vorschläge und Zeit
:)
Εrnst B. schrieb:> Macht ja auch keinen Sinn, die DMA-Engine schreibt das ganz an> CPU&Compiler vorbei in den RAM
Und genau deswegen sollte das volatile sein, weil das dem Compiler sagt,
daß es sich auf nicht-programmatische Weise ändern könnte.
> der Compiler hat also gar keine> Möglichkeit, da über volatile&co irgendwas an der DMA-Arbeitsweise zu> ändern.
Er sollte aber wissen, daß er bestimmte Optimierungen nicht machen darf.
Nop schrieb:> Und genau deswegen sollte das volatile sein, weil das dem Compiler sagt,> daß es sich auf nicht-programmatische Weise ändern könnte.
Aber nur an der Stelle, wo es ausgelesen wird, nicht da wo der Pointer
an die DMA-Engine übertragen wird.
Und auch beim Auslesen braucht's es nur, wenn der Compiler irgendeine
reale Möglichkeit hat den Speicherzugriff wegzuoptimieren.
Beim DMA-Complete Callback ist das nicht der Fall. Da ist der "DMA
Complete", d.H. während dein Callback ausgeführt wird, erfolgen keine
weiteren Schreibzugriffe an der CPU vorbei. Zum anderen erfolgt der
Aufruf des Handlers indirekt aus der Library, insofern kann und darf
der Compiler keinerlei Annahmen treffen, was bei Eintritt in den Handler
im RAM steht.
Wie geschrieben, es gibt ein paar Sonder-/Grenzfälle, wo ein "volatile"
nötig sein könnte. Aber beim 08/15 Vorgehen: DMA-Start und im
Cplt-Handler Buffer auslesen, ist es überflüssig.
Εrnst B. schrieb:> Zum anderen erfolgt der> Aufruf des Handlers indirekt aus der Library, insofern kann und darf> der Compiler keinerlei Annahmen treffen, was bei Eintritt in den Handler> im RAM steht.
Doch, das darf er, denn auch wenn das eine andere translation unit ist,
gilt der C-Standard immer noch. Die andere Frage ist, ob er denn kann -
und das ist jedenfalls mit LTO durchaus denkbar. Jedenfalls ist es
sauberer Stil, an solche Buffer dann auch das volatile dazuzuschreiben.
Nop schrieb:> Doch, das darf er
Bei einer Nicht-Inlinebaren Funktion, die per Funktionspointer in einer
Struct ausgeführt wird?
Selbst wenn du davon ausgehst, dass irgendein hyperintelligenter
Compiler das in der Zukunft kann, ist "volatile" für den ganzen Buffer
an der Stelle overkill.
Eine simple compiler barrier ( asm volatile ("" ::: "memory") ) am
Anfang des Handlers erfüllt denselben Zweck, aber raubt dem Compiler
nicht alle Optimierungsmöglichkeiten innerhalb des Handlers selber.
Und ändert (Stand jetzt) absolut nichts am erzeugten Maschinencode.
Εrnst B. schrieb:> Bei einer Nicht-Inlinebaren Funktion, die per Funktionspointer in einer> Struct ausgeführt wird?
Ja, darf er, weil das nach Standard keine Rolle spielt.
> Selbst wenn du davon ausgehst, dass irgendein hyperintelligenter> Compiler das in der Zukunft kann
Das Können ist ja eine ganz andere Frage - aber es gibt einiges an
altem, nicht konformem C-Code, der mit den damals laxeren Compilern
lief, aber mit heutigen nicht mehr. Und eben aus solchen Erfahrungen bin
ich da vorsichtig.
> Eine simple compiler barrier ( asm volatile ("" ::: "memory") ) am> Anfang des Handlers erfüllt denselben Zweck
Ja, das ist für diesen Fall eine gute Idee, weil sich während des
Handlers nichts mehr ändert.