mikrocontroller.net

Forum: Compiler & IDEs << -Verständnisproblem


Autor: Alexander Schmeil (knut740)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Matthias Lipinsky (lippy)
Datum:

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

Ja. Tust du.
TWCR |= 15;
Hier ist sofort zu erkennen, das Bits 0..3 gesetzt wurden. Jedoch: Was 
machen diese gesetzten Bits?
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.

Autor: ... (Gast)
Datum:

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

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übersichtlicher gehts meiner Meinung nach, wenn im Header die Bits 
direkt als Bitmaske angegeben sind.
Nur so als sinnloses Beispiel:

#define StartBit 0x08

Register |= StartBit;


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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: i5002 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alexander Schmeil (knut740)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Patrick (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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-Tu...

Autor: Patrick (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine verständliche Erklärung findest Du unter Bitmanipulation im 
Abschnitt 3.2.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alexander Schmeil (knut740)
Datum:

Bewertung
0 lesenswert
nicht 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-Tu...

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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
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?
PORTC = PORTC & 0;

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

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

Genau so.

Autor: i5003 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Alexander Schmeil (knut740)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.