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
>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.
Man sollte auch auch den Unterschied zwischen = und |= kennen. Sonst erlebt man böse Überraschungen.
Ü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.
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ß
> 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.
>> 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.
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.
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
>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
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.
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
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.
>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).
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.