PCF8574 4Pns als Eingang 4 Pins als Ausgang

Hallo Zusammen

Gibt es eine Möglichkeit gleichzeitig
die Pins P0 bis P3 als Eingänge und
die Pins P4 bis P7 als Ausgänge zu schalten.

Lassen sich die Zustände der einzelnen Ports auch unabhängig voneinander schalten beziehungsweise abfragen?

In Netz habe ich nur Beispiele für jeweils Ein- oder Ausganskonfiguration gefunden also kein Gemischtbetrieb.

erst Versuche habe ich mit dem Sketchen gemacht:

PCF8574 Pins als Eingänge geschaltet:

/****************************************************************
*
* PCF8574 I²C-Bus-Expander als Eingabeport
*
****************************************************************/

#include <Wire.h>

#define I2C_ADDR    0x23 // I2C-Addresse


void setup() {
  Serial.begin(9600); 
  
  // Für Leonardo
  while (!Serial)
    ;
  
  Wire.begin(I2C_ADDR); 
  
  Serial.println("Testprogramm PCF8574 I2C-Bus-Expander");
  Serial.println("-------------------------------------");
  Serial.println();
}


void loop() {
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(I2C_ADDR | 0x01);    // Alle Ports als Eingang
  Wire.requestFrom(I2C_ADDR, 1);  // Ein Byte (= 8 Bits) anfordern
  while(Wire.available() == 0)    // Warten, bis Daten verfügbar 
    ;
  Serial.print("Eingelesen: ");
  Serial.println(Wire.read());
  Wire.endTransmission(true);
  
  delay(50);
}

PCF8574 Pins als Ausgänge geschaltet:

/****************************************************************
*
* PCF8574 I²C-Bus-Expander als Ausgabeport
*
****************************************************************/

#include <Wire.h>

#define I2C_ADDR    0x40 >> 1 // I2C-Addresse


void setup() {
  Serial.begin(9600); 
  
  // Für Leonardo
  while (!Serial)
    ;
  
  Wire.begin(I2C_ADDR); 
  
  Serial.println("Testprogramm PCF8574 I2C-Bus-Expander");
  Serial.println("-------------------------------------");
  Serial.println();
}


void loop() {
  Serial.println("Pins HIGH schalten");
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(I2C_ADDR & 0xFE);   // Alle Ports als Ausgang
  Wire.write(0xFF);              // Alle Pins HIGH
  Wire.endTransmission(true);
  
  delay(3000);
  
  Serial.println("Pins LOW schalten");
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(I2C_ADDR & 0xFE);   // Alle Ports als Ausgang
  Wire.write(0x00);              // Alle Pins LOW
  Wire.endTransmission(true);
  
  delay(3000);
}

Vielen Dank schonmal im Vorraus Rudi

  Wire.beginTransmission(I2C_ADDR);
  Wire.write(I2C_ADDR | 0x01);    // Alle Ports als Eingang
  Wire.requestFrom(I2C_ADDR, 1);  // Ein Byte (= 8 Bits) anfordern
  while(Wire.available() == 0)    // Warten, bis Daten verfügbar 
    ;
  Serial.print("Eingelesen: ");
  Serial.println(Wire.read());
  Wire.endTransmission(true);

Dieser Code wird nicht den erwarteten Effekt haben.
Zum einen versuchst Du die Adressierung mittels Wire.write nachzuvollziehen. Das macht die Wire-Bibliothek aber schon für Dich, Du darfst das nicht mehr machen (abgesehen davon wäre die Adressierung so falsch, die 7-bit-Adresse müsste noch nach links geschoben werden).
Die Wire.write()-Daten werden übrigens erst beim Aufruf von Wire.endTransmission wirklich gesendet. Der Lese-Vorgang wird also zuerst ausgeführt, erst danach wird geschrieben.

Diese Zeile:

  Wire.write(I2C_ADDR & 0xFE);   // Alle Ports als Ausgang

impliziert eine Konfiguration, die aber nicht stattfindet. Dieser Chip ist relativ einfach gestrickt. Die I/O-Pins werden bei einem Schreibzugriff als Ausgang beschaltet, bei einem Lesezugriff sind sie Eingang. Da Du nur entweder ein ganzes Byte schreiben oder ein ganzes Byte lesen kannst, kannst Du nicht einen Teil der Pins als Eingang und gleichzeitig einen Teil als Ausgang verwenden.

Verwende einen anderen Chip, wenn Du solche Konfigurationen machen willst.

Hallo Summary

danke für Deine Antwort.
Ich habe die zwei Sachen (Schalter-Platinenteil und LED-Platinenteil) jetzt getrennt aufgebaut und mit je einem PCF8574 versehen.
Jetzt funktioniert es .
Die Anweisung " Wire.write(I2C_ADDR | 0x01); // Alle Ports als Eingang " habe ich tatsächlich auch auskomentieren können.

Das mit der 7-Bit Adresse habe ich auch gelesen. Aber mit der Adresseingabe 0X23 funktionierts keine Ahnung wiso.
Meine Schalterplatine hat jetzt 0X23 und die LEDplatine 0X25.

Jetzt stehe ich noch vor dem Problem dass ich die Schalterstellungen auslesen muß und das dann an der LED Platine anzeigen will.
Kann ich da die Variable die Wire.read zurückgibt irgendwie auslesen und dann ein einzelnes Bit weiterverarbeiten.
Ähnlich dann auch bei der Übertragung zur Led-Platine -> lässt sich da auch nur ein einzelnes Bit ändern und dann alles übertragen.

Grüße Rudi

Das mit der 7-Bit Adresse habe ich auch gelesen. Aber mit der Adresseingabe 0X23 funktionierts keine Ahnung wiso.
Meine Schalterplatine hat jetzt 0X23 und die LEDplatine 0X25.

Das funktioniert, weil die Wire-Bibliothek 7-bit-Adressen erwartet. Ich habe ja geschrieben, dass nur Deine selbstgebastelte Version nicht klappen wird, grundsätzlich ist die Adresse schon legitim.

Kann ich da die Variable die Wire.read zurückgibt irgendwie auslesen und dann ein einzelnes Bit weiterverarbeiten.

Ja.

int8_t readBit(uint8_t addr, uint8_t bitno) {
  if (Wire.requestFrom(addr, 1)) {
    uint8_t val = Wire.read();
    if (val & (0x01 << bitno)) {
      return 1;
    } else {
      return 0;
    }
  }
  return -1;
}

Ähnlich dann auch bei der Übertragung zur Led-Platine -> lässt sich da auch nur ein einzelnes Bit ändern und dann alles übertragen.

Auf die selbe Art und Weise lässt sich auch das Schreiben implementieren. Dazu musst Du aber eine globale Variable definieren, die den ganzen Byte-wert hält, denn aus dem Chip auslesen kannst Du ihn ja nicht.

pylon:
Dieser Chip ist relativ einfach gestrickt. Die I/O-Pins werden bei einem Schreibzugriff als Ausgang beschaltet, bei einem Lesezugriff sind sie Eingang. Da Du nur entweder ein ganzes Byte schreiben oder ein ganzes Byte lesen kannst, kannst Du nicht einen Teil der Pins als Eingang und gleichzeitig einen Teil als Ausgang verwenden.

Verwende einen anderen Chip, wenn Du solche Konfigurationen machen willst.

Geht doch. Der Chip ist einfach aber sehr flexibel..

Ganz kurz: Logisch 1 auf den zu leseden i/o-Pin ausgeben, dann kann man 0- oder 1-Pegel dort lesen. Wurde eine "0" zuvor auf dem Pin ausgegeben, wird der Chip diese Lesen. Wird dem Pin dann ein "1"-Pegel von außen zugeführt, wird dies schlimmstenfalls zu Gestank führen. Also: High schreiben und alles wird gut... Achja, beim Schreiben mit anschließendem Lesen sollte eine kurze Pause vor dem Lesen eingelegt werden, damit sich die Pegel Einschwingen (ist jetzt mal geraten), da sonst falsche Werte gelesen werden können. Beim auslesen einer Tastenmatrix musste ich 2ms delay vor dem Lesen einfügen, da sonst ein Bit kippte. Alle anderen waren ok ..Wunderbare welt...

Ganz lang: :wink:

Aktuelles Datenblatt : Suchmaschine nach "Document identifier: PCF8574_PCF8574A" suchen lassen.
Bei der Suche nach "PCF8574" findet man leider nur die alten Dokumente (fatal für Neullinge), sogar bei NXP :-(. Ein Fehler in einer Abbildung des alten Datenblattes behauptet, was du schreibst: Es gibt keinen Eingang der lesbar wäre. Dem ist bekanntermaßen ja nicht so...

Ist zwar schon etwas her, aber da immer wieder Leute den Chip neu kennenlernen, hier mal fast genau lt. Datenblatt (vom 27 May 2013):

"The port must have been previously written to logic 1, which is the condition after power-on reset. To enter the Read mode the master (microcontroller) addresses the slave device and sets the last bit of the address byte to logic 1 (address byte read). The slave will acknowledge and then send the data byte to the master. The master will NACK and then send the STOP condition or ACK and read the input register again. The read of any pin being used as an output will indicate HIGH or LOW depending on the actual state of the pin. If the data on the input port changes faster than the master can read, this data may be lost."

Der Chip ist so aufgebaut, dass jeder I/O-Pin der logisch 1 führt, diesen nur mit einem Strom von max. 300uA treibt. Legt man eine Spannung von 0 Volt an, werden max. 300uA fliessen und logisch 0 wird beim Lesen des Chip korrekt auf dem Bus übertragen.

Ganz korrekt ist, dass beim schreiben einer "1" der i/o-Pin KURZZEITIG mit max. 1mA getrieben wird, damit er Leitungskapazitäten überwinden kann und der Pegel schnell auf High-Pegel gebracht wird. Danach jedoch, wird die Spannung nur noch mit max. 300uA gehalten.

Bsp.: Low-Level wird erkannt ab maximal 0,7*Versorgungsspannung, bei 5V ist also alles unter 3,5Volt Low-Level beim Lesen. Dieser Pegel wird also sicher erreicht mit einem Widerstand von R(gnd)=3,5v/300uA=11,6kOhm oder kleiner.

Anders verhält es sich bei der Ausgabe von logisch 0. Jeder i/o kann mindestens 10mA treiben, im Mittel 25mA und ein max ist im Datenblatt nicht angegeben - also gibt es vermutlich eine Rauchwolke, wenn der Pin "0"-Volt führt und man Ihn ohne oder zu kleinem Vorwiderstand auf die Versorgungsspannung zieht.

Auch 8 LED mit mehr als 10mA pro LED sollten nicht angeschlossen werden, da der Chip max. 80mA nach GND abführen kann - es sei denn, es sind nie mehr als 4 LED eingeschaltet, dann sind 20mA erlaubt (max.25mA pro i/o, max. 80mA gesamt). Vieles geht zwar dennoch, ist aber außerhalb der Spezifikation und die Funktion dann eben nicht garantiert.

Hoffe ich habe nicht mehr verwirrt als geholfen ::slight_smile:

Hättest besser einen MCP23017 genommen. Der macht mit 16 GPIO genau das, was du mit deinen 2 Chips jetzt machst und hat eine bequeme Lib, da kann man dann z.B. mcp.digitalWrite(2, HIGH) usw. machen. Schnell und einsteigerfreundlich.

ElEspanol:
Hättest besser einen MCP23017 genommen. Der macht mit 16 GPIO genau das, was du mit deinen 2 Chips jetzt machst und hat eine bequeme Lib, da kann man dann z.B. mcp.digitalWrite(2, HIGH) usw. machen. Schnell und einsteigerfreundlich.

Nur dass der MCP23017 um einiges größer ist.

Und der 8574 kann es ja auch, wenn er richtig angesteuert wird.