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?
Hmm einfach ins kalte Wasser springen und ausprobieren. In der STM Firmware-Lib sind auch Beispiele drin. Ausprobieren, freuen und Stück für Stück nachvollziehen in Verbindung mit Ref. Manual, wenn du hinter die Firmware Lib schauen möchtest und wissen willst, wie es genau funktioniert. http://www.diller-technologies.de/stm32.html http://mikrocontroller.bplaced.net/wordpress/?page_id=744
> 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!
:
Bearbeitet durch User
Hoffentlich rentieren die später damit zu realisierenden Projekte den Aufwand ;-) Ansonsten, nimm einen simplen AVR und gut ist...
Funktioniert ganz gut mit den Beispielen von BPlaced.net. Habe allerdings noch ein Problem: Was sind das für zuweisungen mit BSRRL, ODR, BSRRH, etc.?
Also ich habe jetzt so ne Idee wofür die sein könnten, aber wo liegt der Unterschied zwischen den beiden?
1 | __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */ |
2 | __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)?
stm32f4er schrieb: > Also ich habe jetzt so ne Idee wofür die sein könnten, aber wo liegt der > Unterschied zwischen den beiden? >
1 | __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, |
2 | > Address offset: 0x18 */
|
3 | > __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, |
4 | > 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)? Steht alles im Refernce Manual: http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf
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.
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.
:
Bearbeitet durch User
> 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?
Ich meine das auf dem Discovery Board der STM mit 3V versorgt wird. Daran dürfte es liegen.
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.
Warum nehmt Ihr nicht die entsprechenden Funktionen ?
1 | HAL_GPIO_WritePin(GPIOD, GPIO_Pin_0, GPIO_BIT_SET); |
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.
Oh schau mal einer guck, fragt man Google, findet man ganz schnell das hier: http://www.willemer.de/informatik/cpp/sysprog.htm Bitte verinnerlichen. Klausur in zwei Stunden. ;)
Dirk K. schrieb: > Oh schau mal einer guck, fragt man Google, findet man ganz schnell > das > hier: http://www.willemer.de/informatik/cpp/sysprog.htm > > Bitte verinnerlichen. Klausur in zwei Stunden. ;) Vielen Dank. Sehr interressant.
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:
1 | #include "stm32f4xx_conf.h" |
2 | #include "stm32f4xx.h" |
3 | |
4 | void init_app(void) { |
5 | |
6 | |
7 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); |
8 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); |
9 | GPIO_InitTypeDef pins; |
10 | |
11 | pins.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; |
12 | pins.GPIO_Mode = GPIO_Mode_OUT; |
13 | pins.GPIO_OType = GPIO_OType_PP; |
14 | pins.GPIO_Speed = GPIO_Speed_100MHz; |
15 | pins.GPIO_PuPd = GPIO_PuPd_NOPULL; |
16 | GPIO_Init(GPIOD, &pins); |
17 | |
18 | |
19 | |
20 | pins.GPIO_Pin = GPIO_Pin_0; |
21 | pins.GPIO_Mode = GPIO_Mode_IN; |
22 | pins.GPIO_OType = GPIO_OType_OD; |
23 | pins.GPIO_Speed = GPIO_Speed_25MHz; |
24 | pins.GPIO_PuPd = GPIO_PuPd_DOWN; |
25 | |
26 | GPIO_Init(GPIOA, &pins); |
27 | }
|
28 | |
29 | int main(void) { |
30 | SystemInit(); |
31 | init_app(); |
32 | while(1) { |
33 | if (GPIOA->IDR & 0x0001) { |
34 | GPIOD -> BSRRH = 0x1000 | 0x2000 | 0x4000 | 0x8000; |
35 | }
|
36 | else { |
37 | GPIOD -> BSRRL = 0x1000 | 0x2000 | 0x4000 | 0x8000; |
38 | }
|
39 | }
|
40 | return 0; |
41 | }
|
Und hier der nicht funktionierende Code:
1 | #include "stm32f4xx.h" |
2 | #include "stm32f4xx_conf.h" |
3 | |
4 | void init_app(void); |
5 | |
6 | int main(void){ |
7 | SystemInit(); |
8 | init_app(); |
9 | while(1){ |
10 | if(GPIOA -> IDR & 0x0001){ |
11 | GPIOC -> BSRRL = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; |
12 | }
|
13 | else{ |
14 | GPIOC -> BSRRH = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; |
15 | }
|
16 | return 0; |
17 | }
|
18 | }
|
19 | |
20 | void init_app(void){ |
21 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); |
22 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); |
23 | GPIO_InitTypeDef pins; |
24 | pins.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; |
25 | pins.GPIO_Mode = GPIO_Mode_OUT; |
26 | pins.GPIO_OType = GPIO_OType_PP; |
27 | pins.GPIO_Speed = GPIO_Speed_100MHz; |
28 | pins.GPIO_PuPd = GPIO_PuPd_NOPULL; |
29 | GPIO_Init(GPIOC, &pins); |
30 | |
31 | |
32 | pins.GPIO_Pin = GPIO_Pin_0; |
33 | pins.GPIO_Mode = GPIO_Mode_IN; |
34 | pins.GPIO_OType = GPIO_OType_OD; |
35 | pins.GPIO_Speed = GPIO_Speed_25MHz; |
36 | pins.GPIO_PuPd = GPIO_PuPd_DOWN; |
37 | GPIO_Init(GPIOA, &pins); |
38 | }
|
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
:
Bearbeitet durch User
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?
Der funktionierende Code if (GPIOA->IDR & 0x0001) { GPIOD -> BSRRH = 0x1000 | 0x2000 | 0x4000 | 0x8000; Der nicht funktionierende Code if(GPIOA -> IDR & 0x0001){ GPIOC -> BSRRL = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; Oben BSRRH, unten BSRRL. Könnte es daran liegen?
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".
:
Bearbeitet durch User
>Der Fehler ist die nach einen Durchmarsch beendete "Endlosschleife".
Verdammt, du hast recht;)
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?
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.