Also langsam kriege ich die Kriese…
Ich habe jetz seit ungefähr einem halben Jahr das STM32F4-Discovery hier
rumliegen und es bis jetzt noch nicht geschafft ein halbwegs
anspruchsvolleres Programm dafür zu schreiben. Ich geb zwar zu, dass ich
mich auch mit ner ganzen Menge anderer Dinge beschäftigt habe, aber
trotzdem würde ich endlich mal weiterkomen. Meine Probleme liegen zum
einen darin, dass das mein erster größerer Controller ist(vorher nur
Arduino und Attiny) und zum anderen, dass ich in Dokumentationen
ersticke. Ich habe mir mal dass programming manual durchgelesen, aber
das hat mir nur sehr begrenzt weitergeholfen(nur Asm und hauptsöchlich
Interrupts). Ab und zu werfe ich auch mal einen Blick ins Reference
Manual, aber die 1700 Seiten schrecken mich irgendwie ab. Und die
Periphal Library enthält erstens keine richtige Doku zu den Befehlen(hab
sowas jedenfalls noch nicht gefunden) und zweitens keinen Beispiele für
so richtig grundlegende Dinge(und wenn nur mit irgendetwas anderem
vermischt).
Ich wäre euch wirklich sehr dankbar, wenn ihr mir irgendwie Tipps geben
könntet, wie ich das besser lernen kann.
Vielleicht hilft auch ein ARM Cortex M4 Grundlagen tutorial weiter?
Gibt es irgendeine ausführliche Beschreibung der PeriphLib?
> http://www.diller-technologies.de/stm32.html
Mich hat da immer Abgeschreckt, dass in der Einleitung steht, für das
F1. Und es sind für die anderen Familien nicht unwesentliche Änderungen
notwendig.
Könnte mir bei diesem Problem nicht auch ein (praktisches) ARM Tutorial
weiterhelfen?
Moin und jein. Es gibt einfach zu viele Möglichkeiten, als dass man mit
einem allumfassenden Tutorial was reißen könnte. Wenn du das durch
hättest, hättest du den Anfang wieder vergessen. Learning by doing ist
meiner Meinung nach auch die beste Methode. Ich kann dir nur ans Herz
legen, von den Beispielprojekten, sehr gut eignen sich meiner Meinung
nach die von Uwe (bplaced Link oben). Die sind direkt lauffähig und
explizit für das STM32F4DISCO und es gibt zu sehr vielen Themen
Beispiele. Wenn du dann deine eigene Projekte damit machst, wirst du
hier und da eine etwas andere Funktionalität brauchen und den Code
anfangen abzuwandeln. Das machst du mit Hilfe der Periph Lib und dem
Reference Manual. Dann folgt Debugging mit Hilfe eines Blicks in die
Peripherie Register (ist alles so eingestellt, wie ich es haben wollte)
und dann irgendwann läufts :)
Übrigens gibt es für die Periph Lib meiner Meinung nach deswegen keine
Doku, weil der Aufbau und die Beschreibung der Bits/Register in der
Library selbst steckt. In Kombination mit dem Reference Manual kommt man
eigentlich gut klar. Wenn man was etwas von Grund auf implementieren
will, schaut man erst in die entsprechende .h der Periph Lib, schaut was
die Init Funktion braucht und das dafür nötige Init Struct und dann
welche anderen Funktionen noch nötig sind. Welche Einstellungen im Init
Struct etc. dann was bewirken geht meist aus deren Namen schon hervor,
ansonsten schaut man halt ins RefMan. Damit man mit dem Konzept klar
kommt, welches sich nach einer gewissen Nutzung, jedenfalls meiner
Meinung nach, als echt gut herausstellt, hangelt man sich am besten an
Tutorials/Beispielen entlang.
Versuch es einfach mal anhand der Beispiele und berichte.
Liebe Grüße, Jan
Okay, ich werde mit den Beispielen von BPlaced.net probieren und
berichte wie's läuft.
aber eine Frage habe ich noch: Läuft das bei allen ARM-Mikrocontrollern
so, oder ẃie macht man das bei den anderen µC?
Ich kenne mich, abgesehen von den STs, leider nur mit alten NXP ARM7s
aus. Da habe ich direkt auf den Registern gearbeitet, was nicht wirklich
angenehm war (Es ist meiner Meinung nach wie ein Bücherregal mit Büchern
auf deren Rücken die ISBN Nummern statt dem Titel stehen: Nervig). Der
Quellcode ist daher auch schnell unverständlich. Außer natürlich man
steckt viel Zeit rein alles in defines etc. zu beschreiben, aber Zeit
(bzw. Lust dazu ;) hat man ja meist nicht.
Wie die Libraries anderer Hersteller für ihre ARMs ist, weiß ich nicht.
Die CMSIS gibt es aber quasi für jeden ARM und in der Grundstruktur
sollte es daher schon zumindest ähnlich sein.
Wünsche dir viel Erfolg!
Also ich habe jetzt so ne Idee wofür die sein könnten, aber wo liegt der
Unterschied zwischen den beiden?
1
__IOuint16_tBSRRL;/*!< GPIO port bit set/reset low register, Address offset: 0x18 */
2
__IOuint16_tBSRRH;/*!< GPIO port bit set/reset high register, Address offset: 0x1A */
/stm32f4xx.h/
reagiert das eine nur wenn Low und das andere nur wenn High, oder
andersrum? Was bedeutet das Address offset da am ende(zieht sich durch
den ganzen Header)?
stm32f4er schrieb:> Also ich habe jetzt so ne Idee wofür die sein könnten, aber wo> liegt der> Unterschied zwischen den beiden? __IO uint16_t BSRRL; /*!< GPIO port> bit set/reset low register, Address offset: 0x18 */> __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register,> Address offset: 0x1A */ /stm32f4xx.h/> reagiert das eine nur wenn Low und das andere nur wenn High, oder> andersrum? Was bedeutet das Address offset da am ende(zieht sich durch> den ganzen Header)?
Das ist ganz einfach
Also das BSRR-Register ist eigentlich ein 32Bit-Register.
Hier ist es in zwei 16Bit Register unterteilt worden.
BSRRL sind die untersten 16Bit von BSRR.
BSRRH sind die obersten 16Bit von BSRR.
Für was sind die Register gut?
Damit kann man atomar (also ohne die anderen Bits zu stören) einzelne
Portbits setzen oder löschen.
Wenn du einen Wert in BSRRL schreibst, dann wird für jedes 1-Bit das
entsprechente Portbit gsetzt. Dort wo ein Bit im BSRRL Null ist, tritt
keine Änderung auf. Es dient also alleinig zum setzen von Bits.
Wenn du einen Wert in BSRRH schreibst, dann wird für jedes 1-Bit das
entsprechente Portbit gelöscht. Dort wo ein Bit im BSRRH Null ist,
tritt keine Änderung auf. Es dient also alleinig zum löschen von Bits.
stm32f4er schrieb:> Also ich finde da keine genaue Beschreibung zu BSRRx und Adress offset.> Wäre schön, wenn du den(oder die) Auszug(Auszüge) hier posten könntest.
Seite 218. Weshalb auch immer wurde in den Registerdefinitionen das
32bit-Register auf zwei 16bit-Register aufgeteilt. Der Address offset
gibt an, an welcher Stelle im Speicher sich das Register relativ zur
Basis-Adresse der Peripherieinheit befindet. Wie du siehst, stimmt die
Angabe von Registerdefinition und Reference manual überein. BSSRL liegt
an der Adresse des 32bit Registers aus dem Handbuch (0x18), BSSRH dann
logischerweise 2bytes darüber (0x18+2=0x1A).
Also noch mal zum Verständnis: wenn ich den Befehl GPIOD->BSRRL = 0x1000
ausführe, dann wird also das Bit an Port 4096 auf 1 gesetzt?
Detlef Kunz schrieb:> Dort wo ein Bit im BSRRL Null ist, tritt> keine Änderung auf.
Das verstehe ich allerdings nicht. Wenn ich ein Bit setze, muss doch da
vorher Null gestanden haben?
Und was ist Adress offset?
> Seite 218.
Bei mir steht da weder was über BSRR, noch über Address offset, sondern
über Clock-Out Capability.
Lukas K. schrieb:> Der Address offset> gibt an, an welcher Stelle im Speicher sich das Register relativ zur> Basis-Adresse der Peripherieinheit befindet.
Okay, vielen Dank das habe ich jetzt verstanden.
@ stm32f4er (Gast)
>Also noch mal zum Verständnis: wenn ich den Befehl GPIOD->BSRRL = 0x1000>ausführe, dann wird also das Bit an Port 4096 auf 1 gesetzt?
Nein. PORT D15 wird auf 1 gesetzt.
>> Dort wo ein Bit im BSRRL Null ist, tritt>> keine Änderung auf.>Das verstehe ich allerdings nicht. Wenn ich ein Bit setze, muss doch da>vorher Null gestanden haben?
Nein. Es kann auch vorher schon 1 sein.
>Und was ist Adress offset?
Eine Differenz zu Basisadresse.
stm32f4er schrieb:> Also noch mal zum Verständnis: wenn ich den Befehl GPIOD->BSRRL => 0x1000> ausführe, dann wird also das Bit an Port 4096 auf 1 gesetzt?>> Detlef Kunz schrieb:>> Dort wo ein Bit im BSRRL Null ist, tritt>> keine Änderung auf.
GPIOD -> BSRRL = 0x1000;
im Parameter ist Bit13 gesetzt;
Ergebnis: PD13 auf H setzen, alle anderen Portausgänge bleiben
unverändert
GPIOD -> BSRRL = 0x1001;
im Parameter ist Bit13 und Bit0 gesetzt;
Ergebnis: PD13 und PD0 auf H setzen, alle anderen Portausgänge bleiben
unverändert
GPIOD -> BSRRH = 0x0001;
im Parameter ist Bit0 gesetzt;
Ergebnis: PD0 auf L setzen, alle anderen Portausgänge bleiben
unverändert
Oder noch einfacher:
BSRRL enthält 16 EIN-Schalter, einen für jeden Portausgang.
Betätigs Du einen dieser Schalter, dann wird der Ausgang eingeschaltet.
BSRRH enthält dementsprechend 16 AUS-Schalter.
Betätigs Du einen dieser Schalter, dann wird der Ausgang ausgeschaltet.
> BSRRH enthält dementsprechend 16 AUS-Schalter.> Betätigs Du einen dieser Schalter, dann wird der Ausgang ausgeschaltet.
Und diese 16 Aus Schalter kann ich mit einem |-Operator verknüpfen?
stm32f4er schrieb:>> BSRRH enthält dementsprechend 16 AUS-Schalter.>> Betätigs Du einen dieser Schalter, dann wird der Ausgang ausgeschaltet.>> Und diese 16 Aus Schalter kann ich mit einem |-Operator verknüpfen?
Ja
stm32f4er schrieb:>> Seite 218.> Bei mir steht da weder was über BSRR, noch über Address offset, sondern> über Clock-Out Capability.
Zahlendreher, 281 wars
Und weiter geht die fragerei ;-)
1.)Wie kommt es, dass ich an den Ausgängen knapp 3V messe? Da müssten
doch eigentlich 5V oder zumindest 3.3V rauskommen?
2.) Wieso schaltet folgende Zeile den Ausgang nicht auf HIGH?
GPIOD -> BSRRL = GPIO_Pin_0;
>>1.)Wie kommt es, dass ich an den Ausgängen knapp 3V messe? Da müssten>>doch eigentlich 5V oder zumindest 3.3V rauskommen?
Eigenartig, bei mir kommen 0V raus.
>>2.) Wieso schaltet folgende Zeile den Ausgang nicht auf HIGH?>>GPIOD -> BSRRL = GPIO_Pin_0;
Vielleicht, weil die Zeile hier im Forum steht?
@ stm32f4er (Gast)
>1.)Wie kommt es, dass ich an den Ausgängen knapp 3V messe?
Reicht das nicht?
>Da müssten>doch eigentlich 5V oder
Nö, der STM32 kann nur 3,3V IO.
> zumindest 3.3V rauskommen?
Wieviel kommt denn GENAU raus? Was hängt an den IOs?
>2.) Wieso schaltet folgende Zeile den Ausgang nicht auf HIGH?>GPIOD -> BSRRL = GPIO_Pin_0;
Wahrscheinlich ist dein IO-Pin nicht als Ausgang konfiguriert.
Detlef Kunz schrieb:> Eigenartig, bei mir kommen 0V raus.
Ich meine natürlich wenn ich den angeschaltet habe.
Detlef Kunz schrieb:>>>2.) Wieso schaltet folgende Zeile den Ausgang nicht auf HIGH?>>>GPIOD -> BSRRL = GPIO_Pin_0;>> Vielleicht, weil die Zeile hier im Forum steht?
Nein so steht sie nicht hier, nur mit Adressen. Wieso geht das nicht
auch so?
Jan Berg schrieb:> Ich meine das auf dem Discovery Board der STM mit 3V versorgt wird.> Daran dürfte es liegen.
Ja, der STM32F4 wird mit 3V versorgt, warum auch immer. Wenn du an VCC
misst, kommen auch 3V raus.
Gruß
Felix
Achso.
Und wieso geht dies nicht? GPIOD -> BSRRL = GPIO_Pin_0; ist da ein
Fehler von der Logik her, denn GPIO_Pin_0 ist doch dasselbe wie 0x0001 ?
Jedenfalls steht das so in der lib.
Jaja mit der geht es. Und so wieich es beschrieben hatte, geht es auch.
Ich war nur zu dumm um GPIOD in GPIOA umzuwandeln. Benutzt man ein
einziges Mal im gesamten Code Copy&Paste, hat man sofort nen Fehler ;-)
@ STM32User (Gast)
>Warum nehmt Ihr nicht die entsprechenden Funktionen ?>HAL_GPIO_WritePin(GPIOD, GPIO_Pin_0, GPIO_BIT_SET);
Weil das uncool ist und je nach Qualität und Abstraktionsgrad der
Funktion deutlich langsamer!
Detlef Kunz schrieb:>>>2.) Wieso schaltet folgende Zeile den Ausgang nicht auf HIGH?>>>GPIOD -> BSRRL = GPIO_Pin_0;>> Vielleicht, weil die Zeile hier im Forum steht?
Hab ich doch gesagt. :)
Gibt es irgendeine Logische Erklärung, warum man IDR mit &-Operator
verknmüpft und BSRRL/H oder ODR mit =-Operator?
Bsp.: GPIOA -> IDR & 0x0001
GPIOA -> BSRRH = 0x0001 | 0x0002 | …
stm32f4er schrieb:> Gibt es irgendeine Logische Erklärung, warum man IDR mit> &-Operator> verknmüpft und BSRRL/H oder ODR mit =-Operator?> Bsp.: GPIOA -> IDR & 0x0001> GPIOA -> BSRRH = 0x0001 | 0x0002 | …
ja
Ich glaube, du solltest mal ein Grundlagenbuch lesen.
Kapitel: Bitoperationen heute.
Beim Input-Register schaust du so nach, ob der Eingang HIGH ist. Dann
ergibt nämlich Input&Blabla eine 1. Ist das Dingen LOW, ergibt das im
Ergebnis 0.
Naja, IDR Register liest du. Es wirft dir für den ganzen Port die Daten
entgegen. Wenn dich nur ein Pin daraus interessiert, musst du eben mit
dem & auswählen, welcher das ist.
Auf das BSRRL/H Register schreibst du außerdem, daher der = Operator.
Und da setzt du dann eben auch die Bits die du verändern willst.
Ich bin am verzweifeln. Ich habe hier zwei Programme: Das eine benutzt
den User Button, um die LED's auf dem Board anzuschalten, und das andere
den Button, um externe LEd's anzuschalten. Beide sehen für mich völlig
identisch aus(naja, wenn man mal von den anderen Ports ausgeht usw.)
Hier der funktionierende Code:
Schau' mal in das Reference Manual/Datenblatt deines Prozessors. Da gibt
es eine Tabelle "Alternate Functions". Vielleicht liegen auf deinen Pins
schon andere Funktionen und du musst erst die Alternate Function korrekt
als GPIO (etwa anstelle von SPI, RAM-Anbindung, I2C, Timer, ...)
einstellen.
Sind die LEDs auch korrekt angeschlossen? Leuchtdioden glimmen nur bei
Stromfluss in eine bestimmte Richtung.
Edit: Für Suchfaule auch hier wieder ein Link auf dem Silbertablett, mit
einem Löffelchen Puderzucker obendrauf -
http://kornakprotoblog.blogspot.de/2012/01/pinout-spreadsheet-for-stm32f4.html
Ups, sehe ich ja jetzt erst. Dein return 0 ist eine Zeile höher
gerutscht. Damit beendest du deine vermeintliche Endlosschleife nach dem
ersten Durchlauf.
Bist du sicher, dass du Grundlagen der Programmierung überspringen und
so weitermachen willst?
Nö, da hat er nur an- und ausschalten umgedreht. Ist vom Prinzip her
egal, lediglich der Anfangszustand ändert sich.
Der Fehler ist die nach einem Durchmarsch beendete "Endlosschleife".
Stimmt, das war jetzt echt ein total be**** Fehler. Da hätte man auch
selber drauf kommen können :(
Aber der gdb zeigte mir aber immer an, das Programm würde noch
laufen('continue' beendete sich nicht, wie nach reset). Ist das normal?