Forum: Mikrocontroller und Digitale Elektronik (STM32) Register Lib mit define oder nicht define..


von DraconiX (Gast)


Angehängte Dateien:

Lesenswert?

Ich komme aus der AVR Ecke und bin es jahrelang gewohnt meine Register 
selbst zu beschreiben.

Nun gibt es ja für die STM32 die HAL Lib und die StdPeriph Lib. Die 
nehmen einen ja schon ne Menge Arbeit ab, aber ich mach es lieber 
"händisch" und beschreibe die Register selber. Früher(tm) habe für die 
AVR die Definitionsfiles von Atmel genutzt, wo ich direkt die Register 
laut Datenblatt beschreiben konnte - so möchte ich das bei den STM32 
auch gerne haben, also habe ich mich hingesetzt und die Register 
übertragen.

Nun allerdings stellt sich mir die Frage, weil das Thema gerade hier 
aktuell war, das #defines ja sehr "verpönt" sind. Oder ist dies alles 
bloß "Voodoo" und nicht nachvollziehbar?

Ich habe mal ein Teil meines RCC Registers angehangen - zugegeben, der 
ARM hat schon ein "paar" Register mehr und somit auch mehr defines, die 
mir später auch zum Problem im Code werden können. Gibt es da 
Alternativen wie man dies anlegen kann?

von RächdSchraib Leera (Gast)


Lesenswert?

DraconiX schrieb:
> Ich habe mal ein Teil meines RCC Registers angehangen

Angehängt!

DraconiX schrieb:
> zugegeben, der
> ARM hat schon ein "paar" Register mehr und somit auch mehr defines, die
> mir später auch zum Problem im Code werden können.

Das wird es auch. Du wirst wegen des Aufwandes der Neuerfindung
des Rades im Urschleim deiner Entwicklungen stecken bleiben.

von DraconiX (Gast)


Lesenswert?

RächdSchraib Leera schrieb:
> DraconiX schrieb:
>> Ich habe mal ein Teil meines RCC Registers angehangen
>
> Angehängt!

In unserer Dorfgemeinschaft fliege ich raus, wenn ich mit perfekten 
Hochdeutsch kommen würde. ;-)


RächdSchraib Leera schrieb:
> Das wird es auch. Du wirst wegen des Aufwandes der Neuerfindung
> des Rades im Urschleim deiner Entwicklungen stecken bleiben.

Wieso Neuerfindung? Und wieso Aufwand? Klar ist das eine Fleißarbeit, 
aber ich mache die Register ja sowieso so wie ich sie brauche. Brauche 
ich die Timer, schreibe ich mir mein Timer Register, brauche ich CAN 
dann das CAN. Das baut sich doch mit der Zeit auf. Ich werde sicherlich 
nicht alle Register gleichzeitig schreiben, sondern so wie ich diese 
halt brauche.



Aber dein Einwand beantwortet halt nicht meine Frage.

von --- (Gast)


Lesenswert?

Das kannst du dir sparen, weil es diese Datei bereits gibt. Wenn du dir 
die HAL/CMSIS herunterlädst, wirst du eine Datei mit den Register 
Defines finden. Auf den Rest der Lib kannst du ja verzichten. Wobei die 
CMSIS ganz brauchbar ist. Es kann sein, dass du noch eine paar 
Abhängigkeiten zur HAL entfernen musst, aber das sollte schneller gehen 
als alles neu zu schreiben.

von Curby23523 N. (Gast)


Lesenswert?

Die Register der STM32 sind mitnichten komplizierter, als die eines AVR.
Ich verwende keine HAL oder Stdlib. Für die Initialisierung eines Timers 
brauche ich 5 Zeilen.

1. Clock zuweisen
2. Prescaler zuweisen
3. Zählregister zuweisen
4. Timer starten

Wenn dann noch Interrupt benötigt wird:

5. INT im Timer aktivieren
6. INT im NVIC aktivieren

Es gibt sehr viele Zusatzfunktionen die aber - wenn man sie nicht 
benötigt - einfach deaktiviert bleiben.

Das Ergebnis von HAL und StdLib ist ganz einfach, dass viele nicht mehr 
wissen werden, was im µC passiert. Dann wird einer auf Klickibunti 
gemacht, wie mit Windows APIs etc.

Mein Rat: Lies das Datenblatt bzw. die vorhandenen Register einmal 
durch. Von den gängigen Peripheribausteinen, ist der ADC etwas 
ungewohnt. Ansonsten ist da aber grundsätzlich nicht viel anders, als es 
noch bei einem Atmega8 war. Beispiel DAC:

1. Clock zuweisen
2. DAC aktivieren
3. ggf. Buffer aktivieren
4. DAC GPIO Ausgangspin auf Analog setzen.

Schon kann man den DAC mit Werten füttern. Natürlich muss auch die Clock 
des GPIOA aktiviert werden. Das ist aber nur eine Zeile COde mehr.

Oder für die RTC kannst du HAL nehmen oder folgendes. Dieser Code 
akvitiert den extern Quarz, programmiert die PLL, aktiviert diese, weist 
diese als Systemclock zu und deaktiviert den internen Oscillator. Ein 
paar schöne Bitmasken fehlen hier noch, dann ist es super übersichtlich 
und man weiß, was passiert.

CMSIS ist das was du suchst, da sind alle Defines drin.
1
  RCC->CR |= RCC_CR_HSEON;  //Extenen Quarz an
2
  while(!(RCC->CR & RCC_CR_HSERDY));  //Auf Quarz warten
3
  RCC->PLLCFGR &= 0xffff0000;
4
  RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC;  //HSE als Source
5
  RCC->PLLCFGR |= 0x04;      //PLL Pre Divider = 8 -> 2Mhz
6
  RCC->PLLCFGR |= (64UL<<6);    //PLL Multiplikaor = 64
7
  RCC->PLLCFGR |= (0b01<<16);    //PLL Devision Faktor = 2
8
  RCC->CFGR |= (0b100 << 10);
9
  RCC->CR |= RCC_CR_PLLON;    //PLL an
10
  while(!(RCC->CR & RCC_CR_PLLRDY));  //Auf PLL warten
11
  RCC->CFGR |= 0b10;      //PLL auswählen
12
  while((RCC->CFGR & 0x0C) != 0b1000);  //Auf PLL warten
13
  RCC->CR &= ~(RCC_CR_HSION);    //HSI aus

von Nop (Gast)


Lesenswert?

DraconiX schrieb:

> Nun allerdings stellt sich mir die Frage, weil das Thema gerade hier
> aktuell war, das #defines ja sehr "verpönt" sind.

Dabei ging es um etwas Anderes, nämlich um Verhaue aus ifdefs und 
nennenswerte Funktionen, die als Macro statt als Inlinefunktion 
definiert werden.

Ersteres macht den Code unübersichtlich, wenn das endemisch wird, 
weswegen man sowas lieber in Wrapper-Funktionen verpackt, wo die 
Plattform-Abhängigkeiten sich dann konzentrieren.

Zweiteres ist schlecht zu debuggen und neigt zu subtilen Typfehlern, 
weil Macros anders als Templates in C++ reine Text-Ersetzung sind. Für 
Register-Definitionen sind defines gut und richtig, das macht ja ARM in 
den CMSIS-Headern selber auch so.

Wobei ich selber gerne Typendefinitionen in die Defines reinsetze, damit 
später statische Code-Analyser besser arbeiten können. Also in etwa so:
1
    #define RCC_BASE            0x40023800ul
2
3
    #define RCC_CR              (*((volatile uint32_t *)(RCC_BASE + 0x00ul)))
4
    #define RCC_CR_PLLRDY       ((uint32_t)(1ul << 25))
5
    (...)
6
    #define RCC_CSR             (*((volatile uint32_t *)(RCC_BASE + 0x74ul)))
7
    #define RCC_CSR_LPWRRSTF    ((uint32_t)(1ul << 31))

Das sieht nach etwas vielen Klammern aus, aber da es reine Textersetzung 
ist und sowas auch in Ausdrücken vorkommen könnte, bin ich da lieber 
vorsichtig.

Bei reinen read-only-Registern bietet sich natürlich auch noch "const 
volatile" an, damit der Compiler schon meckert, wenn man aus Versehen 
versucht, da reinzuschreiben.

> Gibt es da Alternativen wie man dies anlegen kann?

Du könntest Dir die CMSIS-Header ziehen. Die sind von ARM 
standardisiert, d.h. diese Sachen sollten nicht nur auf STM32 gehen, 
sondern allgemein mit Cortex-M. Das hat den Vorteil, daß man mit diesen 
Benennungs-Konventionen portabel bleibt.

Ansonsten ist es aber ein sehr guter Weg, sich anhand des RefMans 
einzuarbeiten und das von Hand zu realisieren. Du müßtest das RefMan 
ohnehin lesen, weil die ST-HAL entgegen ihrem Namen nichts abstrahiert, 
sondern nur Registerteile in structs verpackt und mit erheblichem 
Overhead versieht. Die Bedeutung dieser Registerteile mußt Du nach wie 
vor dem RefMan entnehmen.

Die Fragen, die bei diesem Weg auftreten, hast Du einmal, aber dann 
verstehst Du den Controller auch besser. Wenn Du ohnehin schon Erfahrung 
in händischer Programmierung beim AVR hast, dann kannst Du das auch mit 
STM32.

Die paar Clocks, die man zusätzlich aktivieren muß, sind jetzt auch 
nicht so wild, wenn man den Clocktree aus dem RefMan mal liest. Man muß 
halt im Hinterkopf behalten, daß aus Stromspargründen per default nichts 
aktiviert ist und man alles aktivieren muß, was man braucht.


RächdSchraib Leera schrieb:

> Das wird es auch. Du wirst wegen des Aufwandes der Neuerfindung
> des Rades im Urschleim deiner Entwicklungen stecken bleiben.

Das ist Unsinn und kommt so pauschal meistens von Leuten, die Angst vor 
dem RefMan haben und vor Registern. Wo es wirklich Aufwand wird, das ist 
der USB-Treiber - aber die ST-Software hat da auch nicht gerade die 
Qualität, daß man das einfach so übernehmen könnte.

von Dr. Sommer (Gast)


Lesenswert?

DraconiX schrieb:
> Klar ist das eine Fleißarbeit,

Auf ARM.com kannst du die für jeden Controller eine SVD Datei 
herunterladen, das ist eine XML Datei mit allen Registern drin. Daraus 
kannst du dir deinen Header automatisch generieren. Ist aber nicht 
nötig, da solche Header selbstverständlich schon existieren, wie soll 
die HAL sonst die Zugriffe machen?

von Nop (Gast)


Lesenswert?

Nils N. schrieb:

>   RCC->PLLCFGR |= 0x04;      //PLL Pre Divider = 8 -> 2Mhz

Es ist schlechter Stil, mit magic numbers herumzumachen und dann im 
Kommentar das zu erklären. Dafür gibt's sprechende Defines.

von ___C___ (Gast)


Lesenswert?

Oder man nimmt enum's. Die haben mit den define's zwar auch das Problem 
des globalen Namensraums gemeinsam, aber was soll's.

von Curby23523 N. (Gast)


Lesenswert?

Nop schrieb:
> Nils N. schrieb:
>
>>   RCC->PLLCFGR |= 0x04;      //PLL Pre Divider = 8 -> 2Mhz
>
> Es ist schlechter Stil, mit magic numbers herumzumachen und dann im
> Kommentar das zu erklären. Dafür gibt's sprechende Defines.

Ich schrieb paar Zeilen drüber, dass da noch ein paar bitmasken 
angebracht werden sollten.

von Dr. Sommer (Gast)


Lesenswert?

PS: was aber mal interessant wäre: Die Register-Adressen im Linker 
Script zuweisen, d.h. in den Header nur so was packen:
extern RCC_TypeDef RCC;
und dann im Linker Script:
RCC=0x40001234;
mit richtiger Adresse ;-)
Die Nutzung in normalem C-Code wäre dadurch unverändert, aber es wäre 
dann bspw. möglich Register Instanzen an C++ Template Parameter zu 
übergeben oder in constexpr zu verwenden, weil der Compiler keinen Cast 
Zahl->Pointer mehr sieht. Die finde ich sowieso enorm hässlich. Debugger 
würden damit eventuell auch besser klarkommen wenn man Register anzeigen 
will o.ä.

von Stefan F. (Gast)


Lesenswert?

> ich mach es lieber "händisch" und beschreibe die Register selber

Willkommen im Club.

Sowohl die HAL als auch die alte SPL Library basieren auf der CMSIS und 
das ist genau das, was du verwenden möchtest - ein großer Haufen 
#defines für die ganzen Register und Bits.

> Die Register der STM32 sind mitnichten komplizierter, als die eines AVR.

Das sehe ich auch, es sind aber mehr.

Beim Seriellen Port muss man zum Beispiel im Vergleich zu AVR zusätzlich 
den Port einschalten, ihn auf "alternative function" konfigurieren, die 
Datenrichtung einstellen, und den Interrupt im Interrupt-Controller 
freigeben.

Und der Zugrif auf die RTC, kommt mir wie "von hinten durch die Brust" 
vor. USB ist richtig schlimm wegen der 32 versus 16 Bit Lücken im 
Speicherzugriff. Aber ich komme mit Hilfe der vorhandenen Anleitungen 
trotzdem mit den Registern klar.

Ich glaube, meine Notizen dazu könnten für Dich hilfreich sein: 
http://stefanfrings.de/stm32/index.html

von do not like shift shit a la avr (Gast)


Lesenswert?

DraconiX schrieb:
> Ich habe mal ein Teil meines RCC Registers angehangen - zugegeben, der
> ARM hat schon ein "paar" Register mehr und somit auch mehr defines, die
> mir später auch zum Problem im Code werden können.

Wozu soll der Krams dienen? Es gibt doch ausreichend header für die 
ARMs.

Und die sind ordentlich angelegt, nicht wie dieser Shift-Scheiss aus der 
AVR Ecke. Sorry, musste sein.

Hier ein Beispiel von den ARM Machern:
http://www.keil.com/dd/docs/arm/st/stm32f10x/stm32f10x.h

Schön alle Flags als Masken, wie es sich gehört.

von W.S. (Gast)


Lesenswert?

DraconiX schrieb:
> Nun gibt es ja für die STM32 die HAL Lib und die StdPeriph Lib. Die
> nehmen einen ja schon ne Menge Arbeit ab, aber ich mach es lieber
> "händisch" und beschreibe die Register selber. Früher(tm) habe für die
> AVR die Definitionsfiles von Atmel genutzt, wo ich direkt die Register
> laut Datenblatt beschreiben konnte - so möchte ich das bei den STM32
> auch gerne haben, also habe ich mich hingesetzt und die Register
> übertragen.

Es gibt von allen möglichen und unmöglichen Quellen Headerfiles für die 
diversen ARM-Controller. In sehr vielen Fällen sind diese Files 
ausgesprochen aufgebläht, ganz oft werden ganze Zweitsätze von Namen 
dabei eingeführt, viele Peripherie-Cores als struct's mit Füll-Bytes 
definiert und massenweise einzelne Bits als Masken und als Shifts 
deklariert.

Da kommt bei manchen Controllern ein Machwerk von mehr als 1 MB heraus, 
und obendrein werden noch irgendwelche weiteren und noch weiteren 
Headerfiles mit reingezogen, die dann natürlich gern 
herstellerspezifisch gestaltet sind - damit bloß niemand auf die Idee 
kommt, nicht den ganzen Schwall nehmen zu wollen.

Ich bin bei all den dargebotenen Headerfiles skeptisch.

Wenn sowas einigermaßen leserlich ist und keinen zusätzlichen Firlefanz 
wie Masken und Verschiebekonstanten und zusätzliche Namen und weitere 
Header einführt, dann kann man sowas nehmen.

Aber in allen anderen Fällen mache ich mir meine Headerdateien nach 
Referenzmanual selber. Das ist am Anfang ein bissel mühsam, aber man 
tingelt dabei ja durch das Refmanual und lernt so nebenbei, wie im 
großen und ganzen der Chip aussieht, den man benutzen will. Ich kann dir 
zu diesem Verfahren nur zuraten.

Das oben genannte Beispiel zum STM32F10x ist m.E. ein glattes 
Negativ-beispiel. Es ist ca. 630 K groß, man sucht sich dort drin 
halbtot, wenn man etwas sucht und es ist auch durch die große Anzahl von 
zusätzlichem Zeug fehleranfällig. Ich erklär's dir:
1
#define  RCC_CIR_HSIRDYIE                    ((uint32_t)0x00000400)        /*!< HSI Ready Interrupt Enable */
So. das ist kein Register, sondern ein Bit in einem Register, genauer 
"Bit definition for RCC_CIR register". Und jetzt frag dich mal, wie man 
sowas benutzen soll, es ist in Wirklichkeit ja nichts anderes als die 
Zahl 0x400 - und die hat gar keinen Bezug auf das Register RCC_CIR oder 
je nach Deklaration auch RCC->CIR.

Klar, sowas ist nur zusammen mit dem richtigen Register zu benutzen, 
aber das muß man eben immer im Hinterkopf haben, sonst passiert es einem 
nämlich ganz leicht, daß man es auch mal versehentlich woanders benutzt, 
was ggf. zu einem schwer auffindbaren Bug führt. Das Arbeiten mit 
Kombinationen aus Registerbezeichnung plus Bitbezeichnung ist letzten 
Endes immer mit der Gefahr von Kombinationsfehlern aus beiden verbunden.

W.S.

von Nop (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:

> Und die sind ordentlich angelegt, nicht wie dieser Shift-Scheiss aus der
> AVR Ecke. Sorry, musste sein.

Ich ziehe die Bitmaskendefinition mit Shifts vor, weil im Refman die 
Bits nämlich nach Bitposition gelistet sind. Dadurch kann man einfacher 
abgleichen, ob eine Bitmaske wirklich zum verwendeten Register-Layout 
paßt.

Bei (1UL << 18) weiß ich gleich, es ist Bit 18. Bei (0x00040000UL) weiß 
ich das nicht auf Anhieb.

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Klar, sowas ist nur zusammen mit dem richtigen Register zu benutzen

Eben, und ein Idiom wie
RCC_REG |= CFSR_BIT_FOO;
sieht ja so offensichtlich falsch aus, daß man das kaum verwechseln 
wird.

von GCC (Gast)


Lesenswert?

Dr. Sommer schrieb:
> PS: was aber mal interessant wäre: Die Register-Adressen im Linker
> Script zuweisen, d.h. in den Header nur so was packen:
> extern RCC_TypeDef RCC;
> und dann im Linker Script:
> RCC=0x40001234;
> mit richtiger Adresse ;-)
> Die Nutzung in normalem C-Code wäre dadurch unverändert, aber es wäre
> dann bspw. möglich Register Instanzen an C++ Template Parameter zu
> übergeben oder in constexpr zu verwenden, weil der Compiler keinen Cast
> Zahl->Pointer mehr sieht. Die finde ich sowieso enorm hässlich. Debugger
> würden damit eventuell auch besser klarkommen wenn man Register anzeigen
> will o.ä.
Das funktioniert und sorgt bei mir dafür, daß der Compiler die 
BitBanding-Bereiche als uint32_t Array ansieht und so weniger häufig 
Konstanten aus dem .text-Segment nachläd, obwohl weil das nächste 
bearbeitete Bit nur +-4k-Bit daneben liegt. Oder generell immer weiß, 
wie groß der Offset zum nächsten Bit ist.
 Aber stimmt, das könnte man auch für weniger große Arrays tun. (und als 
template natürlich ;-)

von do not like shift shit a la avr (Gast)


Lesenswert?

W.S. schrieb:
> es ist in Wirklichkeit ja nichts anderes als die
> Zahl 0x400 - und die hat gar keinen Bezug auf das Register RCC_CIR

???
Die Zahl stellt eine Bitmaske dar. Und durch das define bekommt sie 
einen aussagekräftigen Namen. Natürlich hat jeder eine andere 
Vorstellung von sinnvoller Namensgebung.

von do not like shift shit a la avr (Gast)


Lesenswert?

Nop schrRCRCC_CIR_HSIRDYIESIRDYIE_HSIRDYIE_HSIRDYIEBeitrag #5295648:
> Bei (1UL << 18) weiß ich gleich, es ist Bit 18. Bei (0x00040000UL) weiß
> ich das nicht auf Anhieb.

Genau wie heute uint32_t anstatt uns... long genutzt wird, definest du 
einmalig selbstsprechende Bezeichner
#define BIT_2   0x04;   // oder (1<<2)

Und dann nur noch vernünftige, balastlose Bezeichner.

#define   UART_IE    BIT_2;

Alles easy ohne Shift Scheiss.

von Nop (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:

> #define BIT_2   0x04;   // oder (1<<2)

Das soll übersichtlicher sein? Naja, vielleicht für Leute, die noch nie 
einen Shift-Operator gesehen haben, aber das sind dann eh nicht die, 
welche hardwarenahe Programmierung machen.

von Cpp (Gast)


Lesenswert?

Nop schrieb:
> do not like shift shit a la avr schrieb:
>
>> #define BIT_2   0x04;   // oder (1<<2)
>
> Das soll übersichtlicher sein? Naja, vielleicht für Leute, die noch nie
> einen Shift-Operator gesehen haben, aber das sind dann eh nicht die,
> welche hardwarenahe Programmierung machen.
Aber es muß nicht jede Profession, nur weil sie auch Laien kennt, auf 
alles über deren Niveau verzichten, oder?

von do not like shift shit a la avr (Gast)


Lesenswert?

Nop schrieb:
> Das soll übersichtlicher sein?

Must du richtig lesen!

Shift Scheiss
#define   UART_IE    (1 << 2);

gegen besser lesbar
#define   UART_IE    BIT_2;

jmtc

von Nop (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:

> Must du richtig lesen!

Ja sicher, genau so habe ich das auch aufgefaßt. Ich find's aber nicht 
übersichtlicher, im Gegenteil.

Noch schlimmer ist das aus den ARM-Headern, wo die Zahlen als Hexmaske 
reingeklatscht sind und dann rechts daneben im Kommentar erklärt ist, 
welches Bit das ist.

von do not like shift shit a la avr (Gast)


Lesenswert?

Nop schrieb:
> genau so habe ich das auch aufgefaßt

Und warum zitierst du dann falsch? Wollst prollen?

Nop schrieb:
> Ich find's aber nicht
> übersichtlicher, im Gegenteil.

(1 << 2) ist übersichtlicher als BIT_2 ??? Na denn ...

Nop schrieb:
> Noch schlimmer ist das aus den ARM-Headern, wo die Zahlen als Hexmaske
> reingeklatscht sind

Du nutzt doch nur den Alias. Wie der Wert dahinter formatiert wird, ist 
doch völlig egal. Der kann von mir aus auch geshiftet werden, solange 
die Scheisse nicht in meinem selbst geschriebenen code zu sehen ist.

Shiften nutze ich natürlich auch, aber nicht um konstante Bitmasken zu 
erzeugen. Schreib mal 3 bis 4 Bitmasken in eine Zeile. Da kommt es einem 
hoch.

von Nop (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:

> Und warum zitierst du dann falsch? Wollst prollen?

Ich zitiere nicht falsch, sondern höchstens sinngemäß. :-)

> (1 << 2) ist übersichtlicher als BIT_2 ??? Na denn ...

Jepp, denn wenn ich Zahlen erwarte, will ich mich nicht durch Buchstaben 
wühlen müssen. Aus ähnlichen Gründen finde ich auch die begin/end-Blöcke 
bei Pascal unübersichtlicher als geschweifte Klammern in C.

> Du nutzt doch nur den Alias.

Äh?! Ja was denn sonst?!

> Der kann von mir aus auch geshiftet werden, solange
> die Scheisse nicht in meinem selbst geschriebenen code zu sehen ist.

OK, dann haben wir aneinander vorbeigeredet. Ich nutze das Geshifte in 
den Defines für die Bitmasken, welche in einem Header vergraben liegen - 
im Code taucht dann nur noch der Alias auf.

Das Geshifte wird in den Headern natürlich noch anständig formatiert, 
damit man dieselben Zeilenformate erhält, egal ob die 1 in einer Maske 
nun um ein Bit geschoben wird oder in der folgenden Zeile um 10. Ich mag 
es nicht, wenn das von Zeile zu Zeile je nach Shiftweite springt.

von Nop (Gast)


Lesenswert?

Ach ja, und für das Schalten irgendwelcher IOs nehme ich weder "BIT_2" 
noch "1 << 2", sondern ein zentrales Define, was den Sinn wiedergibt. 
Was weiß ich, "MOTOR_CTRL" oder so. Mich interessiert beim Lesen des 
Codes ja nicht, auf welchem Pin der liegt, das kommt in einen Header, 
sondern was der Code inhaltlich tut.

von do not like shift shit a la avr (Gast)


Lesenswert?

Nop schrieb:
> Ach ja, und für das Schalten

So, damit es für dich transparenter wird:
Beitrag "Timer interrupt atmega 2560"

Beispiel schlecht, ganz schlecht
  TCCR1B |= (1<< CS10)|(1<< CS12); // 16Mhz/1024 prescaler

Mit nem richtigen header - also so wie es alle machen ausser atmel - 
sehe es so aus
  TCCR1B |= CS10 | CS12; // 16Mhz/1024 prescaler

Und jetzt möge man mir genau an der Zeile - nicht an irgendwelchen 
headern - den Vorteil der Shifterei aufzeigen.

von Es gibt ein Leben ohne defines (Gast)


Angehängte Dateien:

Lesenswert?

DraconiX schrieb:

> Ich habe mal ein Teil meines RCC Registers angehangen - zugegeben, der
> ARM hat schon ein "paar" Register mehr und somit auch mehr defines, die
> mir später auch zum Problem im Code werden können. Gibt es da
> Alternativen wie man dies anlegen kann?

Gibt es auf jeden Fall, siehe Anhang. In meinen STM32-Header Files gibt 
es kein einziges #define. In den letzten 20000 Zeilen C gibt es immerhin 
noch sieben #defines zwecks Initialisierung von Tabellen. Meine Header 
verwenden überwiegend Bitfelder, selten eine union aus Bitfeld-struct 
und uint32_t und in rcc.h sogar Bitnummern (mit shift!) für z.B. die 
Clock Enable- und Reset-Bits.
Die LED-Helligkeit steuert man damit z.B. so:
1
// led.c
2
#include "syslib.h"
3
#include "dac.h"
4
#include "gpio.h"
5
#include "rcc.h"
6
7
void
8
led (int brightness)
9
{
10
   if (brightness < 0) {
11
      GPIOA->MODER4 = MODE_OUTPUT;
12
      DAC->CR_EN1 = 0;
13
      return;
14
   }
15
16
   if (brightness > 100) {
17
      brightness = 100;
18
   }
19
   RCC->APB1ENR |= 1 << APB1_DAC;
20
   GPIOA->MODER4 = MODE_ANALOG;
21
   DAC->CR_EN1 = 1;
22
   DAC->DHR12R1 = 1024 + brightness * 28;
23
   return;
24
}

von Dr. Sommer (Gast)


Lesenswert?

Es gibt ein Leben ohne defines schrieb:
> Meine Header verwenden überwiegend Bitfelder

So kannst du leider keine 2 Felder in einem Register gleichzeitig 
beschreiben, und alle Schreib-Zugriffe werden einzeln umgesetzt, was 
etwas ineffizient ist. Außerdem ist das Verhalten von Bitfeldern 
generell Implementation defined.
Es ist aber eine gute Idee die elenden Makros loszuwerden und durch 
richtige Konstanten zu ersetzen.

von Dr. Sommer (Gast)


Lesenswert?

APB1_rsvd_c,
APB1_rsvd_d

So etwas kannst du übrigens vermeiden indem man dem enum Einträgen 
explizite Werte vorgibt

APB1_WWDG = 11,
APB1_SPI2 = 14, ...

von Nop (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:

> Beispiel schlecht, ganz schlecht
>   TCCR1B |= (1<< CS10)|(1<< CS12); // 16Mhz/1024 prescaler

Örks.. ja, da stimme ich Dir zu.

> Mit nem richtigen header - also so wie es alle machen ausser atmel -
> sehe es so aus
>   TCCR1B |= CS10 | CS12; // 16Mhz/1024 prescaler

So mache ich das auch. Das Geshifte habe ich in den Defines, und nur da 
finde ich das auch übersichtlicher. Nicht im Code.

von W.S. (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:
> Must du richtig lesen!

Wenn du so weiter schreibst, dann geb ich dir als Strafe mal auf, den 
generischen USB-Treiber von Nuvoton lesen UND VERSTEHEN zu sollen.

Ich nagle aber sicherheitshalber zuvor das Fenster zu, denn aus eigener 
Erfahrung weiß ich, daß man bei dem, was du hier postulierst und was die 
von Nuvoton eben genau so zelebriert haben, schon nach kurzer Zeit am 
liebsten laut schreiend sich aus dem Fenster stürzt.

W.S.

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Eben, und ein Idiom wie
> RCC_REG |= CFSR_BIT_FOO;
> sieht ja so offensichtlich falsch aus, daß man das kaum verwechseln
> wird.

Wenn die Welt derart einfach wäre, dann wären die meisten, die hier 
posten, schon längst arbeitslos. OK, Briefmarken sammeln und Kakteen 
züchten kann auch befriedigend sein.

W.S.

von do not like shift shit a la avr (Gast)


Lesenswert?

Nop schrieb:
> So mache ich das auch.

Na siehste, geht doch. Jetzt wieder zurück zum Thema:
Atmels header definieren keine Bitmasekn, sondern nur die Bitnummern. 
Also Shift-Scheiss im eigenen code verwenden (wollen wir ja nicht) oder 
den header noch einmal abtippen und die Masken erzeugen. So wie der arme 
Tropf hier, der Themeneröffner. Danke Atmel!

Zum Glück haben alle anderen Hersteller (auch ST für die STM32) 
Bitmasken in ihren headern und wir können uns auf die echten Aufgaben 
stürzen.

Daher gilt Beitrag "Re: (STM32) Register Lib mit define oder nicht define.."

W.S. schrieb:
> Wenn du so weiter schreibst, dann geb ich dir als Strafe mal auf

Ob ich jetzt noch ruhig schlafen kann?

von Stefan F. (Gast)


Lesenswert?

Atmel/Microchip hat das _BV() Makro vorgesehen, um eine Bitnummer in 
eine Bitmaske umzuwandeln.

von do not like shift shit a la avr (Gast)


Lesenswert?

Stefan U. schrieb:
> _BV() Makro

  TCCR1B |= _BV(CS10) | _BV(CS12); // 16Mhz/1024 prescaler

sieht auch scheisse aus, über 50% unnötige Zeichen, sorry.

von Rene K. (xdraconix)


Lesenswert?

Also ich nutze auch lieber:
1
#define RCC_CR_PLLON (1<<18)
2
3
rp->cr |= RCC_CR_PLLON;


statt eines Hex-Maske. Warum? Ganz einfach: es ist bedeutend besser zu 
lesen, ich weiß sofort auf welcher Bitposition dieses im Register liegt.

Gut, das nun Atmel mit seinen Bitdefinitionen nicht unbedingt ins 
Schwarze getroffen hatte, ist natürlich korrekt. Spielt doch aber hier 
keine Rolle.

Und warum sollte man denn sein Code extra noch mit einem separaten 
#define BIT_1 •• 32 aufblähen? Wenn eine Schiebeoperation den gleichen 
optischen Effekt erzielt?

Und dem Compiler ist das im Ende eh egal.. der macht zum Schluss sowieso 
das gleiche daraus.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Irgendwie kann ich mich erinnern, dass ich mal die wirkliche Bit-Nummer 
gebraucht habe, also zB die '18' im obigen Beispiel.

wenn meine Definition schon (1<<18) lautet, ist es nicht trivial wieder 
auf die 18 zurückzurechnen.

ich kann mich aber an den konkreten Fall nicht erinnern...

edit: ich weiss es wieder: ich mache in meinem Code gerne kenntlich, 
dass ich ein Bit nicht setze:
1
TCCR1C = (0 << FOC1A) | (0 << FOC1B); // normal operation
oder
1
TCCR1B =  (0 << CS12) | (0 << CS11) | (1 << CS10); // No Prescaler

vor allem wenn ich schnell mal ein paar Kombinationen durchprobieren 
will, ist das Umschreiben 0 <=> 1 (zumindest für mich) übersichtlicher.

YMMV

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Michael R. schrieb:

> edit: ich weiss es wieder: ich mache in meinem Code gerne kenntlich,
> dass ich ein Bit nicht setze:

Dafür definiert man sich eine entsprechende Bitmaske, die den Mosus 
beschreibt, in dem Fall z.B. Prescaling, und die einfach 0 enthält. Etwa 
so:

#define BLA_PRESCALING_NONE 0

Das ist noch lesbarer.

von Stefan F. (Gast)


Lesenswert?

>>  TCCR1B |= _BV(CS10) | _BV(CS12); // 16Mhz/1024 prescaler
> sieht auch scheisse aus

Ja, allerdings ist dann klar, dass hier Bit-Masken verwendet werden. 
Wobei sich das auch aus dem Kontext ergibt, wenn man sich nicht doof 
stellt.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Nop schrieb:
> #define BLA_PRESCALING_NONE 0

und diesen Define findest du dann auch im Datenblatt?

ich persönlich bevorzuge, wenn aus Code und Datenblatt klar hervorgeht, 
was passiert.

von Dr. Sommer (Gast)


Lesenswert?

Hat eigentlich noch keiner erwähnt dass die Header, welche zu 
STM32CubeFx gehören und für Nutzung mit der HAL sind, aber auch 
problemlos "blank" verwendet werden können, da fast alle Wünsche 
erfüllen?
1
#define RCC_CFGR_HPRE_Pos    (4U)                        
2
#define RCC_CFGR_HPRE_Msk    (0xFU << RCC_CFGR_HPRE_Pos) /*!< 0x000000F0 */
3
#define RCC_CFGR_HPRE        RCC_CFGR_HPRE_Msk           /*!< HPRE[3:0] bits (AHB prescaler) */
4
#define RCC_CFGR_HPRE_0      (0x1U << RCC_CFGR_HPRE_Pos) /*!< 0x00000010 */
5
#define RCC_CFGR_HPRE_1      (0x2U << RCC_CFGR_HPRE_Pos) /*!< 0x00000020 */
6
#define RCC_CFGR_HPRE_2      (0x4U << RCC_CFGR_HPRE_Pos) /*!< 0x00000040 */
7
#define RCC_CFGR_HPRE_3      (0x8U << RCC_CFGR_HPRE_Pos) /*!< 0x00000080 */
8
9
#define RCC_CFGR_HPRE_DIV1   0x00000000U                 /*!< SYSCLK not divided */
10
#define RCC_CFGR_HPRE_DIV2   0x00000080U                 /*!< SYSCLK divided by 2 */
11
#define RCC_CFGR_HPRE_DIV4   0x00000090U                 /*!< SYSCLK divided by 4 */
12
...
13
#define RCC_CFGR_HPRE_DIV512 0x000000F0U                 /*!< SYSCLK divided by 512 */
Sieht zwar nicht besonders schön aus, aber in der Datei sucht man ja 
normalerweise auch nicht. Damit kann man dann den gewünschten Wert auf 
verschiedene Art und Weise setzen:
1
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_HPRE_2 | RCC_CFGR_HPRE_3)) | RCC_CFGR_HPRE_0 | RCC_CFGR_HPRE_1;
2
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | (3 << RCC_CFGR_HPRE_Pos);
3
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV4;
4
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE_Msk, RCC_CFGR_HPRE_DIV4);

von Nop (Gast)


Lesenswert?

Michael R. schrieb:

> und diesen Define findest du dann auch im Datenblatt?

Ich mache meine Delfine so, daß sie den Sinn dessen wiedergeben, was ich 
da tue. Im Datenblatt findet man das dann in der Beschreibung der 
jeweiligen Bits wieder. Das wird insbesondere lesbarer, wenn sich das 
über mehrere Bits erstreckt.

Dadurch stelle ich außerdem noch sicher, daß ich an JEDER Stelle die 
richtige Bitkombination einsetze.

Beispiel, im Datenblatt kennt Cortex-M beim RCC-CFGR die untersten 
beiden Bits als "SW" für "switch". Meine Benennung ist dann z.B. 
RCC_CFGR_SW_HSI und RCC_CFGR_SW_HSE, mit dem Wert 0 bzw. 1. Für die 
Werte 2 und 3 entsprechend.

von Vincent H. (vinci)


Lesenswert?

Nop schrieb:
> Michael R. schrieb:
>
>> und diesen Define findest du dann auch im Datenblatt?
>
> Ich mache meine Delfine so, daß sie den Sinn dessen wiedergeben, was ich
> da tue. Im Datenblatt findet man das dann in der Beschreibung der
> jeweiligen Bits wieder. Das wird insbesondere lesbarer, wenn sich das
> über mehrere Bits erstreckt.
>
> Dadurch stelle ich außerdem noch sicher, daß ich an JEDER Stelle die
> richtige Bitkombination einsetze.
>
> Beispiel, im Datenblatt kennt Cortex-M beim RCC-CFGR die untersten
> beiden Bits als "SW" für "switch". Meine Benennung ist dann z.B.
> RCC_CFGR_SW_HSI und RCC_CFGR_SW_HSE, mit dem Wert 0 bzw. 1. Für die
> Werte 2 und 3 entsprechend.


Komisch das "deine" Benennung mit der der ST Header übereinstimmt.
1
#define RCC_CFGR_SW_HSI                    0x00000000U                         /*!< HSI selected as system clock */
2
#define RCC_CFGR_SW_HSE                    0x00000001U                         /*!< HSE selected as system clock */
3
#define RCC_CFGR_SW_PLL                    0x00000002U                         /*!< PLL selected as system clock */

von Nop (Gast)


Lesenswert?

Vincent H. schrieb:

> Komisch das "deine" Benennung mit der der ST Header übereinstimmt.

Das ist überhaupt nicht komisch, weil das absolut logisch ist.

von W.S. (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:
> Ob ich jetzt noch ruhig schlafen kann?

Das hängt von dir ab - jedenfalls solange du dort noch nicht 
reingeschaut hast.


do not like shift shit a la avr schrieb:
> Also Shift-Scheiss im eigenen code verwenden (wollen wir ja nicht) oder
> den header noch einmal abtippen und die Masken erzeugen.

Nö. Damit liegst du grundfalsch. Es ist hier in diesem Forum immer 
wieder zu lesen, daß die Möchtegern-Programmierer kreuz und quer in 
allen ihren Quellen nicht müde werden, direkte HW-Zugriffe 
hinzuschreiben. Am liebsten sogar in main().

Der weitaus bessere Weg ist, die HW-Zugriffe nur in den zugehörigen 
Lowlevel-Treibern zu tun. Genau da gehören sie nämlich hin. Und dann 
relativiert sich das ganze Getue um Shiftmasken und dergleichen auf 
Null. Wer einen sauberen LL-Treiber hat, braucht sich im Rest seiner 
Firmware eben nicht mehr um derartige Dinge zu kümmern. Und innerhalb 
des LL-Treibers ist es völlig in Ordnung mit sowas zu arbeiten (ist ein 
Beispiel zur Veranschaulichung):
1
  // erstmal die benötigten Takte einschalten
2
  SYSAHBCLKCTRL = 0x3F    |    // Basic (Resetwert) SYS, ROM, RAM0,
3
                               // FLASHREG, FLASHARRAY, I2C
4
                  (1<<6)  |    // GPIO
5
      (1<<9)  |    // CT32B0
6
      (0<<10) |    // CT32B1
7
      (1<<12) |    // USART
8
      (1<<16) |    // IOCON
9
      (0<<19) |    // PINT (Pin Interrupts)
10
      0;

Das ist nämlich die übersichtlichste und fehlersicherste Schreibweise, 
denn sie verzichtet auf zusätzliche Bezeichner, die irgendwo stehen und 
die gelegentlich auch mal nicht ganz richtig sind. Wenn später irgendwas 
nicht klappt, wird man ohnehin an die zuständige Stelle im RefMan 
schauen müssen - und dort stehen immer die betreffenden Bitpositionen 
und keine Masken etc.

Aber so eine übersichtliche Schreibweise setzt ein wenig innere 
Gründlichkeit voraus - die hier offensichtlich einige nicht haben.

W.S.

von Nico W. (nico_w)


Angehängte Dateien:

Lesenswert?

W.S. schrieb:
> Wenn später irgendwas
> nicht klappt, wird man ohnehin an die zuständige Stelle im RefMan
> schauen müssen - und dort stehen immer die betreffenden Bitpositionen
> und keine Masken etc.

Ja und nein.
ST hat das schon recht ordentlich gemacht.

Mich stören nur die Casts. Daher hatte ich mir mal nen Python-Script 
gebastelt um aus den (uint32_t) xxx nen xxxUL zu machen. Dann kann man 
damit auch schöne Sachen mit dem Preprocessor machen. Afaik hatte ich 
damit zumindest damals mal Probleme.

Vincent H. schrieb:
> Komisch das "deine" Benennung mit der der ST Header übereinstimmt.

Man gucke einfach mal ins RefMan. Man sucht dann einfach nach RCC_CFGR. 
Nach ein paar hits ist man direkt bei den Registern. Sucht man jetzt 
noch nach SW und HSI. Zack ist man da. RCC_CFGR_SW_HSI (Siehe Anhang). 
Also viel Zufall ist das nicht.

von do not like shift shit a la avr (Gast)


Lesenswert?

Rene K. schrieb:
> Also ich nutze auch lieber:
> #define RCC_CR_PLLON (1<<18)
>
> rp->cr |= RCC_CR_PLLON;

Und schreibst den vollständigen header neu? Die Zeit hätte ich auch 
gerne. :(((

Dr. Sommer schrieb:
> auch
> problemlos "blank"

Das sind auch Bitmasken und keine Bitnummern. Also das Gleiche wie CMSIS 
und nix neues hier im Thread. =>;>>>

W.S. schrieb:
> HW-Zugriffe nur in den zugehörigen
> Lowlevel-Treibern zu tun

Den zähle ich zu meinem Code. Und da will ich die Shift-Scheisse nicht 
sehen. :(((

W.S. schrieb:
> Das ist nämlich die übersichtlichste und fehlersicherste Schreibweise,
> denn sie verzichtet auf zusätzliche Bezeichner

Völliger Schwachsinn! Wie kann es denn übersichtlich sein, wenn die 
'Bezeichner' 1, 2, ... heissen und die Beduetung als Kommentar ergänzt 
wird? Oh man, das ist ja noch schlimmer als der Atmel Krams. :(((

von Stefan F. (Gast)


Lesenswert?

Ihr müsst aber schon zugeben, dass es wichtigere Dinge bei der Arbeit 
eines Programmierers gibt, oder?

von Dr. Sommer (Gast)


Lesenswert?

do not like shift shit a la avr schrieb:
> Das sind auch Bitmasken und keine Bitnummern.

Und was ist dann das da:

Dr. Sommer schrieb:
> #define RCC_CFGR_HPRE_Pos    (4U)

von Rene K. (xdraconix)


Lesenswert?

do not like shift shit a la avr schrieb:
> Und schreibst den vollständigen header neu? Die Zeit hätte ich auch
> gerne. :(((

Nein, ich schreibe mir meine Header so wie ich sie benötige, mitunter 
Projektübergreifend, aber meist speziell für das Projekt.

Wie oben schon gesagt wurde, brauche ich auch keinen Rattenschwanz an 
Definitionen die Unübersichtlich und für mich sinnlos sind. Das fängt 
schon mit CMSIS an, die ist gut, ohne Frage - jedoch brauche ich (gerade 
mal den F103 im Auge) keine Header wo die gesamte Baureihe integriert 
ist (Connectevity Line, Mainstream-Line, etc...) Das brauche ich halt 
nunmal nicht und macht die gesamte Sache wieder völlig unübersichtlich.

Die Sache von Keil oben... Das ist ja sowieso der Knaller. Und gerade 
das ist der Grund warum ich es lieber selbst mache.

Und p.s.: Ja die Zeit nehme ich mir dafür, sogar sehr sehr gerne!

von do not like shift shit a la avr (Gast)


Lesenswert?

Stefan U. schrieb:
> Ihr müsst aber schon zugeben, dass es wichtigere Dinge bei der
> Arbeit eines Programmierers gibt, oder?

Als Header abtippen? Ja, sehe ich so.

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
Noch kein Account? Hier anmelden.