Hallo liebes µC-Forum,
ich taste mich gerade an die digitalen Eingänge am ELEGOO Uno R3, einem
Arduino-Nachbau, an. Ich programmiere den Arduino mit der
Programmiersprache C, da ich gehört habe, dass sehr viele MCUs mit C
programmiert werden.
Jetzt zu meiner Frage: Wieso kann ich bei der Abfrage, ob der Taster
gedrückt ist, den Ausdruck
Christoph B. schrieb:> Wieso kann ich bei der Abfrage, ob der Taster> gedrückt ist, den Ausdruck ersetzen?
Weil in C alles was nicht 0 ist, wahr ist.
> PIND & (1<<3)
Liefert eine Zahl ungleich Null wenn der Pin High ist. Dann ist der
if-Ausdruck wahr.
Mehr is Detail:
Genau genommen erwartet if() keinen booleschen wahr/falsch Ausdruck,
sondern einen numerischen Wert der gleich oder ungleich 0 ist. Das
kannst du sehen, wenn du dir den generierten Assembler-Code anschaust.
Die obere Abfrage vergleicht den eingelesenen und verknüpften
Integerwert des Portstatus mit einer Integer-Konstante. Das Ergebnis
eines Vergleiches ist normalerweise ein Boolean mit true oder false.
Entsprechend wird die if-Anweisung dann ausgeführt.
So etwas kompliziertes gibt es in C aber gar nicht, sondern C kennt nur
Integer, wobei 0 immer false und alles andere true ist.
Die logische Verknüpfung von den zwei Integerwerten unten liefert als
Ergebnis auch 0 oder was anderes. Was als if-Bedingung wieder wie ein
Boolean mit false oder true betrachtet werden kann.
Andere Programmiersprachen lassen das nicht zu, sondern bestehen im
unteren Fall auf eine explizite Typumwandlung bzw. auf die obere Form. C
ist da nicht so pingelig. Und die Programmierer sind es auch nicht. Wenn
du darauf bestehst, es in C immer wie oben zu schreiben, was zwar
vollkommen richtig ist, wirst du irgendwann geteert und gefedert.
Christoph B. schrieb:> Jetzt zu meiner Frage: Wieso kann ich bei der Abfrage, ob der Taster> gedrückt ist, den Ausdruckif ((PIND & (1<<3)) == 0b00001000)
Diese Formulierung ist ausgesprochen tückisch: Du verwendest dort zwei
Konstanten in verschiedenen Schreibweisen, deren Wert identisch sein
muss.
Hallo,
(1<<3) ist schon deine Maske auf die du den Registerinhalt prüfst.
Das heißt, wenn du dich beim tippen von
1
0b00001000
nicht in der Bitstelle vertan hast, was tückisch sein kann, dann muss
der Wert von (PIND & (1<<3)) bei Gültigkeit sowieso gleich dem Wert
0b00001000 sein. Du hättest im dümmsten Fall nur eine überflüssige
Fehlerquelle ohne Nutzen.
Die Abfrage sagt glasklar aus das du nur auf die Gültigkeit des 3. Bits
abfragst.
1
if(PIND&(1<<3))
Die Alternative wäre
1
if(PIND&0b00001000)
Was dir aber wohl niemand so richtig empfehlen wird, weil die
Schreibweise tückisch ist.
Die Alternative zu oben wäre noch
Wolfgang schrieb:> Diese Formulierung ist ausgesprochen tückisch: Du verwendest dort zwei> Konstanten in verschiedenen Schreibweisen, deren Wert identisch sein> muss.
Muss nicht. Dann kommt entweder immer true oder false heraus. Ein guter
Compiler würde das erkennen und warnen. Oder die Maske enthält mehr Bits
als das abgefragte Muster:
1
if(PIND&0b00110000==0b0001000)
Machte durchaus Sinn.
Üblicherweise setzt man Masken und Muster z.B. mit Defines oder
Enumerations. Vereinfachen sollte man selbst aber nichts, das kann der
Optimizer. Und es bleibt lesbar:
Der geht übrigens auch, wenn das PIND als eine Struktur mit
vordefinierten Bitfeldern (Records) angelegt wurde. Ist m. E. die
übersichtlichste Methode.
dfIas schrieb:> Oder die Maske enthält mehr Bits als das abgefragte Muster:> if (PIND & 0b00110000 == 0b0001000)
Unw welchen Sinn macht die Maske dann noch?
dfIas schrieb:> Oder die Maske enthält mehr Bits> als das abgefragte Muster: if (PIND & 0b00110000 == 0b0001000)> Machte durchaus Sinn.
Deine Konstruktion macht so bestimmt keinen Sinn und liefert immer
false.
dfIas schrieb:> Oder die Maske enthält mehr Bits> als das abgefragte Muster:>
1
if(PIND&0b00110000==0b0001000)
> Machte durchaus Sinn.> Üblicherweise setzt man Masken und Muster z.B. mit Defines oder> Enumerations. Vereinfachen sollte man selbst aber nichts, das kann der> Optimizer. Und es bleibt lesbar:>
1
if(PIND&NAME_DER_MASKE==NAME_DES_MUSTERS)
Das ist ja genau das was wir dem TO nicht empfehlen. Doppelt gemoppelt.
Eine zusätzliche Fehlerquelle ohne Nutzen und zudem falsch. Du nennst
das eine Maske und das andere Muster. Ist aber jeweils das Selbe. Eins
davon muss weg. Und eigentlich sollte man keine defines mehr verwenden,
sondern konstante Variablen.
Veit D. schrieb:> Das ist ja genau das was wir dem TO nicht empfehlen. Doppelt gemoppelt.> Eine zusätzliche Fehlerquelle ohne Nutzen und zudem falsch. Du nennst> das eine Maske und das andere Muster. Ist aber jeweils das Selbe. Eins> davon muss weg. Und eigentlich sollte man keine defines mehr verwenden,> sondern konstante Variablen.
Nichts doppelt gemoppelt - genau so sollte man es machen. Kürzen bitte
ausschließlich dem Compiler/Optimizer überlassen, sonst ist die Source
unvollständig.
Die Maske spannt sich über alle Bits auf, das Muster hat innerhalb der
Maske Nullen oder Einsen, außerhab don't-cares. Sind also nicht per se
gleich. Es gibt aber zu jeder Maske auch ein Muster, das der Maske
entspricht.
Dass die Maske aus nur einem Bit besteht, ist ein Sonderfall. Und auch
zu einer Maske mit nur einem Bit gibt es 2^n Muster, also zwei Muster -
eines mit Null, eines mit Eins.
Bei meinem Beispiel hatte sich ein Typo eingeschlichen, hinten fehlte
beim Muster eine 0. Das hätte man erkennen können. Hier noch mal
korrigiert:
1
if(PIND&0b00110000==0b0001000*0*)
Maske und Muster sind allgemein nicht gleich. Sie sind nur gleich, wenn
man ein einzelnes Bit auf Eins abfragt.
Zur Maske 0b00*11*0000 gäbe es die vier abzufragenden Muster
0b00*00*0000, 0b00*01*0000, 0b00*10*0000 und 0b00*11*0000.
Wie gesagt, ein angelegter Record (Schreibweise mit Doppelpunkt) räumt
mit diesen Konstrukten gänzlich auf. Man muss sich dann auch bei den
abgefragten Mustern nicht mehr ums Schieben kümmern.
dfIas schrieb:> if (PIND & 0b00110000 == 0b0001000*0*)
Vorsicht: Den folgenden Code
1
if(PIND&0b00110000==0b00010000)[
2
mach_dies();
3
mach_das();
4
// ... und viel weiterer code
5
}
optimiert GCC zu einem simplen
1
PIND;
;-)
Wenn – wie im Beispiel des TE – nur ein einzelnes Bit abgefragt werden
soll, würde ich das "== irgendwas" generell weglassen, da es mehr zur
Verwirrung als zur Verständlichkeit beiträgt.
Hallo,
habe nochmal nachgedacht. Ich war immer auf einen EIN-Bit Vergleich aus.
Du möchtest 2 Bit vergleichen. Okay, dass ist was anderes. :-)
Ich würde bzw. muss noch eine Klammer drumherum schreiben, ansonsten
meckert mein Compiler.
dfIas schrieb:> Der geht übrigens auch, wenn das PIND als eine Struktur mit> vordefinierten Bitfeldern (Records) angelegt wurde. Ist m. E. die> übersichtlichste Methode.
Ein Bitfeld? Davon höre ich zum ersten Mal. Was kann ein Bitfeld?