Forum: Mikrocontroller und Digitale Elektronik Bits schieben


von Heiopei (Gast)


Lesenswert?

Ich wollte die (invertierten) Portleitungen D7~4 abfragen.
Ergebnis sollte eine Zahl von 0 bis 15 sein.
1
unsigned char n;
2
n = (~PIND) >> 4;
Zuerst wird doch PIND invertiert.
Dann wird das Ergebniss viermal rechtsgeschoben.
Da n unsigned char ist, sollten doch links nullen nachgeschoben werden?!
Leider klappt das so nicht.
1
unsigned char n;
2
n = ((~PIND) >> 4) & 0b00001111;
Aber so klappt es. Warum ist die maskierung mit 0b00001111 nötig?

von Klaus W. (mfgkw)


Lesenswert?

Überrascht mich jetzt spontan auch.
Offenbar ist ~PIND erstmal wieder signed.
Die Lösung ist:
1
n = ( (unsigned char /* bzw. uint8_t */)(~PIND) >> 4) & 0b00001111;

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Heiopei schrieb:

> Da n unsigned char ist, sollten doch links nullen nachgeschoben werden?!

Diese Begründung ist nicht richtig. Mit n hat das nix zu tun, da n gar 
nicht geschoben wird.

Jetzt fragt sich, ob der Kompiler aus den 8Bit bei der Komplementierung 
von PIND einen Integer macht. Der wäre auf dem AVR 16 bit breit. Dann 
wird bei

   PIND = 0b01010101

das Ergebnis von ~PIND auf 16 Bit aufgebohrt, also:

   ~PIND : 0b1111111110101010

Wenn man das nach rechts schiebt um 4 Stellen, dann wird daraus:

   (~PIND) >> 4: 0b1111111111111010

Oben schieben sich bei negativen Zahlen (2er Komplement) 1en rein[1], 
spielt hier aber keine Rolle, denn es werden nur die untersten 8 Bit im 
n gespeichert. Dann sieht n so aus:

   n: 0b11111010

Wohlgemerkt, alles nur, wenn der Compiler das Ergebnis von ~PIND auf 16 
Bit (= Integer auf z.B. ATmega8) aufbohrt. Um welchen Compiler/Prozessor 
handelt es sich denn?

Gruß,

Frank

[1] Vielleicht wird aus ~PIND auch ein unsigned int, dann kommen halt 4 
0en von oben rein. Nützt Dir aber auch nix. Du hast immer noch 4 Einsen 
vor Deinem Wert.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:
> Überrascht mich jetzt spontan auch.
> Offenbar ist ~PIND erstmal wieder signed.

Ob signed oder nicht, ist egal, ~PIND es ist halt ein Integer mit 16 
Bit. Kann auch unsigned int sein, Du hast trotzdem 8 Einsen im 
höherwertigen Byte. Die kriegst Du durch Shiften um 4 Stellen auch nicht 
weg ;-)

> Die Lösung ist:
>
1
> n = ( (unsigned char /* bzw. uint8_t */)(~PIND) >> 4) & 0b00001111;
2
>

Jepp. Hier verkleinerst Du (~PIND) wieder auf 8 Bit. Damit ist wieder 
alles im Lot und das "& 0b00001111" kann man sich wieder sparen.

Gruß,

Frank

von Matthias L. (Gast)


Lesenswert?

>Jetzt fragt sich, ob der Kompiler aus den 8Bit bei der Komplementierung
>von PIND einen Integer macht.

Das kann ich bestätigen. Ich hatte ein vergleichbares Problem vor 
längerer Zeit.

von Heiopei (Gast)


Lesenswert?

vielen Dank für die rege Anteilnahme

Frank M. schrieb:
> Um welchen Compiler/Prozessor handelt es sich denn?

Der Compiler ist der GCC vom AVR Studio 4.
Der Proz ist der ATmega8.

In der Datei IOM8.h steht:
#define PIND  _SFR_IO8(0x10)

Nun ist mir immer noch nicht klar, wie der Compiler darauf kommt,
das Ergebnis von ~PIND auf 16 Bit aufgebohren.
Klar, das maskieren mit 0b00001111 oder casting auf unsigned char hilft, 
ist aber irgendwie nur ein kurieren der Sympthome...   ;-)

von Hc Z. (mizch)


Lesenswert?

Stichwort: Integer promotion.  Die gilt auch für den ~-Operator.  Sie 
besagt (vereinfacht), dass alle Berechnungen mindestens in int 
durchzuführen sind und für kleinere Typen vor der Anwendung des 
Operators zunächst die Variablen nach int aufgeblasen werden müssen.

Der Compiler berechnet also zu Recht das Komplement von 0x00n0 anstelle 
von 0xn0.

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.