Hallo Leute,
ich wollte mal wieder den UART am STm32F407 in Betrieb nehmen.
Das ganze funktioniert auch.
Ich verwende (für diese einfachen Dinge) keine Treiber, da ich denke,
dass eine 5 zeilige UART-Initialisierung auch noch mit puren Registern
erfolgen kann, ohne dass es einem über dsen Kopf wächst :D
Aber nun zu meiner Frage:
Um die Alternate Port functions nutzen zu können muss ich im
GPIOx->MODER Register die Alternate Function aktivieren. Habe bemerkt,
dss dies nur mit bereits eingeschalteter GPIO-Clock geht. Ebenfall habe
ich bemerkt, dass ich die GPIO-Clock einige Takte früher initialisieren
muss, als das MODER-Register.
Schreibe ich:
1
RCC->AHB1ENR|=RCC_AHB1_ENR_GPIODEN;
2
GPIOD->MODER=0x12345678;//Beispiel Wert
spo klappt das ganze nicht. Das MODER bleibt auf 0.
Erst wenn eine delay eingefügt wird
1
RCC->AHB1ENR|=RCC_AHB1_ENR_GPIODEN;
2
wait();//Verzögert um einige takte...
3
GPIOD->MODER=0x12345678;//Beispiel Wert
funktioniert es.
Hat jemand einen Tipp für mich, wie ich es lösen kann, dass ich die
Clock einschalte und dann das MODER setze, ohne übermäßg viel Zeit zu
warten?
Eventuell:
In diesem Thread hier (Beitrag "C++ Exceptions mit gcc-arm-none-eabi")
hatte ich das gleiche Problem und habe ewig gebraucht dies
festzustellen. Am Ende habe ich herausgefunden, dass es zwei Takte nach
dem Einschalten der Clock dauert bis die Peripherie aktiv ist. Leider
konnte ich dazu auch keine Informationen im Datenblatt finden. Man ist
hier leider auch der Willkür des Compilers ausgeliefert, da man nicht so
einfach beeinflussen kann wie die generierten Instructions nacher
angeordnet sind. Möglicherweise braucht man hier auch eine Memory
Barrier (einfügen einer DMB instruction über __dmb()) zwischen den
beiden Zeilen damit der Compiler die Zugriffe nicht zu schnell
hintereinander durchführt. Wobei ich auch hier keine Hinweise im
Datenblatt gefunden habe, nur das es bei mir mit __dmb(); Aufruf
funktioniert. Mich würde daher auch interessieren was denn nun die
tatsächlich beste und richtige Lösung ist.
Wenn Du das Zeug nicht schon vorher aktivierst, dann kannst Du ja die
Berechnung des Teilers für die Baudrate als sinnvollen Zeitvertreib
zwischenschieben.
adenin schrieb:> Wenn Du das Zeug nicht schon vorher aktivierst, dann kannst Du ja> die> Berechnung des Teilers für die Baudrate als sinnvollen Zeitvertreib> zwischenschieben.
Bis der Compiler auf die Idee kommt das doch wieder umzuordnen, sodass
die Berechnung davor oder danach stattfindet. Ich habe gerade doch etwas
gefunden. Scheinbar handelt es sich um einen Fehler der im Errata sheet
beschrieben ist
(http://www.st.com/st-web-ui/static/active/en/resource/technical/document/errata_sheet/DM00037591.pdf).
Dort der Absatz "2.1.13
Delay after an RCC peripheral clock enabling". Als Workarround werden
eine bestimmte Anzahl NOPs oder eine DSB memory barrier empfohlen.
Ok. Vielen Dank.
Werde mir DSB/DMB (noch keine Ahnung => Mal die Assemblerefehle
anschauen) zu Gemüte führen.
adenin schrieb:> Wenn Du das Zeug nicht schon vorher aktivierst, dann kannst Du ja die> Berechnung des Teilers für die Baudrate als sinnvollen Zeitvertreib> zwischenschieben.
Naja. die Buad hab ich händisch ausgerechnet und einfach den Wert 0x1117
für 9600 Baud bei 42MHz Pheripherie-Clock. das ist nur eine Zuweisung.
Das Problem daran ist, dass wahrscheinlich die Clock vom U(S)ART
ebenfalls erst eingeschaltet werden muss, um das register zu schreiben.
Hab ds noch nicht versucht. Ist das Problem hier ebenfalls vorhanden?
Bzw:
1. Muss die Clock für den U(S)ART aktiviert sein, um die Register zu
beschreiben?
2. Gibt es hier auch dieses Delay von ein paar Takten? Ich denka ja,
weil das ja auch erst durch die Bus-Struktur, die ja deutlich langsamer
getaktet ist als der Kern, muss.
M. H. schrieb:> Naja. die Buad hab ich händisch ausgerechnet und einfach den Wert 0x1117> für 9600 Baud bei 42MHz Pheripherie-Clock. das ist nur eine Zuweisung.> Das Problem daran ist, dass wahrscheinlich die Clock vom U(S)ART> ebenfalls erst eingeschaltet werden muss, um das register zu schreiben.> Hab ds noch nicht versucht. Ist das Problem hier ebenfalls vorhanden?
Ja, das Problem ist hier natürlich auch vorhanden.
Aber ich las das berechenen aus der aktuellen Clock für die USART und
als Parameter die Baudrate. Da brauch ich mich nicht um selbst um die
Bautrate kümmern, und die ist dann immer richtig, auch wenn ich bei der
vergabet der Frequenzen irgendwelchen Mist gemacht habe.
Ist gut fürs debuggen, wenn die CPU sich trotzdem immer meldet. ;)
Kann mir jemand den Unterschied zwischen DMB und DSB erklären?
1. DMB
DMB acts as a data memory barrier. It ensures
that all explicit memory accesses that
appear, in program order, before the DMB instruction are completed
before any explicit
memory accesses that appear, in program orde
r, after the DMB instruction. DMB does not
affect the ordering or execution of in
structions that do not access memory.
2. DSB
DSB acts as a special data synchronization memory barrier. Instructions
that come after the
DSB, in program order, do not execute until the DSB instruction
completes. The DSB
instruction completes
when all explicit memory a
ccesses before
it complete.
Condition flags
This instruction does not change the flags.
Ok. Ich glaub ich habs.
DSB hält die Codeausführung an, bis die Speichervorgänge abgeschlossen
sind.
DMB hält nur Befehle, die Speicher brauchen an.
Oder?
Ja oder so ähnlich. Wenn ich es richtig verstehe, dann warten beide
Befehle bis alle Speicherzugriffe vor diesem Befehl abgeschlossen sind.
Allerdings darf die Ausführung bei DMB schon früher weiter gehen wenn
keine Speicherzugriffe damit verbunden sind. Diese Definitionen sind
teilweise ziemlich spitzfindig und man muss sie sehr genau lesen. DSB
scheint auf jeden Fall strenger zu sein als DMB. Wobei ich in diesem
Fall eigentlich sagen würde, dass ein DMB ausreichen sollte aber wenn ST
ein DSB empfiehlt hat das vermutlich seinen Grund.
Hallo. Ich habe da noch ein ähnliches Problem:
Wenn ich eine Interruptroutine habe, und das Interrupt Flag erst am ende
lösche, wird die Interruptroutine dannach erneut ausgeführt. Ich muss
auch hier mehrere takte warten bzw. das Flag am Am Anfang der Routine
löschen.
Hat das auch damit zu tun?
M. H. schrieb:> Ich muss> auch hier mehrere takte warten bzw. das Flag am Am Anfang der Routine> löschen.
Ich lösch immer am Anfang.
Aber das ist mir auch dann noch nie passiert, wenn der Interrupt danach
sofort verlassen wird.
Richtig heißt die Funktion nach dem CMSIS Standard: __DSB();
Wobei die CMSIS Variante dem __asm__("dsb") vorzuziehen ist, da man bei
der CMSIS Version davon ausgehen kann, dass die korrekten Hinweise für
den Compiler da sind damit die memory barrier an genau der genannten
Stelle hinzugefügt wird und nicht wegoptimiert oder umsortiert wird.
Sebastian V. O. schrieb:> Laut dieser Quelle>
(http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHDGBJG.html)
> sind die Zugriffe auf die NVIC Register zumindest mit einer impliziten> DMB memory barrier versehen. Möglicherweise reicht dies aber nicht aus> und sorgt für das von dir beobachtete Verhalten.
Die Interrupt Flags liegen nicht in den NVIC-Registern. das sind
Peripherie Register. Und dieses Falg löst dann im NVIC erst die Reaktion
aus. Die NVIC Flags löschen sich ja auch selbst bei der Annahme eines
Interrupts. Nur die Flags des Peripheriemoduls muss man selbst löschen.