I2C PCF8591 Prinzip der Datenabfrage

Hallo Leute,

im Zuge meiner Modulerstellung habe ich auch den A/D-Wandler des PCF8591 erfolgreich eingebunden.
In der Regel hat dieser die Device-Adresse 0x48. Das Register 40 muss beschrieben werden um den einen D/A-Wandler zu setzen.
In den Registern 40 bis 43 können die vier A/D-Wandler abgefragt werden. Dieses geschieht in den zahlreichen Beispielen so, dass der jeweiligen Eingang zweimal hintereinander lesend abgefragt wird. Erklärung ist: Beim ersten mal lesen wird der PCF8591 „aufgefordert“ die neuen Daten bereitzustellen, beim zweiten lesen stehen dann die aktuellen Daten in den Registern.
Wie geschrieben - das funktioniert so weit auch.

Jedoch dachte ich mir, dass sind acht Datenabfragen für vier Ergebnisse. So kam ich auf die Idee zwei mal in einem Befehl die Register 40-43 im Block abzufragen - m.E. die bessere Lösung, da nur zwei Mal Daten transferiert werden. Bei anderen Sensoren, z.B. dem BME280 funktioniert das auch, nur habe ich es beim PCF8591 so nicht hinbekommen. Es erscheint so, als ob die Daten eines Ausganges „vervierfacht“ werden.

Kann mir jemand erklären, warum ich mit zwei mal vier Einzelabfragen korrekte Ergebnisse bekomme, aber nicht bei zwei Blockabfragen?

Joachim

Das Schreiben auf das Controlregister startet die Wandlung des angewählten AD-Kanal, die dauert mindestens ~90µs laut Beschreibung, ev. ist das was Du sendest zu schnell hintereinander.

Ein wait könnte helfen
Gruß Helmut

Hallo Helmut,

vielen Dank für Deine Antwort!

Das ist der relevante Teil im Skript:

// Aktualisierung der Messerte anfordern
/*
SetValueBoolean($this->GetIDForIdent("WriteProtection"), true);
$this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "i2c_read_block_byte", "Handle" => GetValueInteger($this->GetIDForIdent("Handle")), "Register" => hexdec("40"), "Count" => 4)));
SetValueBoolean($this->GetIDForIdent("WriteProtection"), false);
// Messwerte einlesen
IPS_Sleep(400);
$this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "i2c_read_block_byte", "Handle" => GetValueInteger($this->GetIDForIdent("Handle")), "Register" => hexdec("40"), "Count" => 4)));
*/
			
for ($i = 0; $i <= 3; $i++) {
	SetValueBoolean($this->GetIDForIdent("WriteProtection"), true);
	$this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "i2c_read_byte", "Handle" => GetValueInteger($this->GetIDForIdent("Handle")), "Register" => hexdec("40")|($i & 3) )));
	SetValueBoolean($this->GetIDForIdent("WriteProtection"), false);
	$this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "i2c_read_byte", "Handle" => GetValueInteger($this->GetIDForIdent("Handle")), "Register" => hexdec("40")|($i & 3) )));
			}

Der untere Teil, der keine Zeitverzögerung hat, funktioniert, ist denn Beispiel im Netz nachempfunden.
Der obere Teil, hier mal mit einem IPS_Sleep bis auf 400 hochgetrieben, wird m.E. das Ergebnis von Channel 0 „kopiert“…

Joachim

…was mich indes als Neuling in der Materie fasziniert, ist das ich auf dem Register 0x40 durch ein „write“ den Ausgang setze, aber auf dem gleichen Register 0x40 durch ein „read“ den Eingang 0 abfrage…:eek:

So habe ich es zumindest verstanden…

Joachim

Hallo Joachim,
wenn in dem oberen Code in der Schleife ein Wait drin ist, dann weiß ich auch nicht warum, aber ist es so?
Egal, Hauptsache es funktioniert :wink:
Gruß Helmut

Das ist vermutlich damit zu erklären, dass man oft generell Ausgänge nicht abfragen kann. Der Hersteller hat die Adresse also „recycled“ damit, wenn du manuell eine Feedbackschleife einbaust (Ausgang 0 und Eingang 0 miteinander verbindest), nicht kompliziert umdenken musst. Wolltest du generell immer auch den Status deines Ausgangs abfragen müssten alle chips doppelt vorhanden sein (Kosten) und diese Schleife schon eingebaut haben. Auch wenn du diese Funktion gar nicht benötigst.

Nicht unüblich in der E-Technik.

Edit:

i2c_read_block_byte und i2c_read_byte sind doch bestimmt nicht die selbe Funktion, oder? Kann es sein, dass der Datenblock noch länger ist und du ihn nicht richtig zu Ende gelesen hast? Vielleicht erwartet der Chip eine andere Reaktion vom Host (read_next oder sowas). Während ein read_byte nach exakt einem Byte abgeschlossen sein sollte.

Gruß,

Toni

Hallo Toni,

i2c_read_byte liest aus dem engegebenen Register ein Byte, i2c_read_block_byte liest von dem angegebenen (Start-)Register die nächsten x Bytes (also hier ist x=4) also - wie auch bei dem anderen Befehl aus den Registern 40-43 und gibt die empfangenen x Bytes als Array zurück. Das klappt beim BME280 wunderbar. Vorteil: In nur einer Operation können die x-Bytes eingelesen werden. Da das von meinem Verständnis den gleichen Effekt haben sollte, bin ich eben verwundert warum das hier nicht funktioniert…

Joachim

Und genau deshalb, denke ich, funktioniert es nicht.

Block schreiben lesen ohne Wartezeit dazwischen…
Gruß Helmut

…ich vermute das es sich möglicherweise so verhält:
Nach dem Datasheetin Kapitel 7.1 und 7.2, kommuniziere ich eigentlich nur mit dem Steuerregister (siehe auch Bild 5 in dem Datenblatt). Nehme ich den Hexwert mal auseinander so ist die vordere „4“ eigentlich nur die Freigabe für den Output. der „hintere“ Wert (0-3) sind die angeforderten Daten für den nächsten Lesebefehl. In der Zeit zwischen den beiden Lesezyklen wird der Wert „nach vorne geholt“ bzw. als Antwort auf den nächsten Lesebefehl gegeben. Offensichtlich könnte ich statt 0x40 bis 0x43 auch 0x00 bis 0x03 nutzen - wenn ich den Output nicht freischalten möchte.
Man könnte - wenn die vier analogen Eingänge z.B. als Vergleicher arbeiten sollen über bestimmte Bits auch ein anderes Verhalten erreichen.
Wenn ich also beim Block-Read gleich alle vier auf einmal abfrage, wird nur der letzte Wert beim nächsten Rad zur Verfügung gestellt - und das halt vier mal…

Wie Helmut schon sagte: Es funktioniert!:slight_smile:

Eine Optimierung wie ich mir so vorstellte geht also so nicht…

Joachim

PS: Axel braucht noch den PCF8574, hoffe der wird dann auch schnell integriert sein. Indes versuche ich auch „Helmuts Display“ einzubinden - wo ich aber noch keinerlei Erfolg verbuchen konnte. Brauche auch dort erst einmal einen „Grundkurs“…:smiley: