Forum: Mikrocontroller und Digitale Elektronik ATmega - Bitoperationen


von NN (Gast)


Lesenswert?

Hallo!

Habe eine kurze Frage zum Beschreiben von Registern. Das gesamte 
Register zu beschreiben (0b01010101) oder einzelne Bits des Registers zu 
setzen bzw. löschen ist ja kein Problem. Wie kann man aber auf 
einfachste Weise mehrere Flags auf einmal bearbeiten, ohne das gesamte 
Register zu beschreiben.

Z.B.:

R = 0b11001010;

Es sollen jetzt die letzten 4 Werte auf 1100 gesetzt werden. Dabei 
sollen die 4 ersten Werte nicht beschrieben werden. Am soll dann gelten:

R == 0b11001100;


Wie löst ihr dieses Problem? :)

gruß
NN

von Dietrich L. (dietrichl)


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

NN schrieb:

> oder einzelne Bits des Registers zu
> setzen bzw. löschen ist ja kein Problem.

Der Vorteil der |= bzw &= Schreibweise besteht darin, dass das was mit 
einem Bit geht auch mit mehreren Bits in einem Aufwasch geht.


> Es sollen jetzt die letzten 4 Werte auf 1100 gesetzt werden. Dabei
> sollen die 4 ersten Werte nicht beschrieben werden.

Beschreiben wirst du sie müssen. Aber wenn du wieder dasselbe 
draufschreibst, was eh schon dort steht, dann ändert sich ja nichts.

> Am soll dann gelten:
>
> R == 0b11001100;
>
>
> Wie löst ihr dieses Problem? :)

Erst mal die beswussten 4 Bits sicher auf 0 setzen

       R = ( R & 0b11110000 )

und dann das gewünschte Muster einodern

       R = ( R & 0b11110000 ) | 0b00001100;

Wenn dir klar geworden ist, dass du eine Und-Operation (&) wie ein 
'Sieb' benutzen kannst, durch das nur die von dir genehmigten Bits 
durchkommen und alle anderen auf 0 gesetzt werden, während eine 
Oder-Operation (|) wie ein Stempel benutzt wird, der bestimmte Bits auf 
jeden Fall auf 1 setzt, dann ist deine gewünschte Operation einfach nur 
eine Kombination aus beidem.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

NN schrieb:
> Es sollen jetzt die letzten 4 Werte auf 1100 gesetzt werden. Dabei
> sollen die 4 ersten Werte nicht beschrieben werden.

Wenn du das wörtlich nimmst, ist das bei AVR nicht möglich. Die vorderen 
4 Bits werden trotzdem beschrieben, allerdings mit ihrem ursprünglichen 
Wert. Genauer, mit dem Wert, den sie ein paar Takte vorher hatten. Falls 
sich der Wert der vorderen Bits zwischenzeitlich geändert hat, geht 
diese Änderung verloren.

Diese Erbsenzählerei ist für dich wahrscheinlich gar nicht wichtig – 
außer du arbeitest mit Interrupt-Routinen, die die besagten vorderen 
Bits jederzeit ändern können. Falls dem so sein sollte, helfen dir die 
Assembler-Befehle SBR und CBR, das heißt, die zu ändernden Bits müssen 
einzeln "angefasst" werden. Falls du C verwendest, kannst du in der 
Regel davon ausgehen, dass die Operatoren |= und &= in SBR und CBR 
übersetzt werden, wenn du damit immer nur ein Bit manipulierst. Eine 
absolute Garantie dafür gibt es allerdings nicht, insbesondre dann, wenn 
du die Optimierung des Compilers aktiviert hast.

von NN (Gast)


Lesenswert?

Stop! ^^

Ich muss es anders formulieren:

Nehmen wir mal den Timer/Counter2 register. Ich beschreibe das Register 
bei der Initialisierung:
1
TCCR2 = ((1<<WGM21) | (0<<WGM20)) | ((0<<COM21) | (1<<COM20)) | ((0<<CS22) | (0<<CS21) | (0<<CS20));

(Das shiften der Nuller bringt ja gar nix. Hab ich jetzt nur der 
Lesbarkeit halber eingefügt).


Jetzt starte ich den Timer mit:
1
TCCR2 |= ((1<<CS22) | (0<<CS21) | (0<<CS20));

Das geht gut, weil da vorher Nuller drin standen.

Irgendwo in nen IRS möchte ich den Timer nochmals modifizieren. Z.B:
1
TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));

Das geht nun total schief....

Die einzige Möglichkeit, die ich sehe wäre:
1
TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  //flags erstmal auf 0 setzen
2
TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));  //dann beschreiben

Sowas möchte ich vermeiden, weil da keiner mehr durchblickt:
1
TCCR2  &= 0b11110000;


Oder habt ihr bessere Vorschläge? ^^

von Karl H. (kbuchegg)


Lesenswert?

NN schrieb:

> Das geht nun total schief....

logisch

> Die einzige Möglichkeit, die ich sehe wäre:
>
>
1
> TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  //flags erstmal auf 0
2
> setzen
3
> TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));  //dann beschreiben
4
>

Was stört dich daran?

>
> Sowas möchte ich vermeiden, weil da keiner mehr durchblickt:
>
1
> TCCR2  &= 0b11110000;
2
>

Das war sowieso vorausgesetzt, aber du hast nun mal diese Schreibweise 
in deinem Eröffnungsposting gewählt.

von Oliver (Gast)


Lesenswert?

NN schrieb:
> Das geht nun total schief....

Weil, wie du richtig erkannt hast,

NN schrieb:
> (Das shiften der Nuller bringt ja gar nix. Hab ich jetzt nur der
> Lesbarkeit halber eingefügt).

NN schrieb:
> Die einzige Möglichkeit, die ich sehe wäre:
> TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  //flags erstmal auf 0 setzen
> TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));  //dann beschreiben

So ist es.
Alternativ leg dir zwei Variablen mit den beiden verschiedenen 
Registerinhalten an, die du dann komplett ins jeweilge Register 
schreibst.

Etwa so:
1
uint8_t TimerSetup_Takt_langsam = ((1<<WGM21) | (0<<WGM20)) | ((0<<COM21) | (1<<COM20)) | ((1<<CS22) | (0<<CS21) | (0<<CS20));
2
uint8_t TimerSetup_Takt_schnell = ((1<<WGM21) | (0<<WGM20)) | ((0<<COM21) | (1<<COM20)) | ((0<<CS22) | (1<<CS21) | (0<<CS20));
3
 
4
TCCR2  = TimerSetup_Takt_langsam;
5
 ...
6
TCCR2  = TimerSetup_Takt_schnell;


Oliver

von NN (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  //flags erstmal auf 0
>> setzen
>> TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));  //dann beschreiben
>>
> Was stört dich daran?

Nun ja. Erstens wollte ich wissen, ob das die einzige Möglichkeit ist 
oder ob ich vll grad irgendwo im Wald stehe. ^^  Zweitens nervt mich 
dieses auf 0 setzen...  Hätte gern im Code selbst nen "Einzeiler". Etwa 
so:
1
#define TCCR2_(x)    TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20)); TCCR2 |= x;
2
3
TCCR2_(((0<<CS22) | (1<<CS21) | (0<<CS20)));

Allerdings geht das so nicht, wegen dem vorderen Teil im define:
TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20))
Das müsste auch irgendwie dynamisch an das jeweilige Flag angepasst 
werden. Sonst bräucht ich ja für jedes Flag nen define ....  Das scheint 
mir aber nicht möglich. grmf ^^

von Karl H. (kbuchegg)


Lesenswert?

NN schrieb:
> Karl Heinz Buchegger schrieb:
>>> TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));  //flags erstmal auf 0
>>> setzen
>>> TCCR2 |= ((0<<CS22) | (1<<CS21) | (0<<CS20));  //dann beschreiben
>>>
>> Was stört dich daran?
>
> Nun ja. Erstens wollte ich wissen, ob das die einzige Möglichkeit ist
> oder ob ich vll grad irgendwo im Wald stehe. ^^  Zweitens nervt mich
> dieses auf 0 setzen...  Hätte gern im Code selbst nen "Einzeiler".
1
#define TIMER2_CONFIG    ((1<<WGM21) | (0<<WGM20)) | ((0<<COM21) | (1<<COM20))
2
3
4
   ....
5
6
   TCCR2 = TIMER_2_CONFIG | (1<<CS22) | (0<<CS21) | (0<<CS20);

und später
1
   TCCR2 = TIMER_2_CONFIG | (0<<CS22) | (1<<CS21) | (0<<CS20);

von Karl H. (kbuchegg)


Lesenswert?

NN schrieb:


>
1
> #define TCCR2_(x)    TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20));
2
> TCCR2 |= x;
3
> 
4
> TCCR2_(((0<<CS22) | (1<<CS21) | (0<<CS20)));
5
>
>
> Allerdings geht das so nicht, wegen dem vorderen Teil im define:
> TCCR2 &= ~((1<<CS22) | (1<<CS21) | (1<<CS20))

Ich seh jetzt das Problem nicht

Nenn das Makro TCCR2_PRESCALER, sichere es noch gegen Fehlbenutzung ab 
und alles ist doch in Butter.
Ein schönes Makro, mit dem man den Prescaler des Timers 2 wechseln kann.

> werden. Sonst bräucht ich ja für jedes Flag nen define ....

Übertreib mal nicht.
Die 5 mal, die man sowas im Jahr braucht, kann man das auch 
ausschreiben.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

NN schrieb:
> Nehmen wir mal den Timer/Counter2 register. Ich beschreibe das Register
> bei der Initialisierung:
>
1
> TCCR2 = ((1<<WGM21) | (0<<WGM20)) | ((0<<COM21) | (1<<COM20)) |
2
> ((0<<CS22) | (0<<CS21) | (0<<CS20));
3
>

Das heißt, der Registerinhalt hat einen Anteil, der immer konstant 
bleiben soll. Du kannst dir das zu Nutze machen. In C schaut das so aus:
1
#define TCCR2_CONST (1<<WGM21 | 0<<WGM20 | 0<<COM21 | 1<<COM20)

Timer starten dann so:
1
TCCR2= TCCR2_CONST | 1<<CS22 | 0<<CS21 | 0<<CS20;

Timer modifizieren:
1
TCCR2= TCCR2_CONST | 0<<CS22 | 1<<CS21 | 0<<CS20;

Timer stoppen:
1
TCCR2= TCCR2_CONST;


EDIT: Karl Heinz war wieder schneller. :-)

von NN (Gast)


Lesenswert?

Ok, dann weiß ich nun was geht und was nicht geht. Hat mir 
weitergeholfen.

Vielen Dank für die angenehme Diskussion. :)



Gruß
NN

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.