Hallo zusammen,
ich habe eine Frage zur folgenden Schreibweise
wann nehme ich z.B.
ADCSRA = (1<<ADPS1) | (1<<ADPS0);
oder diese Schreibweise
ADCSRA |= (1<<ADEN);
wo ist der Unterschied?
Viele Grüße
Schlecht erklärt.
Uwe K. schrieb:> ADCSRA = (1<<ADPS1) | (1<<ADPS0);
Damit beschreibst du das ganze Register mit den (wohlgemerkt:)
8 Bit die du dir zusammengereimt hast. Du änderst (womöglich,
abhängig von der Vorgeschichte) den gesamten Registerinhalt.
Uwe K. schrieb:> ADCSRA |= (1<<ADEN);
Damit liest du das Register, ver-oderst ein Bit in den gelesenen
Bit-Satz und schreibst den gesamten Registerinhalt wieder zurück.
Damit änderst du effektiv nur die Bits im Register die du mit der
Ver-Oderung angesprochen hast.
Uwe K. schrieb:> wo ist der Unterschied?
Die Operatoren werden in jedem C und C++ Grundlagenbuch oder Tutorial
beschreiben.
Ein solches, sollte man wenigstens ein mal überflogen haben!
Und bitte nicht mit eine Airbus
EAF schrieb:> Die Operatoren werden in jedem C und C++ Grundlagenbuch oder Tutorial> beschreiben.
Blöd nur das die bei µC gerne mal unvollständig sind.
Denn
1
REG|=(1<<BITWERT);
kann je nach µC mal ein Read-Modify-Write (z.B. ARM) oder auch nicht
(AVR, 8051 je nach Register/Zieladresse) sein.
Einige µC kennen nämlich Befehle zum Setzen/Löschen einzelner Bits. Das
beschreibt Dir ein Grundlagenbuch eher nicht.
Das hat durchaus Relevanz in der Praxis wenn Interrupts im Spiel sind -
das Read-Modify-Write ist unterbrechbar.
Jim M. schrieb:> Das hat durchaus Relevanz in der Praxis wenn Interrupts im Spiel sind -> das Read-Modify-Write ist unterbrechbar.
Drum sind solche Compound Operatoren auch seit C++23(?) auch deprecated,
wenn sie sich auf volatile Daten beziehen.
Allerdings ist diese Tatsache kein Grund, Ein Grundlagenbuch für
überflüssig zu erklären, wie du es mit deinem Beitrag versuchst.
Jim M. schrieb:> Einige µC kennen nämlich Befehle zum Setzen/Löschen einzelner Bits. Das> beschreibt Dir ein Grundlagenbuch eher nicht.
Der Compiler setzt diese Befehle ein!
Ob mit, oder ohne Buch.
In der Überschrift sagst Du Register.
Was da bei |= passiert ist völlig unklar und abhängig von Prozessor,
Register und Compiler.
Meist passiert das gleiche als wäre es eine variable. Es kann aber
beliebige weitere Effekte haben.
A. S. schrieb:> In der Überschrift sagst Du Register.>> Was da bei |= passiert ist völlig unklar und abhängig von Prozessor,> Register und Compiler.
Keine Sekunde. Die Frage war auch nicht nach Atomar oder
Read/Modify/Write. Die ERSTE Antwort vom Lothar war korrekt und
umfänglich! Da hätte man einfach aufhören können!
> Meist passiert das gleiche als wäre es eine variable. Es kann aber> beliebige weitere Effekte haben.
Nö.
Falk B. schrieb:> Die Frage war auch nicht nach Atomar oder Read/Modify/Write. Die ERSTE> Antwort vom Lothar war korrekt und umfänglich! Da hätte man einfach> aufhören können!
Du unterstellst, das "Register" keine Rolle spielt.
Das kann sein, muss aber nicht.
erklehr behr schrieb:> Damit **änderst** du effektiv nur die Bits im Register die du mit> der Ver-Oderung angesprochen hast.
Das stimmt so nicht. Denn wenn das Bit schon auf 1 war, dann **ändert**
sich durch den |= Befehl gar nichts.
Wenn man das Bit tatsächlich **ändern** also toggeln will, muss man den
^= Befehl nehmen.
Wenn da statt **änderst** das Wort **setzt** stehen würde, dann wärs
korrekt.
A. S. schrieb:> Falk B. schrieb:>> Die Frage war auch nicht nach Atomar oder Read/Modify/Write. Die ERSTE>> Antwort vom Lothar war korrekt und umfänglich! Da hätte man einfach>> aufhören können!>> Du unterstellst, das "Register" keine Rolle spielt.>> Das kann sein, muss aber nicht.
Du solltest mal näher erklären und ein Beispiel bringen was bei welchem
Register bei einem µC deiner Wahl unvorhergesehenes passieren wird.
Ich programmiere nur AVRs und mir ist solches noch nicht begegnet.
Uwe K. schrieb:> oder diese Schreibweise>> ADCSRA |= (1<<ADEN);>> wo ist der Unterschied?
Beide haben den selbe "Konstruktionsfehler", das Zusammenwurschteln
einer Konstanten über Präprozessor-shiftoperationen.
Die Shiftoperationen finden nicht wirklich auf dem Zielprozessor statt,
den interessiert nur die resultierende Konstante;
also gleich (Annahme ADEN = 2):
1
ADCSRA=ADCSRA|0x04;
Oder wenn man "Magic-Numbers vermeiden will und dafür eine constant
ADEN_MASK definiert.
1
ADCSRA=ADCSRA|ADEN_MASK;
Oder mit redundzlosen schreibweise für den Fall das ein Register
gleichzeitig als Ziel und Quell-register benutzt wird:
HildeK schrieb:> Du solltest mal näher erklären und ein Beispiel bringen was bei welchem> Register bei einem µC deiner Wahl unvorhergesehenes passieren wird.
Port, zumindest bei alten Pics. Setzen war ob 1 oder 0 bei Output, lesen
war nicht der letzte wert sondern der aktuelle Pin Zustand.
Bei Toshiba gab es Register, die nicht rücklesbar waren. RMW führte da
ins Chaos.
Bei Interrupflagtregistern führt das regelmäßig dazu, dass grad
auftretende Interrupts gelöscht werden und unerkannt bleiben. So selten,
dass meist ein Defekt vermutet wird
In einigen der Fälle reicht es, die Bits einzeln zu setzen/löschen, da
der Prozessor dafur eigene Befehle hat.
In anderen muss man mit Shadowregistern arbeiten (keine Ahnung, ob der
Begriff OK ist. Meint gewöhnliche variablen, die den istwert speichern,
manipulieren und dann zuweisen)
HildeK schrieb:> Du solltest mal näher erklären und ein Beispiel bringen was bei welchem> Register bei einem µC deiner Wahl unvorhergesehenes passieren wird.> Ich programmiere nur AVRs und mir ist solches noch nicht begegnet.
µC's in FPGA's wären auch so ein Thema, da hat ich mal ein Design, das
beim Schreiben auf ein Readonly-register einen Fehlercode auf den
Daten-Bus legte, das ein stupider RMW-Programmiere stur als Bitmaske für
ein WO-Command-Register verwendete.
Führte natürlich zu ungewollten Effekten.
War auch alles in der FPGA-Beschreibung dokumentiert, aber manche
Programmierer sind sich zu fein darin zu lesen. Die denken sich, kennste
einen, kennste alles ...
Uwe K. schrieb:> wann nehme ich z.B.>> ADCSRA = (1<<ADPS1) | (1<<ADPS0);
Ich muß das mal ein bissel auseinandernehmen.
Also ich schätze mal, dein Register enthält ein Feld aus 2 Bits, wo
irgend ein Wert 0..3 eingetragen werden kann und du hast versucht, das
auseinanderzunehmen, damit du (1<<... schreiben kannst. Kannst du
machen, aber leserlicher ist es, wenn du (in diesem Falle) (3<<ADPS)
schreibst. Vorausgesetzt, daß dein Feld so heißt und du dort eine 3 drin
haben willst.
Und das Ganze ist insgesamt eine normale Zuweisung:
ADCSRA = irgend eine Konstante;
Dabei werden eben alle Bits des Registers gesetzt.
Solche Schreibweisen wie Ottokar += Emil; oder ähnliche sind eigentlch
nur Kurzschreibweisen für Ottokar = Ottokar+Emil; und sind nur auf dem
Mist der Faulheit von C Programmierern gewachsen. Man kann sich daran
gewöhnen, sollte aber dabei nicht vergessen, was dahintersteckt, nämlich
eine Addition (oder andere Operation je nach Zeichen vor dem =) zu dem
Inhalt der Zielvariablen.
Ganz generell gilt, daß die Schreibweise (1<<Bezeichner) eben deshalb
verwendet wird, weil man dabei die Bitnummer im Ausdruck drinhat, was
deutlich besser lesbar ist als irgend ein Dezimal- oder Hex-Ausdruck.
W.S.
W.S. schrieb:> aber leserlicher ist es, wenn du (in diesem Falle) (3<<ADPS)> schreibst.
Ist es nicht!
(vielleicht für dich, (nur für dich?))
Ich bevorzuge eher diese Schreibweise:
ADCSRA = (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
Auch:
ADCSRA = _BV(ADPS1) | _BV(ADPS0); // _BV(ADPS2) |
Was allerdings aufs gleiche raus kommt...
oder eben Masken:
constexpr byte adcPrescalerDIV8 {_BV(ADPS1) | _BV(ADPS0)}; //
_BV(ADPS2) |
ADCSRA = adcPrescalerDIV8;
W.S. schrieb:> Ganz generell gilt, daß die Schreibweise (1<<Bezeichner) eben deshalb> verwendet wird, weil man dabei die Bitnummer im Ausdruck drinhat, was> deutlich besser lesbar ist als irgend ein Dezimal- oder Hex-Ausdruck.
Nö.
Bei dezimal mag das stimmen, aber bei Hex sollte doch klar sein das bei
0x05 das bit ganz rechts und das dritte von rechts gesetzt. Und bei 0xFF
ist alles gesetzt. und bei 0x04 nur das dritte.
Ansonsten neben dem Datenblatt des Prozessors mal das Hexedezimalsystem
lernen. Und wenn es garnicht in den Schädel will, dann einen
taschenrechner mit Hex/dec/bin Anzeige verwenden. Auch da finden sich
die Hex-Zahlen.
Hex ist deutlich besser lesbar als das Rumgeshifte, insbesonders wenn
mehr als ein bit besetzt wird.
ADCSRA = 0x53; // '1' auf ADPS0, ADPS1, ADPUPS0 und ADPUPS2; Rest: '0'
Dann kommt auch der Compiler nicht auf die dumme idee, das Ganze erst
berechnen zu wollen statt gleich das richtige Literal zu verwenden.
Und dann vergiss mal in 'C' ein Zeichen vom lustigen Buchstabentanz und
hab dann trotzdem einen korrekten syntax, aber andere
Berechnungsreihenfolge.
A. S. schrieb:> Port, zumindest bei alten Pics. Setzen ... [usw.]> Bei Interrupflagtregistern führt das regelmäßig dazu, dass grad> auftretende Interrupts gelöscht werden und unerkannt bleiben.
Danke für die Ausführungen!
Das Verhalten bei einigen speziellen Registern sind Sonderfälle; da muss
man schon wissen, was man tun will. Und sie sind üblicherweise
dokumentiert.
> Bei Toshiba gab es Register, die nicht rücklesbar waren. RMW führte da> ins Chaos.
Undokumentiert?
Bei AVRs gibt es auch ein paar Sonderfälle, die sind aber klar im DB
benannt. Und funktionieren mit den oben diskutierten Befehlen mit "REG
|= (1<<X)" mit dem WINAVR. Wie da eine Fehlbehandlung aussieht, die ins
Chaos führt, wüsste ich jetzt nicht.
kurz&knap schrieb:> µC's in FPGA's wären auch so ein Thema ... [usw.]> War auch alles in der FPGA-Beschreibung dokumentiert,
Auch dir besten Dank.
Aber wie du sagst: es ist dokumentiert und nicht irgend ein seltsamer
Nebeneffekt. Da habe ich dann wenig Gnade, eher Schadenfreude ... 😀
ADCSRA=0x53;// '1' auf ADPS0, ADPS1, ADPUPS0 und ADPUPS2; Rest: '0'
Also, beim oberen Befehl weiß und sehe ich sofort, was gesetzt wird. Und
ich würde immer "|=" verwenden, außer wenn die anderen Null sein müssen.
Zudem muss ich nicht mühsam die Bits aus dem DB in einen HEX-Wert
umzurechnen.
Und den Kommentar "Rest: '0' kannst du immer dazu schreiben, der passt
oben auch.
Beim unteren muss der Kommentar nicht mit dem Hex-Wert übereinstimmen.
Du änderst was am Wert und vergisst den Kommentar anzupassen. Zumindest
mir würde das passieren.
> Dann kommt auch der Compiler nicht auf die dumme idee, das Ganze erst> berechnen zu wollen statt gleich das richtige Literal zu verwenden.
Ich schätze mal, der berechnet das zur Compilezeit. Wenn ja, wo ist dann
der Unterschied und wieso ist es eine dumme Idee?
Hardwarenaher Programmierer schrieb:> Dann kommt auch der Compiler nicht auf die dumme idee, das Ganze erst> berechnen zu wollen
Das berechnet nicht mal der Compiler sondern schon vorher der
Präprozessor, der auch die Makros durch Quelltext ersetzt.
Einem hardwarenahen Programmierer sollte das aber bekannt sein.
Lothar M. schrieb:>> Dann kommt auch der Compiler nicht auf die dumme idee, das Ganze erst>> berechnen zu wollen> Das berechnet nicht mal der Compiler sondern schon vorher der> Präprozessor, der auch die Makros durch Quelltext ersetzt.
Der Präprozessor BERECHNET rein GAR NICHTS, der macht nur Textersetzung!
Berechnen tut der Compiler und erkennt, daß es eine Konstante ist, und
dementsprechend nicht zur Laufzeit berechnet werden muss.
egal was jetzt genau der Präprozessor, der Compiler oder der Optimizer
macht. Am Ende passiert das alles in dem selbem Programm, nämlich dem
avr-gcc. Wie das intern modularisiert ist, ist doch eigentlich egal.
Stefan F. schrieb:> egal was jetzt genau der Präprozessor, der Compiler oder der Optimizer> macht. Am Ende passiert das alles in dem selbem Programm, nämlich dem> avr-gcc. Wie das intern modularisiert ist, ist doch eigentlich egal.
Nein, solange manche module/Optimierungen erst angeschaltet werden
(-O0,-O2,-Os) müssen.
Und vielleicht wollte ja der Programmer diese umständliche
Konstantenberechnung zur Laufzeit, weil er so "Stress erzeugt".
Manche Sicherheitslücke beruht auf "der CPU Stress machen":
https://www.heise.de/hintergrund/Weshalb-Rowhammer-Angriffe-auf-den-Arbeitsspeicher-funktionieren-koennen-4439749.html
Uwe K. schrieb:> wann nehme ich z.B.>> ADCSRA = (1<<ADPS1) | (1<<ADPS0);>> oder diese Schreibweise>> ADCSRA |= (1<<ADEN);
Du vergleichst Äpfel mit Birnen.
Im ersten Beispiel wird ADCSRA komplett überschrieben, Einsen und
Nullen, alle Bits, wobei dabei 2 Bits gesetzt werden, alle anderen
werden zurückgesetzt.
Im zweiten Beispiel setzt du nur ein Bit und lässt die anderen wie sie
sind.
Du könntest das erste Beispiel auch so formulieren, dass nur die beiden
Bits gesetzt werden:
ADCSRA |= (1<<ADPS1) | (1<<ADPS0);
lang:
ADCSRA = ADCSRA | (1<<ADPS1) | (1<<ADPS0);
Oder das zweite Beispiel auch so dass es direkt zugwiesen wird, und das
komplette Register beschrieben wird.
> ADCSRA = (1<<ADEN);
Gruß
M.
EAF schrieb:> Ist es nicht!> (vielleicht für dich, (nur für dich?))>> Ich bevorzuge eher diese Schreibweise:> ADCSRA = (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
Also, mal ohne dem nachzugehen, von welchem Chip das 'ADPS' herkommt und
was es dort bewirkt, so vermutet man ja doch, daß das ein dreibittiges
Feld ist, was die Werte 0..7 aufnehmen kann.
Und du willst sowas in einzeln hingeschriebene Bits aufdröseln?
Dein 'ist es nicht' kann ich nicht nachvollziehen. Was du schreibst, ist
unleserlich, denn man müßte gedanklich erst besagte Bits zusammensetzen,
um die gewünschte Zahl zu erkennen.
W.S.
HildeK schrieb:> Undokumentiert
Der TO fragt quasi nach dem Unterschied von = und |=. Er ist also
Anfänger.
Nur schränkt er das explizit auf Register ein. Da sollte man schon Mal
darauf hinweisen dürfen. Ein Anfänger entdeckt sowas sonst nicht in Doku
sondern auf die harte Tour.
A. S. schrieb:> Der TO fragt quasi nach dem Unterschied von = und |=. Er ist also> Anfänger.
Er fragt, das ist der Punkt. Er versucht also sich selbst eine meinung
darüber zu bilden, welche (Schreib-)Weise am Besten für ihn passt.
Für einen solchen eigeninitiativen Lerner ist es IMHO am besten, viele
mögliche Varianten vorzustellen und dem Frager selbst die Entscheidung
überlassen.
Das verlangt natürlich, das der Antworter bereit und fähig ist, "über
seinen eigenen Schatten zu springen" und eben nicht seine
jahrzehntelange Routine als das einzig Wahre zu verkaufen.
kurz&knap schrieb:> Das verlangt natürlich, das der Antworter bereit und fähig ist, "über> seinen eigenen Schatten zu springen" und eben nicht seine> jahrzehntelange Routine als das einzig Wahre zu verkaufen.
Ich hab doch genau zum Stil gar nichts gesagt. Ich hab auf mögliche
Seiteneffekt bei Registern hingewiesen, weil der TO explizit nach
Registern fragt.
A. S. schrieb:> Meist passiert das gleiche als wäre es eine variable. Es kann aber> beliebige weitere Effekte haben.
A. S. schrieb:> Ich hab doch genau zum Stil gar nichts gesagt. Ich hab auf mögliche> Seiteneffekt bei Registern hingewiesen, weil der TO explizit nach> Registern fragt.
Ja zum Stil hätte man wohl eher dieses Zitat zu dem Gekritzel mit dem
"Kleiner-als Zeichen" voranstellen sollen:
> Ganz generell gilt, daß die Schreibweise (1<<Bezeichner) eben deshalb> verwendet wird, weil ... deutlich besser lesbar ist
Und das ist nicht von "A. S.", (falls das irgend wen interessiert).
A. S. schrieb:> Ich hab auf mögliche Seiteneffekt bei Registern hingewiesen, weil der TO> explizit nach Registern fragt.
Uwe K. erwähnt in seinem Post mit keinem Wort irgendein Register. Er
fragt auch nicht nach dem Schiebeoperator (1<<xx) und ob das Schieben
einer Zahl (z.B. für einen Multiplexer oder Prescaler) nicht auch
sinnvoll sein könnte. Diese Ausdrücke links und rechts der Operatoren
können für die Frage also durch die Buchstaben a und b ersetzt werden.
Uwe K. fragt letztlich also nur nach dem Unterschied zwischen
1
a=b;
und
1
a|=b;
Und das findet sich nun wirklich in jedem C-Anfänger-Buch ziemlich weit
vorne.
Lothar M. schrieb:> Uwe K. erwähnt in seinem Post mit keinem Wort irgendein Register.
Doch tut er, vielleicht mal die Suchfunktion benutzen, solange noch der
Schlaf in den Augen hockt?!
> Uwe K. fragt letztlich also nur nach dem Unterschied zwischen
Nö fragt er nicht, bei ihm stehen rechts zwei operanden und nicht einer.
und dann kann man schon fragen:
Wo der Unterschied zwischen
a = b op c; und a op = c; besteht insbesonders wenn man die Möglichkeit
b ≡ a impliziert.
Die Frage steht in seinem Topic, nicht in den Beispielen.
Und im Topic steht "Bit's in Register setzen"! Also muß man sich hier
(Mikrocontroller in C programmieren) schon mit Shift-operatoren u.ä.
auseinandersetzen.
kurz&knap schrieb:> Doch tut er, vielleicht mal die Suchfunktion benutzen, solange noch der> Schlaf in den Augen hockt?!
Tatsächlich, 81 Treffer. Mitten im Wald, sieht keinen Baum. Blöd auch...
Aber ich neige stark zur Vermutung, dass ich trotzdem Recht habe und es
nicht um spezielle Eigenheiten beim Schreibvorgang auf ein Register
geht.
Lothar M. schrieb:> Aber ich neige stark zur Vermutung, dass ich trotzdem Recht habe und es> nicht um spezielle Eigenheiten beim Schreibvorgang auf ein Register> geht.
Deine Neigungen sein Dir unbenommen, auch wenn sie völlig aus der Spur
führen.
Auch in der Programmierung ist die Schreibweise ist schon sehr speziell
aka individuell. Der eine erkennt Bitposition nach kurzen Blick auf die
Hexadezimalnotation oder liest den Kommentar am Zeilenende, und der
andere quält halt seine Umwelt mit irrealen Sonderzeichenkonstrukten.