Hallo zusammen,
Angeregt durch den momentan aktuellen Thread über den STM32
(Beitrag "STM32 für Einsteiger - der Artikel zum Krieg (µC Wahl)") habe ich mir auch mal
ein STM32F0 Discovery zugelegt und bin nun ein bisschen am
herumprobieren.
Bis jetzt habe ich immer nur PIC's programmiert. Dort gab es für das
Setzen oder löschen der Bits in den Registern immer sehr angenehme
Unions.
So z.B.
1
CLKRCONbits.CLKRSLR=0;
Ich finde diese Art ziemlich angenehm da ich mittlerweile die meisten
wichtigen Namen der Bits von der Standard Peripherie auswendig kann und
somit meistens schnell erkenne was genau konfiguriert wurde.
Beim STM gibt es soweit ich bis jetzt herausgefunden habe solche
Definitionen in dieser Art nicht.
Nehmen wir als einfaches Beispiel das USART Register CR1. Wenn ich nun
das Bit 27 also das EOBIE Bit setzen will. Wie mache ich das am
besten/leserlichsten?
So?
1
USART1->CR1=USART_CR1_EOBIE;
Oder gibt es noch eine schönere Methode? Und was ist wenn ich ziemlich
viele Bits setzten muss? Z.B. 20? Packt ihr die dann jeweils alle in
eine Zeile oder Unterteile ihr das in mehrere Zeilen?
Ja ich weiss das es die Standard Peripheral library gibt. Jedoch würde
ich gerne zu Begin erst einmal die Grundlegenden Dinge von "Hand"
machen.
Grüsse
Dominic A. schrieb:> Oder gibt es noch eine schönere Methode? Und was ist wenn ich ziemlich> viele Bits setzten muss? Z.B. 20? Packt ihr die dann jeweils alle in> eine Zeile oder Unterteile ihr das in mehrere Zeilen?
Siehe Bit-band mapping
Eine Speicheradressee wird als Bit genutzt, und die Inhalt der
Speicheradresse aber nur als ein Bit abgespeichert. Bei 32 bit sind ja
genug Speicheraderessen vorhanden.
Ich glaube damit kann eine ganze Menge Code eingespart werden, wenn
nicht ein Bit aus einem Byte herausgesucht werden muss.
Damit bekommt jedes Bit einen Namen.
Gruß
Mir scheint, beim STM32 ist Bit-band mapping oder bit banding ziemlich
uninteressant.
* Für die GPIO-Ports ist das GPIOx_BSRR (bit set/reset Register) viel
praktischer: man kann mit einem Befehl mehrere Bits setzen und löschen.
Außerdem brauche ich nur eine Definition für das Port-Bit, beim bit
banding muss ich diese in eine Adresse umrechnen, was für ein Krampf.
* Die kleinen STM32 (F0xx, F1xx, ???) haben kein bit banding
* Es geht das Gerücht, dass GPIOx_BSRR nur einen Schreibzyklus braucht
während bit banding read-modify-write machen muss (scheint mir
plausibel).
* Bei der restlichen Peripherie sollte so ein Mechanismus sowieso
unnötig sein, z.B. durch geschickte Verteilung der Status-Bits auf
verschiedene Register oder "löschen durch schreiben einer 1" oder
ähnliche Hardware-Tricks.
Kennt jemand ein Beispiel, wo bit banding wirklich nützlich ist?
Ersan G. schrieb:> CR1 (1 << 49);
Ja das ist klar. Aber hier sieht man ohne im Datenblatt nachzusehen
nicht was für ein Bit gesetzt wurde.
Scheinbar habe ich mich ein bisschen falsch ausgedrückt. Mir geht es
nicht darum einen Pin zu setzten oder löschen. Viel mehr darum wie ich
am leserlichsten ein Peripherie Modul initialisiere.
Die meisten Hersteller haben dazu ein Header File in welchem fast alle
Register mithilfe von Unions definiert sind. Gibt es soetwas beim STM
nicht?
> Viel mehr darum wie ich> am leserlichsten ein Peripherie Modul initialisiere.
STM Lib benutzen? Zum Beispiel an Hand eines GPIO-Outputs:
void GPIO_OUT_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOD Periph clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Configure PD14 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
Dominic A. schrieb:> Die meisten Hersteller haben dazu ein Header File in welchem fast alle> Register mithilfe von Unions definiert sind. Gibt es soetwas beim STM> nicht?
Ein union reicht nicht, wenn schon brauchts (auch) ein Bitfield. Die
sind vermutlich nicht definiert weil dann jemand auf die Idee kommen
könnte zu schreiben:
1
RegisterXY.BitFoo=0;
2
RegisterXY.BitBla=1;
3
RegisterXY.BitBlubb=1;
Was aufgrund von "volatile" dann 3 Read-Modify-Write-Zyklen generieren
würde, was sehr ineffizient wäre gegenüber dem einzelnen
Read-Modify-Write-Zyklus von:
1
RegisterXY=(RegisterXY&~BitFoo)|BitBla|BitBlubb;
Aber wie schon erwähnt, ist der direkte Register-Zugriff sehr lowlevel
und deswegen hässlich; wenn du es schön haben willst, verwende die
Standard Peripheral Library.
Ich zeige mal ein Beispiel, wie Renesas es bei seinen H8/SH/RX macht.
SYSTEM.SYSCR0.WORD = 0x5a03; // Bus freigeben
SYSTEM.SCKCR.LONG = (unsigned long) 0x00030100;
PORT5.DDR.BIT.B3 = 1;
PORTC.DDR.BYTE = 0x3f;
PORTC.DR.BIT.B2 = 0;
....
PORTC.DR.BIT.B2 = 1;
PORTB.DDR.BYTE = 0xff;
PORTD.DDR.BYTE = 0xff;
IOPORT.PFCR0.BYTE = 0x06; // CS1 + CS2
Da kann man bis auf die Bit-Ebene (auch Bitfelder) die Peripherie nach
belieben ansprechen. Das ist zum einen sehr direkt und dennoch
nachvollziehbar und schon garnicht 'häßlich'.
Dabei entsprechen die Bezeichner den Angaben im Datenblatt.
Und noch ein Beispiel für Bitelemente > 1
EXDMAC0.EDMDR.BIT.MDS = 3; // cluster transfer mode
EXDMAC0.EDACR.BIT.ARS = 2; // SAR wird inkrementiert
Hinzu kommt auch noch, dass Daten- und Adressbusse immer an einem PORTx
zusammengefaßt sind und nicht querbeet über verschiedene ext. Pins
verteilt sind. Mir macht das die Renesas µCs viel sympathischer.
m.n. schrieb:> Ich zeige mal ein Beispiel, wiem.n. schrieb:> Da kann man bis auf die Bit-Ebene (auch Bitfelder) die Peripherie nach> belieben ansprechen.
Gähhnnn, hatten wir schon, geht mit Bitbanding.
32Bit Adressraum schrieb:> Gähhnnn, hatten wir schon, geht mit Bitbanding.
Schön, dann kannst Du mir ja auch ein Beispiel zeigen, wie man damit den
DMAC anspricht. Dann kann man sich ein Bild machen, wie elegant und
transparent das aussieht.
Oder mußt Du erst noch wach werden und willst dann vom Gesagten nichts
mehr wissen?
m.n. schrieb:> Ich zeige mal ein Beispiel, wie Renesas es bei seinen H8/SH/RX macht.
Ziemlich genau auf diese Art meine ich. Ist ja fast genau so wie man es
beim PIC auch macht. (Siehe StarterPost)
32Bit Adressraum schrieb:> Gähhnnn, hatten wir schon, geht mit Bitbanding.
Ja und wie mach ich das nun mit dem Bitbanding? Selber ein Define für
jede Adresse?
Davis schrieb:> ...> LED_GPIO_PORT->BSRR=GREEN_LED_PIN | BLUE_LED_PIN;> Delay(5000);> ...
Es geht immernoch nicht um die IO Register.
Auch wie effizient dieser Code schlussendlich ist ist mir eher egal. Ob
man nun 50 oder 200 Takte zum Initialisieren benötigt spielt ja meistens
nicht eine so grosse Rolle. Auch sind zu 90% beim Initialisieren noch
keine Interrupts aktiv und somit muss der Zugriff auch nicht Atomar
sein. Selbst wenn die Interrupts schon aktiv wären wird ja selten in der
ISR an den Config Bits der Peripherie herum geschraubt.
Dr. Sommer schrieb:> Aber wie schon erwähnt, ist der direkte Register-Zugriff sehr lowlevel> und deswegen hässlich; wenn du es schön haben willst, verwende die> Standard Peripheral Library.
Scheinbar ist die Std Lib für gut erkennbare Config die einzige
Variante.
32Bit Adressraum schrieb:> Gähhnnn, hatten wir schon, geht mit Bitbanding.
Da von Dir nichts Konstruktives mehr gekommen ist, gehe ich davon aus,
dass Du keine Belege für Deine Aussage liefern kannst.
gnd3 schrieb:> * Es geht das Gerücht, dass GPIOx_BSRR nur einen Schreibzyklus braucht> während bit banding read-modify-write machen muss (scheint mir> plausibel).
Bit Banding ordnet eine 32-bit Adresse einem Port-Pin zu, somit kann man
direkt schreiben - ein Schreibzyklus. Ab M3 ist Bit Banding verfügbar,
und nun auch beim M0 Nachfolger M0+
Lothar schrieb:
>> * Es geht das Gerücht, dass GPIOx_BSRR nur einen Schreibzyklus braucht>> während bit banding read-modify-write machen muss (scheint mir>> plausibel).>> Bit Banding ordnet eine 32-bit Adresse einem Port-Pin zu, somit kann man> direkt schreiben - ein Schreibzyklus.
Dann müsste aber jeder Port und jedes Status-Register usw.
Bit-adressierbar sein, was praktisch die gleiche Hardware erfordert wie
für die GPIOx_BSRR Register nur eben für die gesamte Peripherie! Das
scheint mir zu aufwendig zu sein. Und wenn es rein in der CPU
implementiert ist, geht es doch nur per read-modify-write?
> Ab M3 ist Bit Banding verfügbar, und nun auch beim M0 Nachfolger M0+
aber nur optional, d.h. es ist Glückssache, ob es für meinen nächsten
Chip verfügbar ist.
Vorläufig bleib' ich mal dabei: Bit Banding ist (jedenfalls beim STM32)
überflüssiger Krampf.
gnd3 schrieb:> Mir scheint, beim STM32 ist Bit-band mapping oder bit banding> ziemlich> uninteressant.
Stimmt nicht.
> * Die kleinen STM32 (F0xx, F1xx, ???) haben kein bit banding
Stimmt auch nicht.
Mr.T schrieb:
> Stimmt nicht.> Stimmt auch nicht.
ich lasse mich gerne belehren, wo kann man das nachlesen? In den
Datenblättern vom STM32F103, STM32F051 und STM32F050, im Reference
Manual F050/051 (RM0091) und im Cortex M0 Programming Manual (PM0215)
finde ich keine Silbe zum Thema. Also existiert es für mich nicht.
Im STM32F1xx Referenzmanual (3.3.2) kann mans z.B. nachlesen,
funktioniert auch tatsächlich wie beschrieben.
Bei den STM32F0xx scheints das aber laut Refman nicht zu geben.
gnd3 schrieb:> Mr.T schrieb:>> Stimmt nicht.>> Stimmt auch nicht.>> ich lasse mich gerne belehren, wo kann man das nachlesen? In den> Datenblättern vom STM32F103, STM32F051 und STM32F050, im Reference> Manual F050/051 (RM0091) und im Cortex M0 Programming Manual (PM0215)> finde ich keine Silbe zum Thema. Also existiert es für mich nicht.
Hans Heinz schrieb:> Bei den STM32F0xx scheints das aber laut Refman nicht zu geben.
Weil das noch M0 sind.
gnd3 schrieb:>> Ab M3 ist Bit Banding verfügbar, und nun auch beim M0 Nachfolger M0+>> aber nur optional, d.h. es ist Glückssache, ob es für meinen nächsten> Chip verfügbar ist.
Bit Banding im M0+ war Hersteller-Wunsch (auch von STM), eben weil es
der Standard ist, und die Portierbarkeit sicherstellt.
Bitbanding ist eine Eigenschaft des Cores, nicht der Peripherie oder der
Busse des Controllers. Wenn der Hersteller des Controllers den Core mit
dem SDK zusammenbaut, dann kann er sich bei einigen Features aussuchen,
ob und ggf. in welchem Unfang er sie integriert. Es läuft darauf hinaus,
dass ab M3 Bitbanding drin ist, beim M0 (ohne +) nicht.
In jedem Fall aber ist das eine Eigenschaft des Cores. Da der Bus keine
Einzelbitoperationen unterstützt, lässt sich eine entsprechende
Schreiboperation im Core nur über eine Kombination aus Lese- und
Schreibzyklus implementieren. ARM dazu: "For writes, it converts the
write to an atomic read-modify-write operation". Es liegt auf der Hand,
dass dies entsprechend mehr Zeit benötigt.
m.n. schrieb:> Was ich aber nicht sehe, dass diese vielen #define irgendwie schön,> elegant oder sonderlich transparent wären.
Yep. So sind die Bits als Maske definiert, was einer Verwendung per
Bitbanding etwas im Weg steht. Zudem ist mindestens bei den STM32 die
Definition von auf mehreren Bits bestehenden Feldern geradezu exquisit
bescheuert durchgeführt - nämlich so, als wären das nicht z.B. 4 Bits
mit einem Code, sondern 4 einzelne Bits.
A. K. schrieb:> ARM dazu: "For writes, it converts the> write to an atomic read-modify-write operation". Es liegt auf der Hand,> dass dies entsprechend mehr Zeit benötigt.
Der Zeitverbrauch ist m. E. kein großer Nachteil, da beim 'manuellen'
r-m-w-Zugriff kein Interrupt dazwischen funken kann, und das ist wohl
auch der eigentliche Sinn. Anderfalls müßte man mögliche Interrupts
unterbinden, was wohl noch mehr Zeit benötigt.
Lesezugriffe können immer direkt erfolgen, es sei denn, irgendein
Statusbit wird durch reines Lesen zurückgesetzt.
gnd3 schrieb:> Mr.T schrieb:>> Stimmt nicht.>> Stimmt auch nicht.>> ich lasse mich gerne belehren, wo kann man das nachlesen? In den> Datenblättern vom STM32F103, STM32F051 und STM32F050, im Reference> Manual F050/051 (RM0091) und im Cortex M0 Programming Manual (PM0215)> finde ich keine Silbe zum Thema. Also existiert es für mich nicht.
Mein Gott...
J.Yiu - The Definitive Guide to the ARM Cortex-M3 2nd Ed. Kap 5.5.
Das gilt für JEDEN Cortex-M3! Wenn man natürlich im M0 Manual nachliest
und dann auch noch meint ein STM32F103 sein wohl ein M0.... dann hat man
bewiesen, dass die eigene Lesestärke verbesserungswürdig ist.
gnd3 schrieb:> Vorläufig bleib' ich mal dabei: Bit Banding ist (jedenfalls beim STM32)> überflüssiger Krampf.
Meistens unnötig hat es aber einen ganz entscheidenden Vorteil: Die
Operation ist von Haus aus atomar. Bei den GPIOs der STM32 ist das egal,
da gibt es mit dem BSRR eine weit bessere Alternative, aber bei normalen
I/O-Registern ohne set/reset Variante kann eine atomare Operation recht
angenehm sein.
Lothar schrieb:> Bit Banding ordnet eine 32-bit Adresse einem Port-Pin zu, somit kann man> direkt schreiben - ein Schreibzyklus.
Nur aus Sicht der verwendeten Prozessorbefehle, nicht aus Sicht der
Peripheriebausteine und Busse. Der Core setzt einen solchen
Schreibzyklus in mehrere Buszyklen um.
m.n. schrieb:> da beim 'manuellen'> r-m-w-Zugriff kein Interrupt dazwischen funken kann,
Nicht aufregen, falscher Fehler: Es darf natürlich nichts dazwischen
funken!
Dr. Sommer schrieb:> Aber wie schon erwähnt, ist der direkte Register-Zugriff sehr lowlevel> und deswegen hässlich; wenn du es schön haben willst, verwende die> Standard Peripheral Library.
Schönheit liegt im Auge des Betrachters. ;-)
Mir gehts jedenfalls andersrum, weil anhand des Source Codes und der
Doku des Controllers klar ist, was da passiert. Bei der Lib muss man zur
Klärung dessen, was deren Autor sich bei der Lib-Struct und -Funktion
gedacht hatte, oft zusätzlich auch noch den Source Code der Lib zu Rate
ziehen.
Ich abstrahiere lieber eine Ebene drüber. Ein UART-Treiber darf gerne
direkt an die Bits ran.
Dazu kommt, dass der Lib oft eine gewisse Umständlichkeit eigen ist.
Ganz besonders bei der Port-Initialisierung.
A. K. schrieb:> Zudem ist mindestens bei den STM32 die> Definition von auf mehreren Bits bestehenden Feldern geradezu exquisit> bescheuert durchgeführt - nämlich so, als wären das nicht z.B. 4 Bits> mit einem Code, sondern 4 einzelne Bits.
Was auch nervt, dass wohl wegen des kleinen Adressbereiches für die
Peripherie (ironisch) 'Adressklumpen' gebildet werden. Beispiel DMAC:
Für jeden Kanal gibt es identische Register für Start, Count, Status,
... die sich nur im Offset zur Basisadresse unterscheiden. Das ist gut.
Aber dann werden ein paar Bits aller acht Kanäle in DMA_LISR, DMA_HISR,
DMA_LIFCR und DMA_HIFCR gequetscht, anstatt eine einheitliche Struktur
für alle DMA-Kanäle zu schaffen.
Hans Heinz schrieb:
> Im STM32F1xx Referenzmanual (3.3.2) kann mans z.B. nachlesen,> funktioniert auch tatsächlich wie beschrieben.
o.k., überredet, danke. Aber ein Hinweis im Datenblatt wäre schon nett,
wenigstens im Bild "Memory Map" könnte es ruhig auftauchen.
Mr.T schrieb:
> Mein Gott...
zu viel der Ehre ;)
> J.Yiu - The Definitive Guide to the ARM Cortex-M3 2nd Ed. Kap 5.5.
Das hättest du besser nicht erwähnt. Das ist für mich der
entscheidende Nachteil der ARM-Controller. Bei jedem anderen uC gibt's
die vollständige Info direkt beim Hersteller.
> Das gilt für JEDEN Cortex-M3!
eben nicht; oder was bedeutet "optional"?
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Behcjiic.html> Wenn man natürlich im M0 Manual nachliest und dann> auch noch meint ein STM32F103 sein wohl ein M0
das meint doch keiner, die Aussage war vor allem "nicht jeder STM32 hat
bit banding" (natürlich besonders die, die am besten zu meiner Hardware
passen)
gnd3 schrieb:> das meint doch keiner, die Aussage war vor allem "nicht jeder STM32 hat> bit banding" (natürlich besonders die, die am besten zu meiner Hardware> passen)
Bei den LPC M0+ z.B. LPC800 wird Bit Banding unterstützt (es ist davon
auszugehen dass STM nachzieht):
– Bit-banding region
– Single cycle access to all port pins
– Support high frequency I/O toggling – As fast as CPU_Clock/2 = 15MHz
– Enhanced GPIO Pin Manipulation – Capable of simultaneously reading
Bit/Byte/Word or toggling up to 18 I/Os per instruction
> Mir gehts jedenfalls andersrum, weil anhand des Source Codes und der> Doku des Controllers klar ist, was da passiert. Bei der Lib muss man zur> Klärung dessen, was deren Autor sich bei der Lib-Struct und -Funktion> gedacht hatte, oft zusätzlich auch noch den Source Code der Lib zu Rate> ziehen.
Dafür hat die Lib auch eine eigene Dokumentation...
>> Ich abstrahiere lieber eine Ebene drüber. Ein UART-Treiber darf gerne> direkt an die Bits ran.
Ja, genau das macht der UART-Treiber in der Standard Peripheral Library
auch; den kannst du dann mit hübschen Funktionen verwenden.
> Dazu kommt, dass der Lib oft eine gewisse Umständlichkeit eigen ist.> Ganz besonders bei der Port-Initialisierung.
Die Lib ist halt relativ dumm und nah an der Hardware. Wie hier im Forum
schon öfter besprochen ist die Standard Peripheral Library und CMSIS
schon ziemlich hässlich, aber besser als Register-Bit-Fummelei allemal -
und man erhält halbwegs lesbaren Code...