Forum: Mikrocontroller und Digitale Elektronik Vergleich binär-hexadezimal - Problem mit ~-Operator


von Steffen S. (immanuel777)


Lesenswert?

Hallo zusammen,

zunächst mal möchte ich mich bei allen Beitragsschreibern bedanken durch 
die ich die Grundlagen, der Mikrokontrolliererei lernen konnte. Bisher 
fand ich hier auf alle meine Fragen gute Antworten!

Nun muss ich aber selbst etwas posten, weil mir das Verhalten meines 
Programms große Fragen aufwirft.

Ich möchte den Zustand eines Schalters an PD0 auslesen. Port D ist auf 
Eingang geschaltet und ich habe die internen Pull-Up-Widerstände 
aktiviert. An allen Eingängen hängen Taster. Es passt auch alles. Ich 
hab's nachgemessen und an Port D liegt echt überall Vcc an, wenn kein 
Schalter gedrückt ist. Wenn ein Taster gedrückt wird, wird der 
entsprechende Pin auf GND gezogen - wie ich es wollte.

Nun möchte ich zunächst ohne Entprellen oder sonstiges eine Aktion 
ausführen, wenn ein Schalter gedrückt wurde - einfach zu Lernzwecken.

Dazu habe ich in meinem C-Programm quasi in einer Endlosschleife eine 
if-Abfrage konstruiert, die so aussieht:

if (PIND == ~(1<<PD0)){
 // restlicher Code - irrelevant für diese Zwecke
}

Das Argument der if-Abfrage gibt aber nie true. Eigentlich sollte es ja 
true sein, wenn der Taster an PD0 gedrückt wurde, oder?

1<<PD0 ergibt ja in Binärschreibweise sowas wie 0b00000001.
~(1<<PD0) wäre dann entsprechend 0b11111110, also das, was an PIND 
anliegen sollte, wenn der Taster an PD0 gedrückt ist.

Das funktioniert aber nicht.

Wenn ich die if-Abfrage aber folgendermaßen formuliere, geht es:

if (PIND == 0xFE){
 // restlicher Code - irrelevant für diese Zwecke
}

0xFE ist ja das gleiche wie ~(1<<PD0), nur in hexadezimaler 
Schreibweise.

Wieso geht jetzt die Variante mit hexadezimaler Schreibweise und die mit 
Binärschreibweise nicht?

Interessanterweise funktioniert die if-Abfrage übrigens, wenn ich sowas 
mache wie:

if (PIND == (1<<PD0)){
 // restlicher Code - irrelevant für diese Zwecke
}

Die gibt mir true, wenn alle Schalter bis auf den an PD0 gedrückt ist.

Liegt es also am ~-Operator?

Vielen Dank schonmal im Voraus für sachdienliche Hinweise, die zur 
Ergreifung des Übeltäters führen!

von greg (Gast)


Lesenswert?

Du solltest nicht das gesamte Byte des Porteingangs vergleichen, dich 
interessiert ja jeweils nur 1 Bit! Mit einem bitweisen AND kannst du das 
interessante Bit isolieren und so unabhängig voneinander prüfen. Das 
sieht dann so aus:

if ((PIND & (1<<PD3)) == 0) {
    // Taster an PD3 wurde gedrückt
    ...
}

von Peter (Gast)


Lesenswert?

Steffen S. schrieb:
> if (PIND == ~(1<<PD0)){
>  // restlicher Code - irrelevant für diese Zwecke
> }
> Liegt es also am ~-Operator?
ja so ist es, überlegt doch mal genau was nach dem compiler rechts neben 
dem == steht.

Du willst nur ein Bit von den Port nutzen und das machen alles andere 
mit einer und (&) verknüpfung - warum du nicht?

von greg (Gast)


Lesenswert?

Ach, und ~(1<<PD0) ist natürlich äquivalent zu 0xFE. Bist du dir sicher, 
dass du richtig getestet hast? Vielleicht ist dein Compiler etwas 
kaputt? Optimierung an?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

greg schrieb:
> Ach, und ~(1<<PD0) ist natürlich äquivalent zu 0xFE.
Nö... das ist höchstwarscheinlich equivalent zu 0xFFFE und damit 
ungleich von 0x00FE welches der Port wohl anehmen wird nachdem der 
(unsigned) Port auf int und die (signed) invertierte Variable erweitert 
worden sind.

von Steffen S. (immanuel777)


Lesenswert?

greg schrieb:
> Du solltest nicht das gesamte Byte des Porteingangs vergleichen, dich
> interessiert ja jeweils nur 1 Bit! Mit einem bitweisen AND kannst du das
> interessante Bit isolieren und so unabhängig voneinander prüfen. Das
> sieht dann so aus:
>
> if ((PIND & (1<<PD3)) == 0) {
>     // Taster an PD3 wurde gedrückt
>     ...
> }

Das stimmt natürlich. Ich dachte, das hätte ich probiert, aber scheinbar 
wohl doch nicht. Komme heute abend wieder zum Testen. Vielen Dank 
einstweilen!

von Karl H. (kbuchegg)


Lesenswert?

Steffen S. schrieb:

Und um die eigentliche Frage zu beantworten:

> 1<<PD0 ergibt ja in Binärschreibweise sowas wie 0b00000001.

richtig

> ~(1<<PD0) wäre dann entsprechend 0b11111110,

falsch

Du vergisst Datentypen und das in C die Grundeinheit ein int ist.

~(1<<PD0) ist kein 8 Bit Wert, sondern ein 16 Bit Wert. Nämlich 0xFFFE

Und dein Port Eingang wird sich schwer tun, diese Wert jemals annehmen 
zu können.

> Wenn ich die if-Abfrage aber folgendermaßen formuliere, geht es:
>
> if (PIND == 0xFE){

Auch klar. Denn 0xFE ist nicht dasselbe wie 0xFFFE

> 0xFE ist ja das gleiche wie ~(1<<PD0), nur in hexadezimaler
> Schreibweise.

Leider eben nicht.
Die kleinste Einheit, in der in C gerechnet wird, ist immer int (auf dem 
AVR: 16 Bit). Nur dann wenn der Compiler beweisen kann, dass er bei 
Verzicht auf die oberen 8 Bit auf das gleiche Ergebnis kommt, kann er 
die Operation als reine Byteoperation formulieren.

Aber  ~0x01  ist NICHT   0xFE sondern 0xFFFE. Und da machen jetzt die 
oberen 8 Bit den Unterschied.

> Liegt es also am ~-Operator?

Wie schon gesagt:
Eingänge fragt man immer so ab, dass man sich das interessante Bit 
freimaskiert und das dann abfragt und nicht indem  man Annahmen trifft, 
wie wohl die restlichen Bits am Port sein mögen.

von NurEinGast (Gast)


Lesenswert?

Hi,

natürlich sind die Hinweise " mach's mit & " richtig.

Allerdings könnte man auf den ersten Blick denken, dass das

   if (PIND == ~(1<<PD0)){
    // restlicher Code - irrelevant für diese Zwecke
   }

Konstrukt auch gehen müsste.

1<<PD0 = 0b00000001
~(1<<PD0) = 0b11111110

und wenn ich nun an Port D die erste Taste drücke, dann sollte doch dort 
0b11111110 anliegen. Damit sollte 0b11111110 == 0b11111110 sein, und die 
Bedingung erfüllt sein.

Der Compiler wird Dir aber aus ~(1<<PD0) nicht 0b11111110 machen, 
sondern er wird 0b1111111111111110 - also 16 Bit - machen. Und schon 
klappt der Vergleich nicht mehr.

Man könnte nun mal folgendes probieren.

   if (PIND == (char)(~(1<<PD0))){
    // restlicher Code - irrelevant für diese Zwecke
   }


Aber wie gesagt - mach's besser mit
   if (PIND & ~(1<<PD0)){

von NurEinGast (Gast)


Lesenswert?

Ok - Karl heinz Buchegger war schneller.
( Ich muss mal lernen schneller zu tuppen, damit ich auch mal gewinne 
;-) )

von NurEinGast (Gast)


Lesenswert?

... tippen, nicht tuppen.
Kommt vom schnellen tippen :-)

von Karl H. (kbuchegg)


Lesenswert?

NurEinGast schrieb:
> Ok - Karl heinz Buchegger war schneller.

No.
Läubi wars

Ich schreib zu viel :-)

von greg (Gast)


Lesenswert?

Ah stimmt. int vs. char kann eine ganz schön fiese Falle sein. :)
Wie auch immer, mach es per Maskierung, dann gibt es solche Probleme gar 
nicht erst.

von Steffen S. (immanuel777)


Lesenswert?

Läubi .. schrieb:
> greg schrieb:
>> Ach, und ~(1<<PD0) ist natürlich äquivalent zu 0xFE.
> Nö... das ist höchstwarscheinlich equivalent zu 0xFFFE und damit
> ungleich von 0x00FE welches der Port wohl anehmen wird nachdem der
> (unsigned) Port auf int und die (signed) invertierte Variable erweitert
> worden sind.

Sowas hab ich mir auch schon gedacht. Deshalb habe ich auch folgendes 
Konstrukt getestet:

PIND == ~(uint8_t)(1<<PD0)

Das ging aber auch nicht. Ich dachte, wenn ich 1<<PD0, was evtl. ein int 
ist, auf uint8_t caste und dann bitweise negiere, bekomme ich das 
gewünschte Ergebnis.

Aber verstehe ich das richtig, dass der Ausdruck 1<<PD0 16bit lang ist?

von Karl H. (kbuchegg)


Lesenswert?

Steffen S. schrieb:

> Sowas hab ich mir auch schon gedacht. Deshalb habe ich auch folgendes
> Konstrukt getestet:
>
> PIND == ~(uint8_t)(1<<PD0)

Hier hast du schlecht getestet.

Denn selbst wenn (uint8_t)(1<<PD0) ein 8 Bit Wert ist, der ~ macht dir 
daraus wieder 16 Bit. Reihenfolge der Operationen!

von -., (Gast)


Lesenswert?

> PIND == ~(uint8_t)(1<<PD0)
PIND == (uint8_t)(~(1<<PDO))

von Steffen S. (immanuel777)


Lesenswert?

Vielen Dank für die ausführliche Antwort, Karl-Heinz! Daumen hoch
Wieder was gelernt.

Auch allen anderen Helfern vielen Dank für die schnellen Antworten!

Oh, ich sehe gerade, dass ich demnächst auch lernen sollte, C-Code in 
die entsprechenden Tags zu packen. Werde versuchen, daran zu denken!

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.