Hallo
ich befasse mich mit den Timer-Interupts des Arduino. Soweit alles
schlüssig und funktioniert im Testlauf erstaunlicherweise auch sofort.
Abgespickt habe ich auf dieser Seite:
https://arduino-projekte.webnode.at/meine-projekte/servosteuerung/servotest/variante3/
Dort kommen mehrere gleichartige Zuweisungen vor, die ich zwar
syntaktisch verstehe, aber beim WARUM klemmt mein Kopf. Wenn mit 'CS22'
ODER-verknüpft wird, wieso muss dann CS22 vorher um 1 Bit nach links
geschoben werden? Das ist bei fast allen Flag-Operationen auf der Seite
so.
Beispiel:
1
// Setze CS20, CS21 und CS22 - Clock Select Bit 10,11,12
Ich gehe davon aus, dass CS20, CS21 und CS22 einzelne Bits im
repräsentierenden Register repräsentieren. Und wieso muss man die erst
schieben, bevor man sie verwendet? Über kurze Erklärung würde ich mich
wirklich freuen.
Roth schrieb:> wieso muss dann CS22 vorher um 1 Bit nach links geschoben werden?
Das geschieht hier nicht.
x << y
schiebt x um y Bits nach links, nicht umgekehrt.
1 << CS22
Hier wird nicht CS22 geschoben, sondern die "1" wird um CS22 nach links
geschoben.
Beispiel: CSS22 ist 3, weil es das 3. Bit ist.
Dann steht da:
Schiebe die "1" um 3 nach links, es kommt also b000001000 raus
CS* sind nur die Bitpositionen der entsprechenden Bits, zB. 5. Um das
Bitmuster zu erzeugen, muss man eine 1 an die passende Stelle im Byte
bekommen. Und dazu schiebt man eine 1 einfach x-mal nach links.
Es gibt andere Hersteller, da sind diese Definitionen schon "richtig"
(1, 2, 4, 8, 16, ...) und man kann sie ohne << zusammen-odern. Ist halt
Geschmackssache, hat alles Vor- und Nachteile.
@ Roth (Gast)
>syntaktisch verstehe, aber beim WARUM klemmt mein Kopf. Wenn mit 'CS22'>ODER-verknüpft wird,
Die BitMASKE wird oder verknüpft. CS22 ist aber bei den Includes des avr
gcc eine BitNUMMER.
> wieso muss dann CS22 vorher um 1 Bit nach links
Falsch. 1 wird um CS22 nach links geschoben, um aus der BitNUMMER die
BitMASKE zu machen.
Siehe Bitmanipulation.
Roth schrieb:> Ich gehe davon aus, dass CS20, CS21 und CS22 einzelne Bits im> repräsentierenden Register repräsentieren.
CS20, CS21 und CS22 sind Zahlen, die angeben, an welcher Bit-Position im
TCCR2B-Register des Atmel Prozessors die Bit für CS20, CS21 bzw. CS22
liegen. Mit Arduino hat das nichts zu tun.
In einem h-file (Prozessordefinition) wird CS22 definiert als die
Bitposition, z.B.
#define CS22 2
Da die Bits nebeneinander liegen, kann man auch schreiben:
TCCR2B |= 7 << CS20;
eProfi schrieb:> Da die Bits nebeneinander liegen, kann man auch schreiben:>> TCCR2B |= 7 << CS20;
Dann wäre aber die Bezeichnung CS20 eher irreführen und man würde sich
sinnvollerweise ein CS2 definieren, falls das im Headerfile für den
Prozessor nicht ohnehin schon geeignet festgelegt wurde.
Roth schrieb:> Und wieso muss man die erst> schieben, bevor man sie verwendet?
Entgegen aller anderen Hersteller hat Atmel in seinen header nicht über
Masken sondern über Nummern codiert.
normal:
#define BIT_0 0x0000
#define BIT_1 0x0001
...
Atmel:
#define BIT_0 (1<<0)
#define BIT_1 (1<<1)
...
Das die Atmel-Variante total dämlich ist, haben wir hier schon öfter
diskutiert.
so, so schrieb:> Das die Atmel-Variante total dämlich ist,> haben wir hier schon öfter diskutiert.
Allerdings hatte Atmel damals einen Grund dafür, insofern ist es nicht
total dämlich.
S. R. schrieb:> so, so schrieb:>> Das die Atmel-Variante total dämlich ist,>> haben wir hier schon öfter diskutiert.>> Allerdings hatte Atmel damals einen Grund dafür, insofern ist es nicht> total dämlich.
Für jemanden, der die Notation nicht kapiert, muß es natürlich dämlich
aussehen :-)
eProfi schrieb:> In einem h-file (Prozessordefinition) wird CS22 definiert als die> Bitposition, z.B.>> #define CS22 2>> Da die Bits nebeneinander liegen, kann man auch schreiben:>> TCCR2B |= 7 << CS20;
Kann man machen, es verwirrt aber, da es nicht mehr so offensichtlich
ist, welche und wie viele Bits nun gesetzt werden. Gegen die Langform
spricht doch eigentlich nichts außer man ist Tippfaul :) (was einem aber
früher oder später mal auf die Füße fallen kann). Der Compiler wird die
Anweisung, da ja alles Konstanten und Makros, zur Kompilierzeit ohnehin
vereinfachen und optimieren. Wichtig finde ich, das der Code für
Menschen gut lesbar und auf den ersten Blick klar verständlich ist. Und
"7 << CS20" sieht einfach kurios aus wenn man mit Registern auf Bitebene
arbeitet.
FS schrieb:> Wichtig finde ich, das der Code für Menschen gut lesbar und auf den> ersten Blick klar verständlich ist. Und "7 << CS20" sieht einfach> kurios aus wenn man mit Registern auf Bitebene arbeitet.
Wenn es darum geht, würde man wohl eher "clkT2_1024" schreiben und
irgendwo mal clkT2_1024 als "7 << CS2" definieren ;-)
so, so schrieb:> Das die Atmel-Variante total dämlich ist, haben wir hier schon öfter> diskutiert.
Ach was, beide Varianten sind gleichermaßen dämlich. Immer wird
behauptet, daß sich dadurch ein C-Quelltext besser lesen lassen würde,
was in der Praxis schlichtweg nicht stimmt. Siehe hier. Der TO hat sich
nämlich eben durch diese Möchtegern-Bezeichner verwirren und zu falschen
Schlüssen leiten lassen.
Und jetzt kommen die Möchtegern-Fachleute und finden eine Notation
"total dämlich", bloß weil sie nicht ihren jeweiligen Vorstellungen
entspricht.
Mein Rat:
Blick ins Referenzmanual werfen und allen dicken Header-Dateien
grundsätzlich mißtrauen, solange man nicht nachgesehen hat, was welche
Makro-Definition tatsächlich bewirkt.
Noch ein Rat:
Alle die so tollen Bit-Definitionen lieber bleiben lassen und mit den
Zahlen arbeiten, die im RefManual drinstehen - und die Kommentare dazu
nicht vergessen.
Also sinngemäß etwa so:
1
TCCXYZ=(0<<5)|// Modus 0..3
2
(1<<4)|// Dir: 0=vor,1=rück
3
(1<<3)|// Sel: 0=ena,1=dis
4
(5<<0);// subadr 0..7
Sowas ist problemlos lesbar und man braucht auch nicht ne Headerdatei
zum Verständnis zu lesen, sondern nur das Refmanual. Und SO UNSÄGLICH
VIEL zu schreiben ist es auch nicht.
W.S.
W.S. schrieb:> Noch ein Rat:
Von diesem Rat rate ich ab.
Das nur wenig besser als eine ausgerechnete Konstante als "magic number"
auszurechnen und hinzuschreiben, auch wenn der Kommentar versucht, den
Anschein zu erwecken, zu dokumentieren, was dort geschieht.
Naja...
Welche Form man verwendet, ist in der Sache egal.
Wenns funktioniert ist erstmal ok.
Und schöner gehts immmer.
(bzw. liegt im Auge des Betrachters)
Ich persönlich neige eher zum _BV() Makro.
DDRB |= _BV(PB5) | _BV(PB4);
S. R. schrieb:> Allerdings hatte Atmel damals einen Grund dafür, insofern ist es nicht> total dämlich.
Was war denn damals der Grund?
Also was sprach gegen Sprechende Bitstrukturen, ggf. Unions, wo ich dann
z.B. schreibe:
Arduino Fanboy D. schrieb:> Wenns funktioniert ist erstmal ok.
Fürs erste Testprogramm ja. Wenn das Programm aber länger als 1h
unverändert auf der Platte bleibt, dann nein.
Schlendrian angewöhnen geht schneller als abgewöhnen.
Achim S. schrieb:> Was war denn damals der Grund?
Dass der Assembler die Bitnummer für manche Befehle brauchte, und Atmel
für C und Assembler die gleichen Headerfiles benutzen wollte.
Achim S. schrieb:> Also was sprach gegen Sprechende Bitstrukturen, ggf. Unions, wo ich dann> z.B. schreibe:> TCCR2B.CS2=5;
Die Dinger sind - habe ich mir hier erklären lassen - für viele Fälle
praktisch, aber im Allgemeinen ungeeignet. Denn damit kann man nicht
alle Registerbits auf einmal setzen (= kostet Speicher und Zeit). Das
macht manche Hardware unbedienbar. Außerdem müssen die Register volatile
sein, d.h. jeder Zugriff geht auch auf die Hardware.
Zum Beispiel UCSRC (Atmega8515) ist mit so einem Ansatz nicht benutzbar,
weil du URSEL nicht getrennt von den anderen Bits setzen/löschen kannst
(lesen geht damit schon garnicht). Auf anderen Architekturen habe ich
solche Hardwarekonstrukte auch schon gesehen.
Klar kann man in so einem Fall immer die Union mit dem ganzen
Registerwert nehmen, aber damit verliert man den Vorteil des Bitfeldes
(welches sowieso unportabel ist, aber das ist bei AVR eher irrelevant).
Außerdem funktioniert so ein Ansatz nur mit IO- oder MMIO-Registern
vernünftig, aber nicht mit externen Registern (die via I2C, SPI oder
sonstwas angesprochen werden).
@Achim S. (achs)
>> Allerdings hatte Atmel damals einen Grund dafür, insofern ist es nicht>> total dämlich.>Was war denn damals der Grund?>Also was sprach gegen Sprechende Bitstrukturen, ggf. Unions, wo ich dann>z.B. schreibe:>TCCR2B.CS2=5;Beitrag "Re: XC8 compiler - Register schreiben wie bei Atmel"
Achim S. schrieb:> Was war denn damals der Grund?
Die gleichen Files werden auch für Assembler-Quelltext verwendet. Da
benötigt man bei den Einzelbitbefehlen aber die Nummer, nicht die Maske.
Von Nummer zu Maske ist leichter als andersrum.
> Bitstrukturen
Sind ok, wenn man sich nicht dran stört, pro Bit einen oder mehrere
Befehle zu investieren. Will man ein Steuerregister am Stück
initialisieren, klappt das so nicht. Weshalb Bitfelder nur als Option
sinnvoll sind, nicht als einziger Zugriffsmechanismus.
S. R. schrieb:> und Atmel> für C und Assembler die gleichen Headerfiles benutzen wollte
OK, das war ein nachvollziehbarer Grund.
Würde irgendwer das heute noch in reinen C(++)-Headern so machen?
Achim S. schrieb:> Würde irgendwer das heute noch in reinen C(++)-Headern so machen?
Ja. Das ARM Bitbanging benötigt ebenfalls Bitnummern statt Masken.
Leider ist das bei den Headern der Cortex-Mx Controller nicht üblich.
A. K. schrieb:> Achim S. schrieb:>> Würde irgendwer das heute noch in reinen C(++)-Headern so machen?>> Ja. Das ARM Bitbanging benötigt ebenfalls Bitnummern statt Masken.> Leider ist das bei den Headern der Cortex-Mx Controller nicht üblich.
Es gibt bei manchen Herstellern die Position des Bit bzw. der Gruppe von
Bits. Bei ST haben diese immer das "_Pos"-Suffix, z.B.
A. K. schrieb:> Arduino Fanboy D. schrieb:>> Wenns funktioniert ist erstmal ok.>> Fürs erste Testprogramm ja. Wenn das Programm aber länger als 1h> unverändert auf der Platte bleibt, dann nein.>> Schlendrian angewöhnen geht schneller als abgewöhnen.
Wer redet hier von Schlampigkeit?
Du?
Scheinst ja selber ein Problem mit deiner Schlampigkeit zu haben, sonst
müsstest du ja nicht sofort daran denken.
Ich rede von Verfahren.
Verschiedene Verfahren, welche man zur Anwendung bringen kann.
Und da ist im Grunde eins so gut wie das andere.
Natürlich ist das Geschmackssache und mit persönlichen Vorlieben
verbunden.
Also wieder mal eine gute Gelegenheit für Grabenkriege.
Christopher J. schrieb:>> Ja. Das ARM Bitbanging benötigt ebenfalls Bitnummern statt Masken.
Korrektur: Das Bitbanding ist natürlich gemeint.
> Es gibt bei manchen Herstellern die Position des Bit bzw. der Gruppe von> Bits. Bei ST haben diese immer das "_Pos"-Suffix, z.B.
Gut zu hören. Vor einigen Jahren war das noch nicht so.
so so schrieb:> Nö, ein C header ist ein C header und kein ASM Krams.
Und bei inline stehst du dann doof da, oder wie stellst du dir das
vor?
Sag jetzt nicht, inline wäre kein C.
Arduino Fanboy D. schrieb:> Scheinst ja selber ein Problem mit deiner Schlampigkeit zu haben, sonst> müsstest du ja nicht sofort daran denken.> Also wieder mal eine gute Gelegenheit für Grabenkriege.
Und scheinbar eine gute, um ursprünglich sachliche Auseinandersetzungen
in persönliche Angriffe umzuwandeln.
Arduino Fanboy D. schrieb:> Ich rede von Verfahren.> Verschiedene Verfahren, welche man zur Anwendung bringen kann.>> Und da ist im Grunde eins so gut wie das andere.
Wenn es nur um die Unterscheidung Bitnummern / Bitwertigkeit geht,
stimme ich Dir zu.
Wenn man aber, wie hier im Thread (zum Glück nur einmal) vorgeschlagen,
"magic numbers" verwendet, statt der im Datenblatt stehenden Namen, dann
sieht das anders aus.
Das ist strikt abzulehnen.
Ja, in dem Beispiel, macht die Verwendung der magischen Zahlen eher
keinen Sinn.
Auch finde ich, Kommentare sollten das "Warum" erklären.
Und nicht das "Was"
Nein, kein schönes Beispiel.....
Die Verwendung der Bezeichner, statt Zahlen, hat zumindest den Vorteil,
dass der Kompiler viel bessere Chancen hat, einem (Tipp)Fehler um die
Ohren zu hauen.
Oder, bei einem Wechsel des AVRs, kann sich der Code an andere
Bitnummern anpassen, ohne Hand anlegen zu müssen.
Ein Gegenbeispiel, wo magische Zahlen schon etwas Sinn machen:
1
// tiny85
2
enumAnalogReference:byte
3
{
4
REF_VCC=0b00000000,
5
REF_EXT=0b01000000,
6
REF_11=0b11000000,
7
REF_256=0b10010000,
8
REF_256_C=0b11010000,
9
};
Das Bitmuster lässt sich aus dem Datenblatt leicht ablesen.
Ist dann aber auch sehr speziell, nur für diesen µC Type
Ja, auch diese magischen Zahlen könnte man mit den vorgefertigten
Bezeichnern und << zusammenklöppeln, aber besser lesbar/überprüfbarer
wirds dadurch auch nicht.
Rufus Τ. F. schrieb:> Von diesem Rat rate ich ab.>> Das nur wenig besser als eine ausgerechnete Konstante als "magic number"..
Das ist allemal die sauberste und klarste Lösung - und dies mit weitem
Abstand von allen anderen Varianten.
Versuche du doch mal zu überlegen, was wohl besser ist:
a) direkt so, wie ich das geschrieben habe
b) mit irgendwelchen Bezeichnern zu arbeiten und NICHT ZU WISSEN, ob das
nun eine Bitmaske ist oder eine Bitnummer.
Im Falle b) bleibt einem nichts weiter übrig, als zusätzlich zum Lesen
des RefManuals auch noch die entsprechende Headerdatei herauszusuchen
und dort nachzulesen, was denn tatsächlich mit dem Bezeichner gemeint
ist.
Und nun guck dir mal ein paar typische Headerfiles z.B. von ST an. Die
sind oft weit über 1 MB groß und man sucht sich die Hacken wund darin,
weil dort die finalen Bezeichner fast immer mehrstufig (und dieses
natürlich an verschiedenen Stellen) definiert sind, ganz abgesehen von
netten Garnituren wie #ifdef, womit man erstmal herausfinden muß,
welcher Quellteil in welchem Falle überhaupt gelesen wird. NEE, DANKE.
Es gibt hübschere Text-Adventures.
Ich habe von der Unart, alles und jedes in irgend einen selbsternannten
Bezeichner zu verpacken, schon genug gesehen, um definitiv zu wissen,
daß genau DADURCH der größte Bockmist passiert. Eben aufgrund der
Denkunterschiede zwischen dem Verfasser und dem Anwender.
Und nochwas:
Bitgefummel der hier diskutierten Art gehört nicht nach main(), sondern
in einen LowLevel-Treiber. Den schreibt man einmal und testet ihn aus
und anschließend hat man damit rein GARNICHTS mehr zu tun, sondern man
benutzt eben das Interface des LowLevel-Treibers, wo solches Bitgefummel
nicht vorkommen sollte.
So herum wird was Vernünftiges draus.
W.S.
A. K. schrieb:>> Bits. Bei ST haben diese immer das "_Pos"-Suffix, z.B.>> Gut zu hören. Vor einigen Jahren war das noch nicht so.
Ah ja. Grandios!
Und nun hast du nicht nur nicht nur die Bezeichnungen gemäß RefMan,
sondern auch noch mehrere ellenlange dazuerfundene xxx_pos und xxx_msk
Bezeichner. Und das ganze je nach geschmack des jeweiligen Autors auch
noch nach unterschiedlichen Gestaltungsregeln.
Hier geht's wohl um den Wettbewerb, wer die meisten Bezeichner in seinen
Headerdateien hat.
W.S.
Rufus Τ. F. schrieb:> Wenn man aber, wie hier im Thread (zum Glück nur einmal) vorgeschlagen,> "magic numbers" verwendet, statt der im Datenblatt stehenden Namen, dann> sieht das anders aus.>> Das ist strikt abzulehnen.
Ich kann dich nicht hindern, bockig zu sein und dabei ungewollt
Obfuscation zu betreiben, bloß weil du Formalität anstatt Vernunft
herrschen lassen willst.
Nur eines: Auf die Beschreibungen im RefManual kannst du dich im
Allgemeinen verlassen (mit Ausnahmen!!).
Aber die dortigen Bezeichnungen kannst du NIEMALS einfach so verwenden,
denn dafür brauchst du Statements, wo eben diese Bezeichnungen in die
von dir ja so gehaßten Zahlen übersetzt werden. Also sowas wie #define
xyz 123
Entweder schreibst du dir selber was, wo die besagten Statements
drinstehen oder du benutzt eine Datei von jemand anderem. Aber dort
findest du eben nie die "im Datenblatt stehenden Namen", weil die eben
nur ein Teil eines HW-Registers sind und folglich immer eine
Sonderbehandlung brauchen: also so etwa:
XYZ = ABC | DEF | GHI; // implizit true für alle 3
oder
XYZ = (1<<ABC) | (1<<DEF) | (1<<GHI);
aber NIE so:
ABC = true;
DEF = true;
GHI = true;
usw.
Also schreib nicht so einen rechthaberischen Stil. Sonst antworte ich
dir: Was Rufus hier geschrieben hat, ist strikt abzulehnen, weil es
mehrere Sachfehler enthält.
W.S.
W.S. schrieb:> Und nun hast du nicht nur nicht nur die Bezeichnungen gemäß RefMan,> sondern auch noch mehrere ellenlange dazuerfundene xxx_pos und xxx_msk> Bezeichner.
Nein, es sind nämlich eben genau die Bezeichnungen aus dem RefMan, in
diesem Fall aus jenem des STM32F031F6, also RM0091. In diesem findest du
dann auf Seite 367 das "TIM1 Control Register 1", "Bit 7 ARPE:
Auto-reload preload enable".
Man kann zu jeder Peripherie, zu jedem Register und zu jedem Bit des
jeweiligen Registers den Namen zusammensetzen aus
Peripherie_Register_Bit, in diesem Fall eben TIM_CR1_ARPE. Das ist im
Übrigen bei allen Cortex-M der Fall. _Pos ist tatsächlich eine
ST-spezifische "Erweiterung" aber irgendwie logisch. Bei Freescale ist
es eben _SHIFT.
Hat man in einem Register ein Bitfeld, so wird eben Bit0 mit _0, Bit1
mit _1, Bit2 mit _2, etc. im Suffix bezeichnet. _Msk ist dann eben die
Maske für das Bitfeld und _Pos dessen Position, z.B.
Wolfgang schrieb:> Und bei inline stehst du dann doof da, oder wie stellst du dir das> vor?
???
Was willst du mir sagen?
Der Ursprang war, dass Atmel die header für c und asm verwendet. Und das
ist mir egal, weil ein c header ein c header ist und kein asm krams.
Jetzt verstanden?
so so schrieb:> Und das ist mir egal, weil ein c header ein c header> ist und kein asm krams.
Na das ist aber schön, dass dir das egal ist.
Atmel war es offensichtlich nicht egal, und deswegen ist es so.
Du darfst dir gerne eigene Header schreiben. ;-)
Faszinierend. Die Erste Antwort war richtig und wurde vom TO mit Dank
als ausreichend bestätigt.
Trotzdem kamen noch 40 weitere überwiegend nicht hilfreiche Beiträge.
Und es geht munter weiter!
S. R. schrieb:> Atmel war es offensichtlich nicht egal, und deswegen ist es so.
... beschis...
Stefanus F. schrieb:> Trotzdem kamen noch 40 weitere überwiegend nicht hilfreiche Beiträge.> Und es geht munter weiter!
Inkl. Deinem wertlosen Beitrag?