Bits in Register setzten (Schreibweise)

Gast #7336179
Lesenswert?

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.

#7338041
Lesenswert?

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.

Gast #7338050
Lesenswert?

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.

Gast #7338125
Lesenswert?

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.

#7338319
Lesenswert?

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ö.

Moderator (Firma: Titel) Persönliche Seite #7338367
Lesenswert?

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.

Gast #7338390
Lesenswert?

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.

Gast #7338729
Lesenswert?

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:

1
ADCSRA |=  ADEN_MASK;
Gast #7338748
Lesenswert?

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)

Gast #7338769
Lesenswert?

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 ...

Gast #7338888
Lesenswert?

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.

Gast #7338928
Lesenswert?

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;

Gast #7338936
Lesenswert?

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.

1
ADCSRA = (1<<ADPS1) | (1<<ADPS0) | (1<<ADPUPS02) | (1<<ADPUPS0); 
2
//statt 
3
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.

Gast #7338937
Lesenswert?

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 ... 😀

Gast #7338951
Lesenswert?

Hardwarenaher Programmierer schrieb:

Hex ist deutlich besser lesbar als das Rumgeshifte, insbesonders wenn mehr als ein bit besetzt wird.

1
 ADCSRA = (1<<ADPS1) | (1<<ADPS0) | (1<<ADPUPS02) | (1<<ADPUPS0);
2
 //statt
3
 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?

Gast #7339052
Lesenswert?

... zum anfeuern der Viellaberer und Makro Hasser!

1
//bit manipulation
2
#define BIT(b)    (1U << (b))  //set bit mask
3
#define SBIT(r,b)  (r |= BIT(b))  //set bit
4
#define CBIT(r,b)  (r &= ~BIT(b))  //clear bit
5
#define TBIT(r,b)  (r ^= BIT(b))  //toggle bit
6
#define RBIT(r,b)  (r & BIT(b))  //read bit
7
#define WBIT(r,b,v)  ((v)? SBIT(r,b):CBIT(r,b)) //write bit
8

9
//bit mask manipulation
10
#define SBITM(r,m)  (r |= (m))  //set bit mask
11
#define CBITM(r,m)  (r &=~(m))  //clear bit mask
12
#define TBITM(r,m)  (r ^= (m))  //toggle bit mask
13
#define RBITM(r,m)  (r &  (m))  //read bit mask
14
#define WBITM(r,m,v) (((r)&((0xFF)^(m)))|((m)&(v))) // write bit mask
#7339057
Lesenswert?

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.

Gast #7339063
Lesenswert?

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.

Gast #7339078
Lesenswert?

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

#7339141
Lesenswert?

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.

Beitrag #7339156 wurde von einem Moderator gelöscht.
Gast #7339453
Lesenswert?

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.

Gast #7339499
Lesenswert?

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.

Gast #7339620
Lesenswert?

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.

Gast #7339632
Lesenswert?

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.

Gast #7339647
Lesenswert?

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).

Moderator (Firma: Titel) Persönliche Seite #7339649
Lesenswert?

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.

Gast #7339661
Lesenswert?

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.

Moderator (Firma: Titel) Persönliche Seite #7339837
Lesenswert?

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.

Gast #7340730
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren