mikrocontroller.net

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


Autor: Steffen S. (immanuel777)
Datum:

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

Autor: greg (Gast)
Datum:

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

Autor: Peter (Gast)
Datum:

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

Autor: greg (Gast)
Datum:

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

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

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

Autor: Steffen S. (immanuel777)
Datum:

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

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

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

Autor: NurEinGast (Gast)
Datum:

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

Autor: NurEinGast (Gast)
Datum:

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

Autor: NurEinGast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... tippen, nicht tuppen.
Kommt vom schnellen tippen :-)

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

Bewertung
0 lesenswert
nicht lesenswert
NurEinGast schrieb:
> Ok - Karl heinz Buchegger war schneller.

No.
Läubi wars

Ich schreib zu viel :-)

Autor: greg (Gast)
Datum:

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

Autor: Steffen S. (immanuel777)
Datum:

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

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

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

Autor: -., (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> PIND == ~(uint8_t)(1<<PD0)
PIND == (uint8_t)(~(1<<PDO))

Autor: Steffen S. (immanuel777)
Datum:

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

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.