I2C - PCF8574 Portexpander

Hallo,

mit Wire.h werden doch nur 7 Bits für die Device Adresse übertragen. Nur gibt es doch noch das ominöse R/W Bit was Bit7 (das 8.) sein soll. Was hat es damit auf sich wenn es nie genutzt und auch nicht übertragen werden kann?

Und warum lassen viele Programmierer beim lesen den Adressierungsbefehl weg?
Wire.beginTransmission(I2C_Adresse);

Womit ich auch noch nicht so recht klar komme ist, dass ein Port-Direction Register fehlt.
Wenn ich jetzt 1111 0000 schreibe, dann sind für mich alles Ausgänge mit H oder L Pegel. Wenn ich dann lese erhalte ich wieder 1111 0000. Soweit noch okay, sind immer noch LEDs angeschlossen.
Wenn ich aber einen Pin der Ausgang Low hat, fest auf H legen würde, dann müßte das doch im IC einen Kurschluss geben? Denn der interne Transistor zieht das Signal immer noch auf Masse.

Kennst sich jemand mit dem Teil aus?

/*
Arduino Mega2560
PCF8574 - 8bit Portexpander, I2C Adressbereich A6-A3 fest auf b0100
PCF8574 - 8bit Portexpander, I2C Adressbereich von 0x20  =  B?010 0000
                                               bis 0x27  =  B?010 0111 )
PCF8574 - max. I2C Takt = 100kHz                          Bit7 ist R/W
*/

#include <Wire.h>
#define PCF8574  0x20   // PCF8574 Adresse, A2-A0 auf Masse


void setup()
{
  Wire.begin();
  Serial.begin(57600);
  
}

void loop()
{
  schreiben_PCF8574(PCF8574, 0xAA);
  Serial.println( lesen_PCF8574(PCF8574), BIN );
  delay(500);
  schreiben_PCF8574(PCF8574, 0x00);
  Serial.println( lesen_PCF8574(PCF8574), BIN );
  delay(500);     
}


void schreiben_PCF8574(int I2C_Adresse, byte data)
{
  Wire.beginTransmission(I2C_Adresse);          // Connect
  Wire.write(data);                             // data Byte senden
  Wire.endTransmission();                       // Disconnect
  delay(1);
}  
    

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  Wire.beginTransmission(I2C_Adresse);        // Connect
  Wire.requestFrom(I2C_Adresse, 1);           // ein Byte (8 Bit) anfordern
  if (Wire.available()) data = Wire.read();   // Daten vorhanden?
  Wire.endTransmission(true);                 // Disconnect
  return data; 
  delay(1);
}

mit Wire.h werden doch nur 7 Bits für die Device Adresse übertragen.

Nein, es wird ein ganzes Byte übertragen, aber der Parameter ist als 7-bit-Wert definiert.

Und warum lassen viele Programmierer beim lesen den Adressierungsbefehl weg?
Wire.beginTransmission(I2C_Adresse);

Weil das nicht der Adressierungsbefehl ist, sondern der Beginn einer Schreibanweisung.

Womit ich auch noch nicht so recht klar komme ist, dass ein Port-Direction Register fehlt.
Wenn ich jetzt 1111 0000 schreibe, dann sind für mich alles Ausgänge mit H oder L Pegel. Wenn ich dann lese erhalte ich wieder 1111 0000. Soweit noch okay, sind immer noch LEDs angeschlossen.
Wenn ich aber einen Pin der Ausgang Low hat, fest auf H legen würde, dann müßte das doch im IC einen Kurschluss geben? Denn der interne Transistor zieht das Signal immer noch auf Masse.

Du kannst nicht die Hälfte der Pins lesend haben und die anderen schreibend. Es sind etweder immer alle Ausgänge (wenn Du einen Schreibbefehl schickst) oder alle Eingänge (wenn Du liest). Ein interner Kurzschluss entsteht nicht, aber möglicherweise ein externer, wenn Du die gleichen Pins für's Lesen und Schreiben verwenden willst.

Hallo,

das R/W Bit verwirft die Wire.h Library selbstständig?

Wire.beginTransmission(I2C_Adresse);
Man muß doch zuerst dem Bus mitteilen, dass man sich mit Gerät yxz verbinden möchte. Ich denke das ist in beiden Richtungen notwendig? Also kurz inne gehalten. Obiger Befehl ist nur beim schreiben notwendig, weil danach direkt das zu schreibende Byte folgt. Beim lesen hat man den Befehl Wire.requestFrom wo man die I2C ADresse eh mit angibt. So korrekt?

Externer Kurzschluss dürfte nicht entstehen, weil ich keinen Taster o.ä. und einen Verbraucher gleichzeitig an einen Pin klemmen wollte. Macht man ja eh nicht. Mir ging es darum, falls man den Portexpander falsch "konfiguriert", dass dann nichts kaputt geht. Falls man zum Bsp. einen Pin Low beschreibt und dann mittels Taster einen High Pegel drauf gibt.
Man kann demnach bei dem IC nicht jeden Pins einzeln auf Ausgang oder Eingang schalten? Entweder alles oder nichts? Das ist blöd. Seinen letzten Ausgangszustand scheint er jedoch zubehalten wenn man wieder einliest nach einem Schreibbefehl?
Das Ding ist so einfach das es schon wieder kompliziert ist. :wink:

das R/W Bit verwirft die Wire.h Library selbstständig?

Nein, normalerweise erzeugt sie es (wenn sie Master ist) oder verwendet es, um die entsprechende Reaktion auszulösen (im Slave-Fall).

Man muß doch zuerst dem Bus mitteilen, dass man sich mit Gerät yxz verbinden möchte. Ich denke das ist in beiden Richtungen notwendig? Also kurz inne gehalten. Obiger Befehl ist nur beim schreiben notwendig, weil danach direkt das zu schreibende Byte folgt. Beim lesen hat man den Befehl Wire.requestFrom wo man die I2C ADresse eh mit angibt. So korrekt?

Das ist soweit korrekt. Das Schreiben erfolgt erst beim Wire.endTransmission(). Alles vorangegangene wird nur in einem internen Puffer gespeichert (der übrigens nur 32 Byte gross ist). Deshalb sollte der Rückgabewert von endTransmission() immer geprüft werden.

Falls man zum Bsp. einen Pin Low beschreibt und dann mittels Taster einen High Pegel drauf gibt.

Das darfst Du nicht, da müsstest Du erst einen Lese-Befehl absetzen, damit die Richtung umgeschaltet wird.

Seinen letzten Ausgangszustand scheint er jedoch zubehalten wenn man wieder einliest nach einem Schreibbefehl?

Nein. Sobald Du liest, wird der Ausgang zu einem Eingang und Du bekommst den Zustand des Eingangs zurück.

Das Ding ist so einfach das es schon wieder kompliziert ist.

Das unterschreibe ich.

Wenn ich jetz eine LED mit der Kathode an den PCF anschließe, die Annode mit Vorwiederstand auf 5V lege und dann via write-Befehl den Pin des PCFs, an dem die LED angeschlossen ist, auf LOW setze, kann ich damit prima die LED an/aus schalten ohne das was passiert.

Ist das jetzt der falsche Weg und ich muss nach dem Schreiben den request-Befehl ausführen, oder habe ich da irgendwas kommplett falsch verstanden.
Bin gerade total verwirrt :~

Habe 5 PCF8574AP

Das scheint korrekt zu sein. requestFrom() ist dazu da um Daten vom einem Device zu lesen.

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  Wire.beginTransmission(I2C_Adresse);        // Connect
  Wire.requestFrom(I2C_Adresse, 1);           // ein Byte (8 Bit) anfordern
  if (Wire.available()) data = Wire.read();   // Daten vorhanden?
  Wire.endTransmission(true);                 // Disconnect
  return data; 
  delay(1);
}

In diesem Fall kann man sich das beginTransmission()/endTransmission() komplett sparen. Siehe hier:

Bei Devices die mehr als ein Register haben, kommt nach beginTransmission() die Register-Adresse mit write(). Danach gleich endTransmission() und erst dann requestFrom() und read(). z.B. für eine RTC:

bool DS1307RTC::read(tmElements_t &tm)
{
  uint8_t sec;
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.write((uint8_t)0x00); 
  if (Wire.endTransmission() != 0) 
    return false;

  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
  if (Wire.available() < tmNbrFields) 
	  return false;

  sec = Wire.read();
  ...
  ...
  return true;
}

Hier wird nach beginTransmission() das Register 0 adressiert, damit die read() Anweisungen von da Anfangen. Dieser Port-Expander hat nur das eine Register also braucht man das nicht. Aber endTransmission() schließt nur beginTransmission() ab! Nicht requestFrom()!

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  Wire.beginTransmission(I2C_Adresse);        // Connect
  Wire.requestFrom(I2C_Adresse, 1);           // ein Byte (8 Bit) anfordern
  if (Wire.available()) data = Wire.read();   // Daten vorhanden?
  Wire.endTransmission(true);                 // Disconnect
  return data; 
  delay(1);
}

Das Problem dieser Implementation ist, dass hier vom Chip gelesen wird, also alle Pins als Eingänge geschaltet werden und gleich danach wieder geschrieben wird, womit alle Pins wieder Ausgänge sind. Da beim Schreibzugriff keine Daten übertragen werden, ist der Zustand der Ausgänge möglicherweise nicht definiert (oder der alte Zustand), so genau habe ich das Datenblatt nicht studiert, es ist auf jeden Fall nicht die Art und Weise, wie der Chip angesteuert werden soll.

Diese Methode sollte so aussehen:

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  if (Wire.requestFrom(I2C_Adresse, 1)) data = Wire.read();           // ein Byte (8 Bit) anfordern
  return data; 
}

Wenn ich jetz eine LED mit der Kathode an den PCF anschließe, die Annode mit Vorwiederstand auf 5V lege und dann via write-Befehl den Pin des PCFs, an dem die LED angeschlossen ist, auf LOW setze, kann ich damit prima die LED an/aus schalten ohne das was passiert.

Ja, natürlich, da mit einem Schreibbefehl die Pins als Ausgänge geschaltet werden und das übertragene Byte die entsprechenden Pins auf Masse oder 5V setzen.
Der Chip ist wirklich sehr einfach gestrickt. Wird gelesen, werden die Pins Eingänge und der gelesene Wert wird zurückgeliefert. Wird geschrieben, werden die Pins Ausgänge und repräsentieren den geschriebenen Wert als Bits. Eine Kombination, wo der Chip sowohl gelesen wie auch geschrieben wird, sollte vermieden werden, dafür würde ich einen richtigen I2C-IO-Expander nehmen. Der von Dir skizzierte Fall wo Du per Schreibbefehl eine 1 für einen Pin schickst und dann ein Taster gedrückt wird, der den Pin nach Masse zieht, ruiniert den Chip, falls das Masse-Ziehen nicht über einen genügend grossen Widerstand realisiert ist.

Hallo,

@ Addi: was ich sicher sagen kann ist, dass Du nichts falsch gemacht hast. Wenn Du LEDs angeschlossen hast und den PCF8574 nur beschreibst, dann ist alles richtig. Selbst wenn Du lesen solltest, dürfte nichts kaputt gehen, weil Du für Deine LEDs sowieso Vorwiderstände hast. Du mußt nur aufpassen mit der Adressierung, weil Deine ein A nach der IC Ziffer haben. Das wirst Du sicherlich schon wissen.

@ all:
So, zurück zu meinem Problem. :wink:
Wenn ich lese, würde folgender Code ausreichen für den PCF8574?
Mit dem Wire.available()) bin ich mir nicht sicher ob das sein muß. Laut Beschreibung vom Befehl soll man das verwenden. Nur wenn man die Klammer leer läßt, ist das doch auch irgendwie sinnlos?

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  Wire.requestFrom(I2C_Adresse, 1);           // ein Byte (8 Bit) anfordern
  if (Wire.available()) data = Wire.read();   // Daten vorhanden?
  return data; 
  delay(4);
}

und das schreiben haut so hin?

void schreiben_PCF8574(int I2C_Adresse, byte data)
{
  Wire.beginTransmission(I2C_Adresse);          // Connect
  Wire.write(data);                             // data Byte senden
  Wire.endTransmission();                       // Disconnect
  delay(1);
}

Nachfragen.
Wo siehst Du unten in meiner letzten Antwort einen Schreibbefehl nach dem ersten lesen? Ich habe kein Wire.write eingebaut? Oder "schreibt" der Wire.beginTransmission Befehl? Ich denke der weckt/aktiviert nur das gewünschte Device am Bus.

Womit ich jetzt aber wieder aus dem Tritt komme, war gerade wieder drin, ist das Bsp. mit dem RTC. Bei dem benötigt man seltsamerweise wieder Wire.beginTransmission und Wire.endTransmission für das lesen vom Device? Jetzt sagt mir bitte nicht, weil beim RTC mehrere Register im Spiel sind die ich gezielt ansprechen muß zum auslesen.

Als ich vor paar Tagen noch unbekümmert rumgespielt habe mit dem lesen nach schreiben erhielt ich immer genau die gleichen Zustände die ich vorher ausgegeben hatte. Ich habe nur LEDs dran jeweils mit Transistor. Am IC ist also nur die Basis eines Transistor dran. Da fließen jeweils weiter unter 1mA. Nun kann ja die Transistor-Basis rückwärts keine Spannung am IC anlegen. Vielleicht ist das Zufall durch irgendwelche unbekannten Kapazitäten?

Was ich jetzt verstanden habe ist, dass man den IC entweder komplett als Eingang verwendet oder komplett für Ausgänge. Wenn er praktisch aufgebaut und fest verdrahtet ist, kann man ja nichts mehr ändern. Also wenn man mehr Eingänge und mehr Ausgänge bräuchte, müßte man 2 davon nehmen.

Edit:
habe das mit dem lesen nach schreiben nochmal probiert. Ist lustig. Folgender Code für ein Lauflicht.

/*
Arduino Mega2560
PCF8574 - 8bit Portexpander, I2C Adressbereich A6-A3 fest auf b0100
PCF8574 - 8bit Portexpander, I2C Adressbereich von 0x20  =  B?010 0000
                                               bis 0x27  =  B?010 0111 )
PCF8574 - max. I2C Takt = 100kHz                          Bit7 ist R/W
*/

#include <Wire.h>
#define PCF8574  0x20   // PCF8574 Adresse, A2-A0 auf Masse


void setup()
{
  Wire.begin();
  Serial.begin(57600);
  
}

void loop()
{
  for (int i=1; i<129;)
    {
     schreiben_PCF8574(PCF8574, i);
     Serial.println(lesen_PCF8574(PCF8574), BIN);
     delay(200);
     i=i*2;
    }  
}


void schreiben_PCF8574(int I2C_Adresse, byte data)
{
  Wire.beginTransmission(I2C_Adresse);          // Connect
  Wire.write(data);                             // data Byte senden
  Wire.endTransmission();                       // Disconnect
  delay(1);
}  
    

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  Wire.requestFrom(I2C_Adresse, 1);           // ein Byte (8 Bit) anfordern
  if (Wire.available()) data = Wire.read();   // Daten vorhanden?
  return data; 
  delay(1);
}

und erhalte endlos im seriellen Monitor: :smiley:

1
10
100
1000
10000
100000
1000000
10000000

Doc_Arduino:
Mit dem Wire.available()) bin ich mir nicht sicher ob das sein muß. Laut Beschreibung vom Befehl soll man das verwenden. Nur wenn man die Klammer leer läßt, ist das doch auch irgendwie sinnlos?

Das ist das gleiche wie if(Wire.available() > 0). available() gibt die Anzahl der Bytes zurück. Alles was ungleich 0 ist, ist "true"

Womit ich jetzt aber wieder aus dem Tritt komme, war gerade wieder drin, ist das Bsp. mit dem RTC. Bei dem benötigt man seltsamerweise wieder Wire.beginTransmission und Wire.endTransmission für das lesen vom Device? Jetzt sagt mir bitte nicht, weil beim RTC mehrere Register im Spiel sind die ich gezielt ansprechen muß zum auslesen.

Ja, es liegt daran dass es mehrere Register gibt. Jedes read() inkrementiert automatisch den internen Zeiger auf das aktuelle Register. Deshalb muss man den Zeiger der RTC einmal auf einen Start-Wert setzen.

Das ist auch aus dem Datenblatt ersichtlich. Das sind die Sequenzen der Befehle angegeben.

Also wenn man mehr Eingänge und mehr Ausgänge bräuchte, müßte man 2 davon nehmen.

Oder einen MCP23017:

Der hat zwei getrennte I/O Ports

Wenn ich lese, würde folgender Code ausreichen für den PCF8574?

Er würde ausreichen, wäre aber immer noch falsch. Zum einen wird der Rückgabewert von requestFrom() nicht geprüft und zum anderen ist da ein völlig unnützes delay(). Was ist an meinem Code so unschön, dass er für Dich nicht geht?

Nur wenn man die Klammer leer läßt, ist das doch auch irgendwie sinnlos?

Welche Klammer?

und das schreiben haut so hin?

Ja, aber auch hier ein völlig unnötiges delay().

Wo siehst Du unten in meiner letzten Antwort einen Schreibbefehl nach dem ersten lesen? Ich habe kein Wire.write eingebaut? Oder "schreibt" der Wire.beginTransmission Befehl? Ich denke der weckt/aktiviert nur das gewünschte Device am Bus.

Hast Du meine vorletzte Antwort gelesen? Dort erkläre ich das. Wire.endTransmission() schreibt etwas auf den Bus, Wire.beginTransmission() und Wire.write() schreiben nur in einen Puffer.

Womit ich jetzt aber wieder aus dem Tritt komme, war gerade wieder drin, ist das Bsp. mit dem RTC. Bei dem benötigt man seltsamerweise wieder Wire.beginTransmission und Wire.endTransmission für das lesen vom Device? Jetzt sagt mir bitte nicht, weil beim RTC mehrere Register im Spiel sind die ich gezielt ansprechen muß zum auslesen.

Doch, so in etwa kommt das hin. Da dort mehrere Register vorhanden sind, musst Du ja sagen, von welchem Register-Index an gelesen werden soll. Üblicherweise geschieht das, indem man diesen Index (wird auch Adresse genannt) schreibend übergibt. Erst dann kannst Du den Lesebefehl absetzen. Rein theoretisch ginge das auch ohne, aber dann müsste man immer alle Register von Anfang an lesen bis zu jenem, das eigentlich von Interesse ist. Die meisten mir bekannten Bausteine wählen den ersten Weg.

Nun kann ja die Transistor-Basis rückwärts keine Spannung am IC anlegen. Vielleicht ist das Zufall durch irgendwelche unbekannten Kapazitäten?

Der Transistor selbst hat schon eine Kapazität, die Leitung dorthin auch. Kann durchaus sein, dass das zum Resultat führte. Es kann auch sein, dass nach einem Schreibzugriff der erste Lesezugriff bei einem floatenden Pin den alten Ausgabezustand zurückliefert, das Datenblatt definiert das nicht so genau.

Also wenn man mehr Eingänge und mehr Ausgänge bräuchte, müßte man 2 davon nehmen.

Oder besser einen anderen Chip. Dieser hat den Vorteil, dass die Ausgabe und das Einlesen mit maximaler I2C-Geschwindigkeit stattfinden kann, ansonsten sehe ich eigentlich fast nur Nachteile.

Hallo,

habt Nacht's auch langeweile ... :wink:

Mir qualmt gerade die Birne.

Das mit der leeren Klammer hat Serenifly schon erklärt. Nur entsteht dadurch wieder eine Frage, was ich auch an Deinem Code pylon noch nachgefragt hätte. Andere Bsp. Codes im Netz machen das seltsamerweise genauso.

Wire.request und wire.available liefern ein true oder false zurück. Okay. Aber das wird überhaupt nicht abgefragt.
Ich lese nur immer if(wire. ... () ) mach was;
Ja gut, nur wenn was?
Fehlt da nicht noch ein if (wire. ... () == true) mach was; ?

Der Rest ist soweit erstmal klar.

Wegen dem delay. Ja das ist so eine Sache. Wenn ich das IC alleine betreibe ist kein delay notwendig. Richtig. Baue ich das Ding und damit den Code spaßenshalber in mein großes Projekt ein, womit das Lauflicht mit max. Speed vom Code läuft, sozusagen die (Nicht)Beschäftigungsanzeige vom µC, dann funktioniert meine RTC DS3231 nicht mehr. Mit einem delay(4) beim Portexpander funktioniert wieder alles. Fehlt das delay, kann die RTC scheinbar nicht mehr angesprochen werden.

Wire.request und wire.available liefern ein true oder false zurück. Okay. Aber das wird überhaupt nicht abgefragt.
Ich lese nur immer if(wire. ... () ) mach was;
Ja gut, nur wenn was?
Fehlt da nicht noch ein if (wire. ... () == true) mach was; ?

Genau genommen liefern beide nicht true oder false zurück, sondern ganze Zahlen (ersteres die empfangenen Bytes, zweiteres die Anzahl Bytes im Puffer). Die Klammer der "if"-Anweisung prüft, ob der Inhalt nach boolean konvertiert true oder false liefert. Ganze Zahlen (Integer) werden nun standardmässig so konvertiert, dass eine Null zu false wird, alles andere zu true. Der Ausdruck prüft also ob überhaupt Bytes empfangen wurden.

Wegen dem delay. Ja das ist so eine Sache. Wenn ich das IC alleine betreibe ist kein delay notwendig. Richtig. Baue ich das Ding und damit den Code spaßenshalber in mein großes Projekt ein, womit das Lauflicht mit max. Speed vom Code läuft, sozusagen die (Nicht)Beschäftigungsanzeige vom µC, dann funktioniert meine RTC DS3231 nicht mehr. Mit einem delay(4) beim Portexpander funktioniert wieder alles. Fehlt das delay, kann die RTC scheinbar nicht mehr angesprochen werden.

Dann ist mit grosser Wahrscheinlichkeit Deine Stromversorgung nicht in Ordnung. Wahrscheinlich braucht die Beleuchtung zuviel Strom, die Spannung sackt ein, der DS verweigert seinen Dienst (also genau genommen nur die Kommunikation). Unter Umständen genügt ein Kondensator an der richtigen Stelle, aber mit dem delay() lässt Du den Prozessor im Grunde nur warten, bis wieder genügend Spannung aufgebaut ist.

Hallo,

naja okay. Mir fehlt aber immer noch in der if Anweisung der Vergleich mit dem Rückgabewert true/false. Ich meine, das true/false kommt doch nicht von if zurück, sondern von den Wire.request oder wire.available oder so. In der if Anweisung muß doch dann laut meiner Meinung dieser Rückgabewert mit einer Vorgabe verglichen werden.

Weiter zurück hat Serenifly zu Wire.available()) mal geschrieben:
Das ist das gleiche wie if(Wire.available() > 0). available() gibt die Anzahl der Bytes zurück. Alles was ungleich 0 ist, ist "true"

Muß ich mir das jetzt wie eine verkürzte Schreibweise vorstellen? Wenn man in einer if Anweisung den Vergleich leer läßt, wird immer auf > 0 geprüft?

Wegen dem delay. Ohne delay streikt sogar mein MAX6675 am SPI Bus. Das Display am SPI Bus funktioniert jedoch. Die Dallas am OneWire Bus funktionieren auch. Die SD-Karte am SPI Bus funktioniert auch. Nur der RTC DS3231 am I2C und der MAX6675 am SPI spinnen rum. Das Steckbrett mit zusätzliche Kondensatoren "zugepflastert" Elkos und Tantal hilft auch nicht. Das stecken 2er zusätzlichen PullUp Widerstände auf den I2C half nur einmal ganz kurz. Das Steckbrett versorge ich vom Arduino aus. Vielleicht doch zu viele Steckkontakte mittlerweile. Ich werde wohl doch mal eine externe Spannungsversorgung ranbauen müssen.
Sobald ein I2C Device den Bus freigibt, kann sofort der nächste angesprochen werden? Die Größe der PullUp Widerstände "in Summe" sind jetzt 2,4kOhm. Je zwei 4,7k parallel. Einmal bei der RTC direkt und einmal am Portexpander. Das sollte eigentlich für 100kHz locker ausreichen.

Muß ich mir das jetzt wie eine verkürzte Schreibweise vorstellen? Wenn man in einer if Anweisung den Vergleich leer läßt, wird immer auf > 0 geprüft?

Wenn die Funktion einen Integer-Wert zurückgibt, ja, dann kannst Du's Dir so vorstellen, allerdings nicht >0 sondern != 0. Wenn die Funktion schon einen Boolean-Wert zurückgibt erübrigt sich die Sache (hoffentlich in Deiner Vorstellung auch) und bei Pointern ergibt sich ein != NULL.

Sobald ein I2C Device den Bus freigibt, kann sofort der nächste angesprochen werden?

Ja, wobei eigentlich der Master den Bus kontrolliert, vom Spezialfall Clock-Stretching mal abgesehen.

Die Größe der PullUp Widerstände "in Summe" sind jetzt 2,4kOhm.

Die sind zu klein, da wird das Strom-Limit der I2C-Spezifikation überschritten (wenn sie an 5V hängen).

Einmal bei der RTC direkt und einmal am Portexpander. Das sollte eigentlich für 100kHz locker ausreichen.

Das hängt nicht nur von der Frequenz ab, sondern auch von der Bus-Kapazität und diese wiederum von den angehängten Devices und von der Bus-Länge. Bei 5V solltest Du nicht unter 2k7 gehen, sonst droht etwas heikleren Chips der Stromtod.

Hallo,

mir ist das jetzt erstmal alles klar gewurden. Danke Euch. Falls noch Fragen auftauchen sollten, ich weis ja wo ich Euch finde ... :wink: Das mit den 2. Paar Buswiderständen war nur ein Test. Ich bau erstmal eine extra Spannungsversorgung ran.

Soweit so gut. Danke.

Hallo,

habe mich heute nochmal mit I2C beschäftigt. Man kann zwar vielen fragen und lesen, verstehen kommt erst später mit probieren.

Also. Wenn ein I2C Device nur ein Register hat, üblicherweise 0x00, dann sind diese Zeilen ausreichend?

byte lesen_PCF8574(int I2C_Adresse)
{
  byte data = 0xFF;
  if (Wire.requestFrom(I2C_Adresse, 1)) data = Wire.read();           // ein Byte (8 Bit) anfordern
  return data; 
}

Wenn ein I2C Device mehrere Register hat, zum Bsp. RTC DS3231, dann muß man mit Wire.beginTransmission() arbeiten, wenn man mittendrin und nicht unbedingt bei Register 0x00 anfangen möchte zu lesen? Das Wire.available() kann man weiterhin weglassen? Scheint ja zu funktionieren. So korrekt?

 Wire.beginTransmission(ds3231_adress);   // Connect
 Wire.write(0x04);                        // Anfrage der Register Nummer
 Wire.endTransmission();                  // Connect fertig
 Wire.requestFrom(ds3231_adress, 3);      // Wieviel Bytes in Folge auslesen?
  
 byte Tag = Wire.read();                  // die 3 Bytes hintereinander auslesen, BCD Format
 byte Monat = Wire.read();
 byte Jahr = Wire.read();

Ich denke, dass dabei ein großes Problem sein wird, dass wenn du eine falsche Adresse übergibst
auch keine Daten in den Bus geschrieben werden. Wenn du dann ohne zu überprüfen, ob überhaupt Daten da sind, ließt
und keine da sind, dass es dann zu ziehmlich komischen Dingen kommen kann.

Wenn ein I2C Device mehrere Register hat, zum Bsp. RTC DS3231, dann muß man mit Wire.beginTransmission() arbeiten, wenn man mittendrin und nicht unbedingt bei Register 0x00 anfangen möchte zu lesen? Das Wire.available() kann man weiterhin weglassen? Scheint ja zu funktionieren. So korrekt?

Du kannst das Wire.available() schon weglassen, dann solltest Du aber den Rückgabewert von Wire.requestFrom() prüfen. Allerdings prüfst Du den Rückgabewert von Wire.endTransmission() auch nicht, also scheinst Du Dir keine Sorgen über mögliche Fehler zu machen. Wenn Die Hardware genau macht, was Du von ihr erwartest, dann funktioniert Dein Code. Wenn aber nur das geringste schief geht, dann kommt es zu Fehlfunktionen und dann kann es sich rächen, dass Du die Fehlerzustände nicht mal kontrollierst, geschweige denn darauf reagierst.

Ich habe den RTC Code zugegebenermaßen im diese Fehler-Behandlungen gekürzt. Wird vielleicht nicht allen Libs gemacht, aber bei dieser (ich glaube die als Zusatz für Time.h) wird beides gemacht. Der Rückgabe-Wert von endTransmission() und bei read() wird nach requestFrom() zusätzlich noch available() geprüft

Hallo,

zugegebenermaßen bin ich unbedarft rangegangen. Also mach ich nochmal einen Reset.

Dazu gleich eine Frage. Warum wird dann beim lesen so ein Aufriss gemacht mit der Überprüfung, dass da ja nichts schief geht, jedoch beim schreiben wird nichts überprüft? Wenn ich etwas lesen möchte, was soll ich da prüfen? Ich weis doch nicht was drin steht, also kann ich nicht überprüfen ob der Datensatz den ich erhalte der ist der wirklich drin steht, in der Speicherzelle. Wenn ich etwas schreibe, dann könnte ich mittels lesen im Anschluss überprüfen ob das übereinstimmt. Also ob es noch das ist was ich geschrieben hatte. Wird jedoch so nicht gemacht. Ich hoffe ihr versteht meine Gedankengänge.

Also fangen wir nochmal von vorn an. Gehen wir mal weg vom blöden PortExpander und gehen zu einem EEProm.

schreiben wie folgt:

void schreibeEEprom(int I2C_Adresse, unsigned int SpeicherAdresse, byte data)
{
  Wire.beginTransmission(I2C_Adresse);          // Connect
  Wire.write( (byte)(SpeicherAdresse >> 8)   ); // MSB
  Wire.write( (byte)(SpeicherAdresse & 0xFF) ); // LSB
  Wire.write(data);                             // data Byte zum speichern senden
  Wire.endTransmission();                       // Disconnect
}

lesen wie folgt:

byte leseEEprom(int I2C_Adresse, unsigned int SpeicherAdresse)
{
  byte data = 0xFF;
  Wire.beginTransmission(I2C_Adresse);           // Connect
  Wire.write( (byte)(SpeicherAdresse >> 8)   );  // MSB
  Wire.write( (byte)(SpeicherAdresse & 0xFF) );  // LSB
  Wire.endTransmission();                        // Disconnect
  Wire.requestFrom(I2C_Adresse, 1);              // Daten vom Slave anfordern, hier 1 Byte
  if (Wire.available()) data = Wire.read();      // wenn Daten vorhanden, diese lesen
  return data;
}

wenn ich jetzt mehrere Bytes hintereinander lesen möchte, ist dann folgendes richtig oder sollte ich obige Funktion einfach mehrfach hintereinander aufrufen? Das hier würde dann beim auslesen der RTC auch schneller sein, weil man sich das ständige neu ansprechen des I2C Device auf dem Bus erspart.

  Wire.beginTransmission(I2C_Adresse);           // Connect
  Wire.write( (byte)(SpeicherAdresse >> 8)   );  // MSB
  Wire.write( (byte)(SpeicherAdresse & 0xFF) );  // LSB
  Wire.endTransmission();                        // Disconnect
  Wire.requestFrom(I2C_Adresse, 3);              // Daten vom Slave anfordern, hier 3 Byte
  if (Wire.available())                          // wenn Daten vorhanden, diese lesen
    {                                     
     data_1 = Wire.read();  
     data_2 = Wire.read();  
     data_3 = Wire.read();  
    }                                    
}

Und wenn ich die Wire Transmission auch noch sicher machen möchte, muß ich dann
Wire.endTransmission(true); // Disconnect
schreiben?