Zerlegen einer 8Bit Zahl in einzelne Bits

Hi,

heut steh ich wieder wie der Ochs vorm Berg.

Ich bekomme vom I2C-Bus von einem PCF8574 ein Byte zurück.
An dem Port-Extender hängen 8 Tasten. Jede stellt eines der Bits dar.

Jetzt such ich was einfaches um die Tasten aus dem Byte einzeln zu lesen.

Mir fällt dazu nur & ein:
In etwa so...

Taste1 = !(Byte & 0x01)
Taste2 = !(Byte & 0x02)
Taste3 = !(Byte & 0x04)

Das NOT ist wegen invertierter Logik des PCF

Gehts einfacher oder eleganter?

hk007:
Gehts einfacher oder eleganter?

Mit dem von Arduino dafür vorgesehenen Macro:
https://www.arduino.cc/en/Reference/BitRead

oh oh, so einfach. Danke

Steht sogar in der Language Reference
Wie lange gibts das schon?
Ist mir noch nie aufgefallen. Schäm.... :blush:

Wird wahrscheinlich auch nichts anderes machen, aber liest sich imho einfacher.

Ansonsten ist hier auch eine doch sehr sch

https://www.mikrocontroller.net/articles/Bitmanipulation

Um den händischen Weg zu verdeutlichen: das ist der korrekte Weg, aber man erzeugt normal die Maske mit Bit-Schieben: (1 << n)

Also wenn man das vierte Bit möchte:

if (b & (1 << 4))

So kann man dann das Bit als Parameter übergeben. 1 vier mal nach links geschoben gibt:
0001 0000. Also Bit 4

Da gibt es auch ein fertiges avr gcc Makro dafür: _BV() für "bit value". Auf dem Arduino hat man dass dann bit() genannt.

Also z.B.:

if (b & _BV(4))

oder:

if (b & bit(4))

@Serenifly:
Wie gewohnt, noch etwas Theorie zum Verständnis. Schön.

Aber wieso schreibst du immer if? Ich brauche doch eine boolean-Zuweisung. Oder seh ich da was falsch?

Auf alle Fälle hab ich jetzt mehrere Möglichkeiten:

    PCF_IN=Wire.read();       //Receive the data
    Taste1 = !bitRead(PCF_IN,0);
    Taste1 = !(PCF_IN & (1<<0));
    Taste1 = !(PCF_IN & bit(0));

if, bool. Ist das gleiche :slight_smile:

Das einzige wo du aufpassen musst: bitRead() gibt 0 oder 1 zurück. Wenn man es so per Hand macht kommt 0 oder ungleich 0 zurück. Das kann man daher nicht mit HIGH/LOW vergleichen!
Das wird nur auf 0/1 zurechtgestutzt wenn man das Ergebnis einem bool zuweist. Nicht wenn die Variable lediglich ein byte/unsigned char ist.

Serenifly:
if, bool. Ist das gleiche :slight_smile:

In meinem Hirn wohl nicht.... :o
Bei if denke ich an bedingte Zuweisung.

Taste1 hätte ich schon so definiert:

boolean Taste1;

Aber danke für den Hinweis

Das ist das, was mir nicht so gefällt. Dass ich eine Boolschen Variable auch einem Ergebnis mit mehr als einem Bit zuweisen kann. Ich kenne es eher, dass da ein Typkonflikt kommt.
Aber ich komm ja auch aus einer anderen Ecke.

Dass ein bool nur 0 oder 1 annehmen kann ist super. Damit kann das nicht fehlschlagen:

if (bVar == true)
{
}

Sollte bVar größer 1 sein, wäre das false! Das wäre dagegen wieder true:

if (bVar)
{
}

Das war früher in C nicht so. Da war bool(ean) nur ein typedef auf unsigned char. Deshalb gab es da dieses Ideom:

unsigned char value = !!(b & ...);

Eine doppelte Negation macht aus allem was größer 1 erst eine 0 und dann eine 1.

In C99 und C++ gibt es dagegen bool als richtigen Datentyp.

In Arduino was da lange Zeit idiotischerweise auch so. Da war boolean auch nur ein typedef auf unsigned char. Vor ein paar Versionen hat man das endlich geändert und boolean ist jetzt das gleiche wie bool. Kann also auch nur 0 oder 1 sein.

ist jetzt das gleiche wie bool. Kann also auch nur 0 oder 1 sein.

Ich finde die Sicht sollte man konsequent weiter führen und stattdessen sagen:
Bool Variablen können nur true und false werden.

Ob da jetzt eine physikalische 0 oder 1 hinter stecken ist (mir) dabei egal...

Bei Floats sagen wir ja auch nicht, dass das eine Folge von Bytes im Speicher ist.
Auch wenn dieses der Realität entspricht. Das beachten wir erst, wenn Daten transportiert werden wollen.

Serenifly:
In Arduino was da lange Zeit idiotischerweise auch so. Da war boolean auch nur ein typedef auf unsigned char. Vor ein paar Versionen hat man das endlich geändert und boolean ist jetzt das gleiche wie bool. Kann also auch nur 0 oder 1 sein.

Habe gerade das probiert:

boolean test;
bool test2;

Bei beiden habe ich ein Syntax-highlighting. Und kompiliert bekomm ich es auch.
"bool" seht aber nicht in der Language-Reference.
Aber wie du gesagt hast, ist es ja das gleiche. Dann nehm ich in Zukunft nur noch bool. Ist mir gewohnter und spart immer 3 Buchstaben :wink:

Ich finde die Sicht sollte man konsequent weiter führen und stattdessen sagen:
Bool Variablen können nur true und false werden.

Ja und nein. true und false sind 1 und 0. Andererseits sind Variablen allgemein wahr wenn sie ungleich 0 sind, solange man nicht direkt auf == true abfragt. Das kann verwirren.

In Visual C++ kommt da auch eine Warnung:

Warning C4805 '==': unsafe mix of type 'byte' and type 'bool' in operation

"bool" seht aber nicht in der Language-Reference.

Bool ist Standard C++! Boolean ist eine Arduino Kreation. Wer weiß was man sich dabei gedacht hat. Meiner Meinung ist es wahrscheinlich, dass man kompatibel zu Processing sein wollte, was Java ist und daher boolean hat.

Hallo,

vielen Dank für den Hilfreichen Thread!

Ich habe das mal soweit übernommen und kam zurecht, bis ich eine "if-Abfrage" gemacht habe.....

Egal welchen Taster ich in einer "if" abfrage in einer "UND-Verknüfung" mit der Abfrage, ob die entsprechende LED an ist oder aus (2 getrennte if-Abfragen), wird dieser Taster beim loslassen und erneuten drücken nicht wieder eingelesen bzw. löst keinen neuen Interrupt mehr aus.
Ohne "if-Abfrage" gehen alle Taster einwandfrei.....

Könnt ihr mir bitte den Wald vor den Augen entfernen?

Vielen Dank im Voraus für eure Unterstützung!

Gruß
Tobi

#include <Wire.h>
#define PCF8574_Chip_1 0x20

boolean Interrupt_Empfangen_PCF1oderPCF2 = false;
byte PCF1_Ausgelesen = 0;                        
boolean PCF1_Taster_1 = 1;
boolean PCF1_Taster_2 = 1;
boolean PCF1_LED_1 = 1;
boolean PCF1_LED_2 = 1;
boolean PCF1_LED_3 = 1;

unsigned long vergangene_Zeit = millis();  

void Interrupt_Routine_PCF1undPCF2() {           
if (millis() > vergangene_Zeit + 250) 
{         
  Interrupt_Empfangen_PCF1oderPCF2 = true;      
  vergangene_Zeit = millis();                   
}
}
void setup() 
{
Wire.begin();                                                                    
Serial.begin(9600);                                                                 

pinMode(2, INPUT_PULLUP);                                                            
attachInterrupt(digitalPinToInterrupt(2), Interrupt_Routine_PCF1undPCF2, CHANGE);

Wire.beginTransmission(PCF8574_Chip_1);
Wire.write(B11111111);                         
Wire.endTransmission();                        
delay (200);
}

void loop() 
{
if (Interrupt_Empfangen_PCF1oderPCF2)          
{
  Interrupt_Empfangen_PCF1oderPCF2 = false;    
  Wire.beginTransmission(PCF8574_Chip_1);
  Wire.requestFrom(PCF8574_Chip_1,1);         
  PCF1_Ausgelesen = Wire.read();                     
  Wire.endTransmission();
  delay (200);

//Auslesen der Taster am PCF1
PCF1_Taster_1 = bitRead(PCF1_Ausgelesen,0);
 PCF1_Taster_2 = bitRead(PCF1_Ausgelesen,1);

//Auslesen der LED's am PCF1
PCF1_LED_1 = bitRead(PCF1_Ausgelesen,2);
 PCF1_LED_2 = bitRead(PCF1_Ausgelesen,3);
   PCF1_LED_3 = bitRead(PCF1_Ausgelesen,4);
delay (200);

/*

if (PCF1_Taster_2 == 0 && PCF1_LED_1 == 1)      
{
   Wire.beginTransmission(PCF8574_Chip_1);   
   Wire.write(bitWrite(PCF1_Ausgelesen,2,0));      
   delay (200);                            
   Wire.endTransmission();               
}

if (PCF1_Taster_2 == 0 && PCF1_LED_1 == 0)    
{
   Wire.beginTransmission(PCF8574_Chip_1); 
   Wire.write(bitWrite(PCF1_Ausgelesen,2,1));  
   delay (200);                             
   Wire.endTransmission();           
}

delay(200);                                        
*/
}

}

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Das kannst Du auch noch nachträglich ändern.

Entferne bitte auch alle unnötigen Leerzeilen.

Gruß Tommy

Danke und sofort erledigt! :slight_smile:

boolean Interrupt_Empfangen_PCF1oderPCF2 = false;

Das sollte volatile deklariert sein das es in einem Interrupt geändert wird

  Wire.requestFrom(PCF8574_Chip_1,1);         
  PCF1_Ausgelesen = Wire.read();                     
  Wire.endTransmission();

Nach dem Auslesen kommt kein endTransmission()! Die Methode ist verwirrend benannt. Sie sendet was im Ausgangspuffer steht. Und schließt nicht jegliche Übertragungen ab

Hallo Serenifly,

du hast recht mit dem "volatile" und habe ich abgeändert!
Ebenso hast du mit deiner Aussage über "endTransmission()", welche ich auch bei den Lesevorgängen entfernt habe.

Leider ist das Ergebnis immer noch so das, sobald die "PCF1_LED_1" eingeschaltet ("0") ist, die Taster nach bei "0" hängen bleiben.
Drücke ich lediglich Taster 2, so kann ich ihn beliebig oft drücken und es wird sauber gelesen, aber sobald ich Taster 1 betätige und die LED sich eingeschaltet hat, bleibt Taster 2 nach dem Betätigen auch bei "0" hängen.

Ich sehe einfach den Fehler noch nicht und muss mich mal wieder belesen, um Wissen nachzuholen.....

#include <Wire.h>

#define PCF8574_Chip_1 0x20                      // Definiere die Hex-Adresse "0x20" dem "PCF8574_Chip_1" zu

volatile boolean Interrupt_Empfangen_PCF1oderPCF2 = false; // Weise "Interrupt_Empfangen" "false" (falsch) zu

byte PCF1_Ausgelesen = 0;                        // Weise "PCF1_Ausgelesen" den Wert "0" zu

boolean PCF1_Taster_1 = 1;
boolean PCF1_Taster_2 = 1;

boolean PCF1_LED_1 = 1;
boolean PCF1_LED_2 = 1;
boolean PCF1_LED_3 = 1;

unsigned long vergangene_Zeit = millis();        


void Interrupt_Routine_PCF1undPCF2() {  
 if (millis() > vergangene_Zeit + 250) {        
   Interrupt_Empfangen_PCF1oderPCF2 = true;
   Serial.println("Interrupt PCF1undPCF2 angesprochen");
   vergangene_Zeit = millis();                   
 }
}

void setup() 
{
Wire.begin();                                                                        
Serial.begin(9600);                                                                  
pinMode(2, INPUT_PULLUP);                                                            
 attachInterrupt(digitalPinToInterrupt(2), Interrupt_Routine_PCF1undPCF2, CHANGE);   


Wire.beginTransmission(PCF8574_Chip_1);        
Wire.write(B11111111);                         
Wire.endTransmission();                        

delay (200);

}

void loop() 
{
 // Wenn sich irgendein Pin am PCF8574 ändert:
 if (Interrupt_Empfangen_PCF1oderPCF2)          
 {
   Interrupt_Empfangen_PCF1oderPCF2 = false;    
   //Wire.beginTransmission(PCF8574_Chip_1);
   Wire.requestFrom(PCF8574_Chip_1,1);         
   if(Wire.available()){
     PCF1_Ausgelesen = Wire.read();              
     Serial.println("PCF1-Wert ausgelesen:");
     Serial.println(PCF1_Ausgelesen,BIN);       
                        }

    delay (200);

//Auslesen der Taster am PCF1
PCF1_Taster_1 = bitRead(PCF1_Ausgelesen,0);
Serial.println("PCF1-Taster1 Wert ausgelesen:");
Serial.println(PCF1_Taster_1,BIN);

PCF1_Taster_2 = bitRead(PCF1_Ausgelesen,1);
Serial.println("PCF1-Taster2 Wert ausgelesen:");
Serial.println(PCF1_Taster_2,BIN);


//Auslesen der lED's am PCF1
PCF1_LED_1 = bitRead(PCF1_Ausgelesen,2);
Serial.println("PCF1-LED1 Wert ausgelesen:");
Serial.println(PCF1_LED_1,BIN);

  PCF1_LED_2 = bitRead(PCF1_Ausgelesen,3);
  Serial.println("PCF1-LED2 Wert ausgelesen:");
  Serial.println(PCF1_LED_2,BIN);

    PCF1_LED_3 = bitRead(PCF1_Ausgelesen,4);
    Serial.println("PCF1-LED3 Wert ausgelesen:");
    Serial.println(PCF1_LED_3,BIN);
    delay (5);


 if (PCF1_Taster_1 == 0 && PCF1_LED_1 == 1)          
 {
    Wire.beginTransmission(PCF8574_Chip_1);      
    Wire.write(bitWrite(PCF1_Ausgelesen,2,0));       
    Serial.println("LED 1 an PC8574_Chip_1 an"); 
   // delay (200);                                
    Wire.endTransmission();                      
    delay(200);
 }

 if (PCF1_Taster_2 == 0 && PCF1_LED_1 == 0)         
 {
    Wire.beginTransmission(PCF8574_Chip_1);     
    Wire.write(bitWrite(PCF1_Ausgelesen,2,1));                     
    Serial.println("LED 1 an PC8574_Chip_1 aus"); 
    Wire.endTransmission();                     
    delay(200);
 }

 }


delay(200);
 Serial.println(PCF1_Ausgelesen,BIN);
}
void Interrupt_Routine_PCF1undPCF2() {  
 if (millis() > vergangene_Zeit + 250) {        
   Interrupt_Empfangen_PCF1oderPCF2 = true;
   Serial.println("Interrupt PCF1undPCF2 angesprochen");
   vergangene_Zeit = millis();                   
 }
}

Das ist mir gar nicht aufgefallen. Serial hat in Interrupts nichts verloren. Das arbeitet selbst mit Interrupts. Das musst du auch außerhalb machen! Flag und Zeit setzen ist alles was man in der ISR tun sollte.
Und bei der Abfrage auf die Zeit solltest du von millis() subtrahieren

Die ganzen Delays sind generell unnötig.

Baudrate nach oben setzen schadet auch nichts wenn man so viel Text schreibt

Ansonsten kommt vielleicht noch was durcheinander weil du den Baustein für Ausgänge und Eingänge nutzt. Das geht zwar, aber ich weiß nicht ob man das was spezielles beachten muss

Hallo,

hier ist vieles falsch was sich gegenseitig killt.
Deine Interruptroutine mit dem Zeitmerker läuft gegen den Baum.
Serial hat im Interrupt auch nichts zu suchen, weil es selbst Interrupts benötigt.
Wenn du das Innere der Interruptroutine zeitlich verzögerts, eine Art Entprellung, dann brauchst du auch keinen Interrupt. Pollen reicht dafür dann aus.

Deinen Wire...Transmission Code solltest du auch nochmal überdenken bzw. dich in den Arduino Referenzen nochmal einlesen.

Beim auslesen übermittelt man das Register was man auslesen möchte und bekommt den Inhalt zurück.

Beim beschreiben muss man das erst das Register übermitteln und dann den Wert der da rein soll. Das sehe ich bei dir nicht.

Auszug aus meinen Funktionen mit Fehlerüberprüfung.

bool readOneRegister (uint8_t i2cAdr, uint8_t reg, uint8_t &var)
{
  Wire.beginTransmission(i2cAdr);       // Connect
  Wire.write(reg);                      // Anfrage ab/der Register Nummer
  if (Wire.endTransmission() > 0) {     // war alles fehlerfrei?
    return false;                       // I2C Busfehler ?     
  }
  Wire.requestFrom(i2cAdr, 1);          // ein Bytes anfordern/lesen
  if (Wire.available() > 0) {           // sind Daten vorhanden?
    var = Wire.read();                  // auslesen und zurückgeben
  }
  return true;
}

bool writeOneRegister (uint8_t i2cAdr, uint8_t reg, uint8_t data)
{
  Wire.beginTransmission(i2cAdr);
  Wire.write(reg);                       // setzen auf Registeradresse ab der es losgehen soll
  Wire.write(data);                      // Daten in Register schreiben
  if (Wire.endTransmission() > 0) {      // war alles fehlerfrei?
    return false;                    // I2C Busfehler
  }
  return true;
}

Bitte beachten um was für ein IC es sich hier handelt. Der PCF8574 hat nur ein Register. Da muss man keine Register adressieren