Hi
Ich stehe gerade völlig auf dem Schlauch beim setzten eines Registers
des STM32F407VET.
Konkret geht es um GPIOB->AFR Register. In diesem möchte ich AF4
(0b0100) für PIN 6 und 7 setzten (weil ich I2C aktivieren möchte).
Bisher habe ich bestimmte Sachen immer so aktiviert wie hier den Pullup
für PB6:
1
GPIOB->PUPDR|=GPIO_PUPDR_PUPD6_1;
Wenn ich aber nun folgendes versuche
1
GPIOB->AFR|=GPIO_AFRL_AFRL6_2;
bekomme ich diese Fehlermeldung:
1
error: assignment to expression with array type
Kann mir jemand erhellen warum die erste zuweisung geht und die zweite
nicht? GPIOB->AFR und GPIOB->PUPDR sind doch einfach nur zwei Register
und GPIO_PUPDR_PUPD6_1 und GPIO_AFRL_AFRL6_2 sind doch nur bestimmte
Bits in den Register...
Paul G. schrieb:> GPIOB->AFR[0] |= GPIO_AFRL_AFRL6_2;
Das macht trotzdem sehr wenig Sinn. Es handelt sich um eine Kombination
von 4 Bits, von denen manche 0, manche 1 sind. Das '|=' kann aber nur
Bits setzen, nicht löschen. Und man sollte nicht stillschweigend
voraussetzen, dass vorher alles nur Nullen waren ...
A. B. schrieb:> Paul G. schrieb:>> GPIOB->AFR[0] |= GPIO_AFRL_AFRL6_2;>> Das macht trotzdem sehr wenig Sinn.
Doch, da man das meistens nur bei der Initialisierung des Controllers
ändert und der Reset-Wert 0 ist.
John Doe schrieb:> Doch, da man das meistens nur bei der Initialisierung des Controllers> ändert und der Reset-Wert 0 ist.
Aufpassen: bei den neueren STM32 ist GPIOx->MODER mit 0xffffffff
initialisiert. Hier muß man die betreffenden Bits erst löschen, wenn man
keinen Analog-Eingang haben möchte.
John Doe schrieb:> Doch, da man das meistens nur bei der Initialisierung des Controllers> ändert und der Reset-Wert 0 ist.
Ja, ja, das "meistens" spricht Bände ... das glaube ich nur demjenigen,
der dann auch die Lock-Bits setzt.
Paul G. schrieb:> STM32F407VETm.n. schrieb:> John Doe schrieb:>> Doch, da man das meistens nur bei der Initialisierung des Controllers>> ändert und der Reset-Wert 0 ist.>> Aufpassen: bei den neueren STM32 ist GPIOx->MODER mit 0xffffffff> initialisiert. Hier muß man die betreffenden Bits erst löschen, wenn man> keinen Analog-Eingang haben möchte.
Es ist von einem STM32F407VET die Rede...
Die sind offenbar bei den neueren STMs schon von Werk aus definiert in
der stm32<modellreihe>.h
Finde ich ganz praktisch, auch wenn es etwas mehr Schreibarbeit ist.
Insbesondere die MODIFY_REG Variante finde ich praktisch.
Markus M. schrieb:> #define SET_BIT(REG, BIT) ((REG) |= (BIT))
Da lauert für mich der Fehler, daß ich unter BIT eine Bit-Nummer
verstehe, hier aber eine Bit-Maske erwartet wird. Für Setzen/Löschen
eines Portpins eignet sich dieser Befehl nicht, da er nicht atomar
ausgeführt wird.
Markus M. schrieb:> #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG),> (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
Wie sieht der Code mit diesem Makro aus, wenn zum Beispiel bit0 - bit3
eines Ports auf Ausgang gesetzt werden sollen?
Ich schreibe es lieber explizit hin; da sehe ich besser, wo etwas
passiert.
Planloser schrieb:> Es ist von einem STM32F407VET die Rede...
noch ...
m.n. schrieb:> Markus M. schrieb:>> #define SET_BIT(REG, BIT) ((REG) |= (BIT))>> Da lauert für mich der Fehler, daß ich unter BIT eine Bit-Nummer> verstehe, hier aber eine Bit-Maske erwartet wird. Für Setzen/Löschen> eines Portpins eignet sich dieser Befehl nicht, da er nicht atomar> ausgeführt wird.
Hinsichtlich der möglichen Fehlinterpretation: SET_BIT*S* statt SET_BIT
wär' vielleicht eine gute Idee.
Fürs atomare Setzen/Löschen sogar mehrerer(!) Port-Pins (von einem Port)
gleichzeitig nimmt man auch das BSRR.
m.n. schrieb:>> #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG),>> (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))> Wie sieht der Code mit diesem Makro aus, wenn zum Beispiel bit0 - bit3> eines Ports auf Ausgang gesetzt werden sollen?
Das ist eingentlich ganz einfach:
* Der erste Parameter gibt das Register an, dass man ändern will.
* Der zweite Parameter gibt die Bit-Gruppe an, die man ändern will.
* Der dritte Parameter gibt die neuen Werte an.
Beispiel für den STM32F303:
Hier gibt es keine Definitionen für die 4 Modi
* 00: Input mode (reset state)
* 01: General purpose output mode
* 10: Alternate function mode
* 11: Analog mode
Sondern nur zwei einzelne für die beiden Bits. Will man mehr als ein Bit
setzen, muss man kombinieren:
Um auf deine Frage zurück zu kommen:
> Wie sieht der Code mit diesem Makro aus, wenn zum Beispiel bit0 - bit3> eines Ports auf Ausgang gesetzt werden sollen?
1
// PA0-3 = Output
2
MODIFY_REG(GPIOA->MODER,
3
GPIO_MODER_MODER0+
4
GPIO_MODER_MODER1+
5
GPIO_MODER_MODER2+
6
GPIO_MODER_MODER3,
7
GPIO_MODER_MODER0_0+
8
GPIO_MODER_MODER1_0+
9
GPIO_MODER_MODER2_0+
10
GPIO_MODER_MODER3_0);
Kann man schön finden, muss man nicht. Der darauf generierte Assembler
Code sieht jedenfalls deutlich schlanker aus.
Ben S. schrieb im Beitrag #6570130:
> Immer bei jedem Controller ins Datenblatt schauen, wie die Resetwerte> wirklich sind. Oder ein Makro/Funktin schreiben, welches vorher die> betreffenden Bits nullt.
Beim F407 ist der Reset Value des Registers GPIOB->AFR[] 0x00000000. Und
da ich im Moment auch die Funktion des PINs nicht weiter ändere war mir
das nicht wichtig das ich die restlichen drei Nullen nulle ;)
Stefan ⛄ F. schrieb:> Um auf deine Frage zurück zu kommen:>> Wie sieht der Code mit diesem Makro aus, wenn zum Beispiel bit0 - bit3>> eines Ports auf Ausgang gesetzt werden sollen?// PA0-3 = Output> MODIFY_REG(GPIOA->MODER,> GPIO_MODER_MODER0+> GPIO_MODER_MODER1+> GPIO_MODER_MODER2+> GPIO_MODER_MODER3,> GPIO_MODER_MODER0_0+> GPIO_MODER_MODER1_0+> GPIO_MODER_MODER2_0+> GPIO_MODER_MODER3_0);>> Kann man schön finden, muss man nicht. Der darauf generierte Assembler> Code sieht jedenfalls deutlich schlanker aus.
Ich finde es auf keinen Fall schön, denn, was dort passieren soll, ist
überhaupt nicht ersichtlich. Das ist doch mindestens genau so
intransparent wie HAL.
m.n. schrieb:> Ich finde es auf keinen Fall schön, denn, was dort passieren soll, ist> überhaupt nicht ersichtlich. Das ist doch mindestens genau so> intransparent wie HAL.
Meiner Meinung nach fehlen da Definitionen mit sprechenden Namen, also
so:
1
MODIFY_REG(GPIOA->MODER,
2
GPIO_MODER_MODER0+
3
GPIO_MODER_MODER1+
4
GPIO_MODER_MODER2+
5
GPIO_MODER_MODER3,
6
GPIO_MODER_MODER0_OUTPUT+
7
GPIO_MODER_MODER1_OUTPUT+
8
GPIO_MODER_MODER2_OUTPUT+
9
GPIO_MODER_MODER3_OUTPUT);
Bei den vielen Registern wird das ja auch so gemacht - warum nicht bei
allen? Das müsste man wohl ST fragen.
Für jeden Pin an den Bits rumwackeln, egal ob mit Magic Numbers oder
CMSIS oder Macros und CMSIS, ist doch etwas mühsam.
Da empfiehlt es sich einen kleinen "Treiber" zu schreiben.
Dieser bekommt ein struct mit sinnvollen enums.
Bei den STM32 und anderen größeren SoCs sind die Pinbetriebsarten doch
ziemlich mächtig.
Dann ist ein Pin ausreichend beschrieben und daraus lässt sich auch ein
Array bilden falls man mal einem Treiber den SPI austrauschen muss oder
auch die alternativen Pins eines SPI nutzen will.
Damit meine ich jetzt nicht das aufgeblasene Teil der STM32 HAL, was so
ziemlich garnicht abstrahiert.
Hier im Forum kam ja schonmal die Frage auf wieso man beim HAL
Konfigstruct etwas auf output stellen muss für eine alternate function
input.
Beispiel:
uint8_talt_func;//!< alternate function, see datasheet
36
};
Das enum gpio_mode ist so mit Bits hinterlegt, dass die nurnoch
auseinander gezogen werden müssen zum Register bespielen,
Bei der alt_func lässt sich leider nicht viel abstrahieren, das ist je
nach STM32 Familie anders und bei den kleineren ist das sogar je nach
Port anders belegt.
Das geht sicher besser, aber ist bisher ausreichend.
Alles, was in Richtung Eierlegendewollmilchsau geht, ist letztlich für
einen Außenstehenden schwer nachzuvollziehen.
Mein Beispiel für allgemeinen Bedarf:
John Doe schrieb:> Doch, da man das meistens nur bei der Initialisierung des Controllers> ändert und der Reset-Wert 0 ist.
Das ist ne Blindheit und es ist PFUSCH.
Sowas sollte man immer richtig machen, also derart, daß man sich eben
nicht drauf verläßt, daß ja sowieso und meistens und überhaupt da der
gewünschte Defaultwert drinsteht.
W.S.
Stefan ⛄ F. schrieb:> Meiner Meinung nach fehlen da Definitionen mit sprechenden Namen, also> so:> MODIFY_REG(GPIOA->MODER,> GPIO_MODER_MODER0+> GPIO_MODER_MODER1+> GPIO_MODER_MODER2+...
Und was ist daran ein "sprechender" Name?
Nö, so eben nicht. Hier hat der Mw E. mal ausnahmsweise eine Idee in die
richtige Richtung gehabt. Es ist nämlich vermutlich weitaus besser, wenn
man gerade bei µC-Typen, die das Portvehalten in irgendwelchen
Sammelregistern steuern, mit einer Initialisierungsroutine und
geeigneten Makros arbeitet. Das testet man einmal gründlich aus und kann
es dann ohne weitere Änderungen für alle auf dieser Plattform zu
machenden Projekten verwenden. Und sowas ergibt einen schlanken Code.
Ich geb mal ein Beispiel zur Erläuterung:
1
/* Die Varianten der Pinfunktionen */
2
#define noPin 4 /* für nicht vorhandene Pins */
3
#define ANALOG 0 /* analoger Eingang (oder Ausgang?) */
4
#define OUT_10 1 /* out, 10 MHz */
5
#define OUT_2 2 /* out, 2 MHz */
6
#define OUT_50 3 /* out, 50 MHz */
7
#define IN 4 /* digitaler Eingang */
8
#define OUT_10_OD 5 /* out, 10 MHz, OpenDrain */
9
#define OUT_2_OD 6 /* out, 2 MHz, OpenDrain */
10
#define OUT_50_OD 7 /* out, 50 MHz, OpenDrain */
11
#define IN_PUPD 8 /* digitaler Eingang, Pullup/down je nach GPIOx_ODR */
Aber dort muß man nach dem gründlichen Austesten nie wieder heran.
Ähnlich kann man es halten mit den Einstellungen für andere Zwecke wie
AHB, APB1/2 usw. Zuerst ein rein optisch gut überschaubarer Teil mit
Bezeichnern, die man gut erfassen kann und weiter hinten dann der
Kruscht, den man nur einmal gründlich austesten muß und der dann immer
gleich bleibt.
W.S.
> MODIFY_REG(GPIOA->MODER,> GPIO_MODER_MODER0+> GPIO_MODER_MODER1+> GPIO_MODER_MODER2+...W.S. schrieb:> Und was ist daran ein "sprechender" Name?
So heißen die Register und Bitfelder halt auch im Referenzhandbuch.
> GPIOA->MODER
Ist das MODER Register vom Port GPIOA
> MODER0
Ist das MODER Feld für Pin 0
Dementsprechend ist GPIO_MODER_MODER0 die Bitmaske für das Feld MODER0
im MODER Register von Port GPIOA. Ich finde das total logisch. Ist zwar
anders als bei AVR, aber auch gut.
Stefan ⛄ F. schrieb:> W.S. schrieb:>> Und was ist daran ein "sprechender" Name?>> So heißen die Register und Bitfelder halt auch im Referenzhandbuch.>>> GPIOA->MODER>> Ist das MODER Register vom Port GPIOA>>> MODER0>> Ist das MODER Feld für Pin 0
Grandios! Und warum rezitierst du dann in einer das Refman? Kein Mensch
will da wissen, daß im GPIOA_MODER sowas wie die Elemente
GPIO_MODER_MODER_0..x enthalten sind, das kann jeder (so er will) alles
im RefManual nachlesen.
Was hingegen ein jeder gern mag, ist auf einfache und übersichtliche
Weise angeben zu können, daß zum Beipiel
PA3 = analog
und PB4 = input
und PC2 = Alternativfunktion
sein sollen.
Das ist hilfreich.
Aber irgendwelche Funktionen a la
MODIFY_REG(Register, Bit0+Bit1+Bit2+....+sonstwas)
sind überhaupt nicht hilfreich. Bei sowas muß man sich neben das RefMan
seinen Notizblock legen und manuell den ganzen Parameter-Kruscht
zusammenstellen. Unbequemer geht's garantiert nicht.
W.S.
W.S. schrieb:> Was hingegen ein jeder gern mag, ist auf einfache und übersichtliche> Weise angeben zu können, daß zum Beipiel> PA3 = analog> und PB4 = input> und PC2 = Alternativfunktion> sein sollen.> Das ist hilfreich.
Kannst du haben, in Form der HAL. Jeder so wie er mag.