Tobi schrieb:
> Hallo,
>
> ich bin neu in der AVR Programmierung und versuche mir gerade klar zu
> machen, wie das ganze funktioniert. Bei der Implementierung eines LCD
> mit dem Code von dieser Seite bin ich auf folgende Zeile gestoßen:
>
>
1 | PORTD &= ~(0xF0>>(4-PD0));
|
Wo hast du das her.
Du steht im Original sicher nicht PD0. Zumindest nicht in der
Tutorialversion.
Was dort aber steht ist
1 | #define DATA_PIN_0 PD0
|
2 |
|
3 | ...
|
4 |
|
5 | PORTD &= ~( 0xF0 >> (4 - DATA_PIN_0) )
|
(schlag mich jetzt nicht, wenn da anstelle von DATA_PIN_0 ein anderer
Makro-Name benutzt wird. Auch mit einem anderen Namen geht es um
dasselbe)
Und nein. Diesen Teil des Ausdrucks betrachtest du am besten nicht
bitweise. Das ist eine ganz normale Subtraktion. 4 minus irgendwas.
Worum gehts denn? Was ist das Ziel.
Das Ziel ist es doch, den LCD Routinen die Vorgabe zu machen, an welchen
4 Datenpins eines Ports die 4 Datenleitungen zum LCD hängen.
Das kann sein, wenn DATA_PIN_0 auf 0 definiert wird
1 | #define DATA_PIN_0 0
|
2 |
|
3 | 7 6 5 4 3 2 1 0
|
4 | +--+--+--+--+--+--+--+--+
|
5 | | | | | |D3|D2|D1|D0| Dx sind die Datenleitungen zum LCD
|
6 | +--+--+--+--+--+--+--+--+
|
7 | ^
|
8 | | DATA_PIN_0 sagt uns, das D0 am Bit 0 vom Port zu finden ist
|
das kann aber auch sein, wenn DATA_PIN_0 auf zb 3 definiert ist
1 | #define DATA_PIN_0 3
|
2 |
|
3 | 7 6 5 4 3 2 1 0
|
4 | +--+--+--+--+--+--+--+--+
|
5 | | |D3|D2|D1|D0| | | |
|
6 | +--+--+--+--+--+--+--+--+
|
7 | ^
|
8 | | DATA_PIN_0 sagt uns, das D0 am Bit 3 vom Port zu finden ist
|
Beachte: größer als 4 kann DATA_PIN_0 nicht sein. Denn dann würden die 4
Datenleitungen zum LCD schon links aus dem Portregister raushängen.
Klar soweit?
Jetzt ist es aber auch so, dass die entsprechenden 4 Portbits auch mal
auf 0 gesetzt werden müssen.
Wie machst du das in Form eines Ausdrucks, wenn du im Vorfeld nicht
weißt, an welchen der 4 Portbits denn die 4 Datenleitungen nun wirklich
hängen?
Wenn wir diese Zahl wüssten und sich die auch nicht ändern kann, dann
wäre das einfach.
Sind die untersten 4 Datenbits betroffen (wenn also D0 immer am Portpin
0 liegen würde), dann würdest du einfach schreiben
Wenn die 4 Datenleitungen mit dem D0 am Bit 1 beginnen, dann könnte man
das zb so schreiben.
1 | PORTD &= ~ ( 0x0F << 1 );
|
Liegt D0 am Portbit 2, dann könnte man das zb so formulieren
1 | PORTD &= ~ ( 0x0F << 2 );
|
da zeichnet sich ein Muster ab! Das Kochrezept lautet: nimm die 0x0F
(0x0F deswegen, weil da 4 1 Bits drinnen liegen, die die 4
Datenleitungen 'ansprechen'). Und dann wird dieses Muster um genau so
viele Stellen nach links verschoben, so dass die rechteste 1 des Muster
genau dort zu liegen kommt, wo auch D0 im Byte des Portregisters liegt.
Würde man also formulieren
1 | PORTD &= ~( 0x0F << DATA_PIN_0 );
|
dann entsteht mittels
bzw.
jeweils genau das richtige Muster an 1 Bits, so dass durch die Negierung
und die Verundung genau diese Bits am Port definitiv auf 0 gesetzt
werden.
Jetzt hat aber das Originalautor aus irgendeinem Grund die Sache 'von
hinten' aufgezäumt. Warum auch immer.
Er legt seine ersten 4 Stück 1 Bits nicht nach rechts in den Port
1 | 7 6 5 4 3 2 1 0
|
2 | +--+--+--+--+--+--+--+--+
|
3 | |0 |0 |0 |0 |1 |1 |1 |1 |
|
4 | +--+--+--+--+--+--+--+--+
|
sondern er legt diese nach links in den Port
1 | 7 6 5 4 3 2 1 0
|
2 | +--+--+--+--+--+--+--+--+
|
3 | |1 |1 |1 |1 |0 |0 |0 |0 |
|
4 | +--+--+--+--+--+--+--+--+
|
Daher steht bei ihm 0xF0 im Code und nicht wie hier bei mir 0x0F
Um die Problemstellung, dass diese 4 Stück 1-Bits an die Bitposition 0
kommen müssen, wenn vereinbart ist, dass D0 am Bit 0 zu liegen kommen
soll, hat sich nichts geändert.
D.h. die Fragestellung lautet in diesem Fall:
Wenn ich das rechteste dieser 4 Stück 1 Bits an die Bitposition 0 haben
will und ich dazu die Definition
habe, wie oft muss ich dann dieses 0xF0 nach rechts verschieben, damit
genau dieses Ergebnis
1 | 7 6 5 4 3 2 1 0
|
2 | +--+--+--+--+--+--+--+--+
|
3 | |0 |0 |0 |0 |1 |1 |1 |1 |
|
4 | +--+--+--+--+--+--+--+--+
|
rauskommt?
Na das ist einfach. Ich musste 4 mal nach rechts verschieben, damit aus
der Ausgangssituation dieses Ergebnis entsteht.
Wenn ich wieder die Ausgangssituation
1 | 7 6 5 4 3 2 1 0
|
2 | +--+--+--+--+--+--+--+--+
|
3 | |1 |1 |1 |1 |0 |0 |0 |0 |
|
4 | +--+--+--+--+--+--+--+--+
|
habe und mein Ergebnis soll wegen
das hier sein
1 | 7 6 5 4 3 2 1 0
|
2 | +--+--+--+--+--+--+--+--+
|
3 | |0 |1 |1 |1 |1 |0 |0 |0 |
|
4 | +--+--+--+--+--+--+--+--+
|
wie oft muss ich dann nach rechts schieben?
Simpel: sieh einfach hin. Offenbar musstest du 1 mal nach rechts
verschieben.
Es ist also so, dass wenn ich von 0xF0 ausgehe und ich das rechteste
1-Bit auf der Position x haben will, dann muss ich genau (4-x) mal nach
rechts verschieben.
Genau das ist die Erklärung für den Teil
1 | #define DATA_PIN_0 PD0
|
2 |
|
3 | ...
|
4 |
|
5 | PORTD &= ~( 0xF0 >> (4 - DATA_PIN_0) )
|
6 |
|
7 | ***********************
|
den du im Code siehst.
Das ist also nichts irgendwie trickreiches auf Bit-Ebene. Sondern es
geht einfach nur darum, eine Abfolge von 1 Bits abhängig von einer
Zahlen-Definition an genau die Stelle in einem Byte zu bugsieren, an die
man sie haben will, wenn man die Position des rechtesten 1 Bits davon
vorgeben können will.
> Wenn ja, warum macht man sowas?
Weil man allgemeine Funktionen haben will.
Je nachdem an welche der 4 aufeinanderfolgenden Portleitungen du die 4
Datenleitungen deines LCD anschliesst, willst du lediglich
auf den jeweils richtigen Wert setzen und den Rest soll der Compiler
erledigen und den Code für dich anpassen.
Und genau deswegen hab ich am Anfang auch gefragt, wo du das her hast.
Denn wenn man den entsprechenden Zahlenwert kennt und der sich auch
nicht ändert und man auch nicht die Notwendigkeit hat, das leicht
änderbar machen zu müssen, wird man diesen 'Aufwand' nicht treiben. Es
sei denn natürlich, man hat bei den allgemeinen Funktionen geklaut, die
für sich adaptiert und nicht verstanden, warum das im allgemeinen Fall
genau so geschrieben wurde.