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?
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.
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.
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.
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.
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:
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.
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?
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.
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.
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.ä.
> 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
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.
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:
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.
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.
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.
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 ;-)
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.
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.
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.
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?
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
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.
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.
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.
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.
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.
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:
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.
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, ...
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.
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.
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.
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?
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.
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
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.
>> 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.
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.
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?
#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:
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.
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 */
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):
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.
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.
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. :(((
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!
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.