Forum: Compiler & IDEs << -Verständnisproblem


von Alexander S. (knut740)


Lesenswert?

Hallo,


kann mir vielleicht jemand erklären, wie eine Zeile wie

TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

funktionieren kann, wenn man doch anderseits im Lehrbuch für C liest,
 << würde das Links-Verschieben bezeichnen, wobei die nach links 
verschobenen Teile des Bytes mit Nullen aufgefüllt würden.


Obige Zeile ist zwar bequem und übersichtlicher, als wenn man z.B. 
schreibt

PORTB |= 15;

aber bei letzter Zeile weiß man genau, an welchen Bits man manipuliert, 
und daß der Rest unangetastet bleibt. Wohingegen oben das Auffüllen der 
linksverschobenen Bits etwas maghrebinisch erscheint.

Gibt es eine Erklärung für diesen Widerspruch oder verstehe ich da etwas 
falsch?
Gruß
knut

von Matthias L. (Gast)


Lesenswert?

>Gibt es eine Erklärung für diesen Widerspruch oder verstehe ich da etwas
>falsch?

Ja. Tust du.
1
TWCR |= 15;
Hier ist sofort zu erkennen, das Bits 0..3 gesetzt wurden. Jedoch: Was 
machen diese gesetzten Bits?
1
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
Hier sieht man allerdings, dass:
-das Interrupt-enable Bit,
-das TWI enable Bit,
- und das Stopp-Bit gesetzt wurde.
Somit lässt sich durch zweite Schreibweise sofort erkennen, welche 
Funktion hier erfüllt wird.

von ... (Gast)


Lesenswert?

Man sollte auch auch den Unterschied zwischen = und |= kennen. Sonst 
erlebt man böse Überraschungen.

von Christian R. (supachris)


Lesenswert?

Übersichtlicher gehts meiner Meinung nach, wenn im Header die Bits 
direkt als Bitmaske angegeben sind.
Nur so als sinnloses Beispiel:
1
#define StartBit 0x08
2
3
Register |= StartBit;

Ist beim MSP430 immer so, ich find das sehr übersichtlich.

von Gast (Gast)


Lesenswert?

Hallo,
es gibt zwei Arten ein Bit zu bezeichnen:

1. die Bitnummer also z.B.: 0...15
2. die Bitmaske 2^BitNo

Da einige ASM-Befehle das Bit als Nummer annehmen, ist die Referenz als 
Nummer angebracht.
Braucht man nun eine Bitmaske, wird eine 1 um BitNo Stellen nach links 
verschoben. Das kann schon der Compiler und es wird kein ASM-Code 
zusätzlich erzeugt.

Will man einige Bits in einem Register setzen, alle anderen rücksetzen, 
dann macht man das mit der Oder-Verknüpfung der einzelnen Masken:

TWCR = Maske1 | Maske2 | Maske3 = (1<<BitNo1) | (1<<BitNo2) | 
(1<<BitNo3)

Das weist dem ganzen Register neue Bitwerte zu!

Nur einzelne Bits setzen ohne die anderen zu ändern:

TWCR |= Maske1 | Maske 2
oder
TWCR = TWCR | Maske1 | Maske2 = TWCR | (1<<BitNo1) | (1<<BitNo2)

Also: Es hat schon seinen Sinn (1<<BitNo) zu schreiben.

Gruß

von Rolf Magnus (Gast)


Lesenswert?

> kann mir vielleicht jemand erklären, wie eine Zeile wie
>
> TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
>
> funktionieren kann, wenn man doch anderseits im Lehrbuch für C liest,
>  << würde das Links-Verschieben bezeichnen, wobei die nach links
> verschobenen Teile des Bytes mit Nullen aufgefüllt würden.

Das ist doch schon die Erkärung ;-)

> Obige Zeile ist zwar bequem und übersichtlicher, als wenn man z.B.
> schreibt
>
> PORTB |= 15;
>
> aber bei letzter Zeile weiß man genau, an welchen Bits man manipuliert,

Echt? Ich muß doch erst die 15 in die einzelnen Bits zerlegen, um 
rauszubekommen, daß das Bit 0, 1, 2 und 3 sind. Dann muß ich im 
Datenblatt nachschauen, was an diesen Bitpositionen noch gleich war.
Bei der ersten Methode kann ich mir diese zwei zusätzlichen Schritte 
sparen.

von i5002 (Gast)


Lesenswert?

>> PORTB |= 15;
> Bits zerlegen, um rauszubekommen, daß das Bit 0, 1, 2 und 3 sind.
Da fängts schon an. ;-)


Ich finde diese Schreibweise auch häßlich: TWCR = (1<<TWINT) | (1<<TWEN) 
| (1<<TWSTO);
Aber das wurde wohl so eingeführt, weil:
> Da einige ASM-Befehle das Bit als Nummer annehmen, ist die Referenz als
> Nummer angebracht.
Andere Prozessorhersteller machen das anders. Ich empfinde die 
Atmellösung eher als unübliche (und schlechter lesbare) Ausnahme.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Christian R. wrote:

> Übersichtlicher gehts meiner Meinung nach, wenn im Header die Bits
> direkt als Bitmaske angegeben sind.

Einigkeit, keine Frage.  Geht beim AVR nur deshalb nicht, weil die
Headers auch für die Assemblerprogrammierung benutzbar sein sollen
und es einige Assemblerbefehle gibt, die mit Bitnummern statt
Bitmasken arbeiten (SBI, CBI, SBIS, CBIS, SBRS, SBRC).  Da es trivial
ist, aus einer Bitnummer eine Maske zu machen aber nicht umgekehrt,
hat man sich offenbar seinerzeit zu den Nummern entschieden.

von Alexander S. (knut740)


Lesenswert?

Rolf Magnus wrote:

>> PORTB |= 15;
>>
>> aber bei letzter Zeile weiß man genau, an welchen Bits man manipuliert,
>
> Echt? Ich muß doch erst die 15 in die einzelnen Bits zerlegen, um
> rauszubekommen, daß das Bit 0, 1, 2 und 3 sind. Dann muß ich im
> Datenblatt nachschauen, was an diesen Bitpositionen noch gleich war.
> Bei der ersten Methode kann ich mir diese zwei zusätzlichen Schritte
> sparen.

Das mit den vordefinierten Bezeichnungen der Bits wie TWCR = (1<<TWINT)
finde ich ja auch ganz gut, aber mein Problem ist bei der Verschieberei 
nach links das Auffüllen mit Nullen.
Nehmen wir doch an, ich hätte einen PORTC, wo alle Bits irgendwie 
gesetzt sind und ich wollte den vierten und nur diesen einschalten.
Habe ich mit
PORTD = (1<<Ausgang_3).
dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?

Wozu also das << ?  Steht zwar in jedem Manual, aber Und-bzw. 
Oder-Verknüpfungen wären doch genau so übersichtlich.


Vielen für Eure Nachhilfe in C.
mfg
Alexander

von Patrick (Gast)


Lesenswert?

>Habe ich mit
>PORTD = (1<<Ausgang_3).
>dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?
Ja, damit schon. Aber mit
PORTD |= (1<<Ausgang_3)
nicht. Damit kannst Du einzelne Bits setzen, ohne andere zu 
beeinflussen. Siehe 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Schreiben_von_Bits

von Patrick (Gast)


Lesenswert?

Eine verständliche Erklärung findest Du unter Bitmanipulation im 
Abschnitt 3.2.

von Karl H. (kbuchegg)


Lesenswert?

Alexander Schmeil wrote:

> Das mit den vordefinierten Bezeichnungen der Bits wie TWCR = (1<<TWINT)
> finde ich ja auch ganz gut, aber mein Problem ist bei der Verschieberei
> nach links das Auffüllen mit Nullen.
> Nehmen wir doch an, ich hätte einen PORTC, wo alle Bits irgendwie
> gesetzt sind und ich wollte den vierten und nur diesen einschalten.
> Habe ich mit
> PORTD = (1<<Ausgang_3).
> dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?
>
> Wozu also das << ?  Steht zwar in jedem Manual, aber Und-bzw.
> Oder-Verknüpfungen wären doch genau so übersichtlich.

Das eine hat doch nichts mit dem anderen zu tun.

Beim << geht es darum, wie die Binärzahl zustande kommt.

 1 << 6   ergibt eine Binärzahl, in der genau das Bit Nr 6
gesetzt ist und alle anderen Bits 0 sind.

Was du dann mit dieser Zahl machst, ist eine ganz andere Thematik.
Nichts und niemand hindert dich daran, diese Zahl (mit dem einen
gesetzten Bit) auf das Port Register zu ver-oder-n um damit genau
dieses eine Bit zu setzen.

Ob du

  PORTC |= 64;
  PORTC |= 0b01000000;
  PORTC |= ( 1 << 6 );

schreibst, ist gehupft wie gesprungen. Es passiert immer das
gleiche: Das Bit 6 am PORTC wird auf 1 gesetzt. Nur in der letzten
Schreibweise kannst du aber auf einen Blick und ohne groß Nachrechnen
oder nachzählen ersehen, dass du das Bit 6 setzt. Das nur dieses eine
Bit beeinflusst wird ergibt sich aus dem |=, das hat aber nichts damit
zu tun, wie denn die Bitmaske erzeugt wurde.

Wenn du jetzt diesem Bit noch einen Namen gibst

#define LED_BIT  6

dann wird die Schreibweise

  PORTC |= ( 1 << LED_BIT );

noch sprechender und interessanterweise auch universeller, da du
nicht mehr auswendig wissen musst, an welchem Bit denn nun die
Led tatsächlich sitzt.

Analog gilt: Wenn ich im TIMSK das Bit setzen will, welches mir
den Overflow Interrupt für den Timer 0 einschaltet, dann schreibe
ich einfach

   TIMSK |= ( 1 << TOIE0 );

und muss mich überhaupt nicht mehr darum kümmern, welches Bit das
denn jetzt bei diesem speziellen AVR-Controller ist. Ich setze einfach
nur das TOIE0 Bit, welches das auch immer in diesem Register bei diesem
speziellen Prozessor sein mag. Das TOIE0 Bit deshalb, weil mir
1 << TOIE0 eine Binärzahl ergibt, in der genau dieses eine Bit und nur
dieses eine Bit gesetzt ist. Und das im Register TIMSK dann auch nur
dieses eine Bit verändert (auf 1 gesetzt) wird, wird durch den |=
sichergestellt.

von Alexander S. (knut740)


Lesenswert?

Patrick wrote:
>>Habe ich mit
>>PORTD = (1<<Ausgang_3).
>>dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?
> Ja, damit schon. Aber mit
> PORTD |= (1<<Ausgang_3)
> nicht. Damit kannst Du einzelne Bits setzen, ohne andere zu
> beeinflussen. Siehe
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Schreiben_von_Bits

Vielen Dank

daß das mit dem
PORTC |= (1<<Ausgang_3) möglich ist, wußte ich nicht, aber daran werde 
ich mich schnell gewöhnen.
Sicher gilt auch
PORTC &= (0<<Ausgang_3) ? Oder muß ich schreiben
PORTC &= ~(1<<Ausgang_3)?

mfg
Alexander

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Alexander Schmeil wrote:

> Sicher gilt auch
> PORTC &= (0<<Ausgang_3) ?

Das gilt, macht aber vermutlich nicht das, was du dir vorstellst. ;-)

Nimm's mal auseinander, das ist die Kurzform für:
1
PORTC = PORTC & (0<<Ausgang_3);

Der Term (0<<Ausgang_3) ist dabei eine umständliche Formulierung :)
für die Zahl 0: du kannst eine 0 noch so oft hin- und herschieben,
es wird nur eine 0 bleiben.  Was passiert also?
1
PORTC = PORTC & 0;

PORTC wird auf 0 gesetzt, aber vorher noch einmal eingelesen...

> Oder muß ich schreiben
> PORTC &= ~(1<<Ausgang_3)?

Genau so.

von i5003 (Gast)


Lesenswert?

>Habe ich mit PORTD = (1<<Ausgang_3).
>dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?
Nicht nur das, sondern auch noch die Pins 4,5,6,7. (Nur 3 nicht - den 
hast du gesetzt).

von Alexander S. (knut740)


Lesenswert?

i5003 wrote:
>>Habe ich mit PORTD = (1<<Ausgang_3).
>>dann nicht nebenbei und unbeabsichtigt die Ausgänge 0,1 und 2 gelöscht?
> Nicht nur das, sondern auch noch die Pins 4,5,6,7. (Nur 3 nicht - den
> hast du gesetzt).

Leuchtet mir ein. Also schreibe ich demnächst

PORTD |= (1<<Ausgang_3)   // ein

und

PORTD &= ~(1<<Ausgang_3)  // aus

mfg

Alexander

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.