Portexpander PCF8574

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Ausführliche Beschreibung der Verwendung des PCF8574 als Portexpander für bis zu 8 Ein- / Ausgängen
    Der I²C- Bus (TWI)
    Vielseitige Zweidrahtverbindung zu einer speziellen Schaltkreisfamilie, erklärt am PCF 8574

    I2C-Logo.png
    Der sogenannte I²C-Bus, ursprünglich von der Firma Phillips© zum Austausch von Daten innerhalb von Fernseher, Videorecorder und anderen Geräten entwickelt,wird innerhalb von Mikrocontrollersystemen gerne zur Erweiterung der Möglichkeiten eingesetzt. Eine Vielzahl von speziellen Schaltkreisen steht uns hier zur Verfügung. Hervorzuheben sind insbesondere folgende Bereiche:

    • Porterweiterungen für mehr Ausgabe- und Eingabepins
    • Leichte Ansteuerung von Siebensegmentanzeigen
    • Einfache Erweiterung des EEprom-Speichers
    • Uhrenbausteine mit Pufferbatterie
    Die Bausteine haben gemeinsam, dass sie neben der Spannungsversorgung über nur zwei zusätzliche Leitungen miteinander verbunden werden. Der große Vorteil ist, dass nicht jeder Baustein zwei eigene Leitungen benötigt, sondern das zwei gemeinsame Leitungen für alle angeschlossenen Schaltkreise ausreichend sind. Wie an zwei „Wäscheleinen“ kann man die gewünschten IC´s einfach an den Bus“ hängen“ und dieses System bei Bedarf auch nachträglich noch erweitern (oder reduzieren). Um diesen Bus zu steuern gibt es bei BASCOM eine Reihe spezieller Befehle, die leicht erlernt werden können und die Verwendung des I²C- Bus zum Kinderspiel machen.


    So arbeitet der I²C-Bus

    Das Wichtigste, was beim I²C-Bus zu beachten ist, das jede Aktion immer aus mindestens 2 Kommandos besteht, welche zwischen der Start- und der Stopanweisung für den I²C-Bus steht. Dabei können durchaus auch mehrere Kommadopaare gesendet werden.
    Es gibt viele verschiedene Bausteine der I²C-Familie. Damit diese gezielt angesprochen werden können, haben gleiche IC´s natürlich erst mal eine gemeinsame Adresse. Bildlich gesehen ist unsere Schaltung eine Stadt und IC´s des gleichen Typs (z.B. PCF8574) befinden sich in der gleichen "Straße" mit dem Namen PCF8574.

    Da unser Controller Zahlen lieber mag, heißt die "Straße" bei ihm 0100 (Die feste, nicht änderbare Adresse des PCF 8574). Da in dieser "Straße" bis zu 8 identische IC´s vorhanden sein dürfen, braucht der Controller noch eine Information, welcher IC gemeint ist. Jeder IC hat also sozusagen eine "Hausnummer", welche über drei Adressjumper an der Hardware gesetzt werden kann. Diese wird als dreistellige 0-1´er Kette an die "Straße" angehängt. Im einfachsten Fall ist kein Jumper gesetzt (Auslieferungszustand) und die „Hausnummer“ lautet: 000.


    Wären wir bei der Post, wüssten wir jetzt genau, in welcher "Straße" unter welcher "Hausnummer" eine Aktion ausgeführt werden soll. Da der PCF8574 als Eingang oder Ausgang verwendet werden kann, bleibt noch offen, ob Daten "abgeholt", oder Daten "gebracht" werden sollen. Hierfür hängen wir eine letzte 0 oder 1 an.

    Wenn man nun zusammenfasst, hat man jetzt einen ersten 8-Bit Wert, der genau beschreibt, welches IC wir ansprechen möchten und ob wir Daten lesen oder schreiben möchten.
    Dies ist der erste Befehl, den wir als I²C- Kommando senden.

    Jetzt folgt eine zweite I²C- Kommandozeile, die ebenfalls 8 Bit lang ist. Hierin enthalten sind beispielsweise die Daten für die 8 Ausgänge des PCF8574 - Ja, eine 8- Bit Kette für 8 Ausgänge; jedes Bit entscheidet, ob der zugehörige Ausgang 0 oder 1 ist.

    Fertig- In unserem Programm muss nur noch bestimmt werden, welcher Ausgang 1 oder 0 ist. Wie dies auf einfache Weise umzusetzen ist, erfährst Du nach diesem Abschnitt.

    Bei einem PCF8574 (ohne gesetzten Adressjumper, also so wie geliefert) der als Ausgang alle angeschlossenen LED´s ausschalten soll, sieht das dann so aus:

    I2cstart
    I2cwbyte &B01000000 'Adresse
    I2cwbyte &B11111111 'Daten
    I2cstop



    Ist Dir bei der Datenzeile etwas aufgefallen? Ja, alle LED´s sollen ausgeschaltet werden. Wir aber senden lauter Einsen. Das bedeutet doch eigentlich: Einschalten!


    Beim Programmieren von Mikrocontrollern kommt es oft vor, dass die beiden möglichen Zustände 0 und 1 aus technischen Gründen vertauscht werden. Man spricht in diesem Fall von „Invertierter Logik“. Das bereitet am Anfang etwas Schwierigkeiten. Mit ein wenig Übung beherrschen wir das aber sehr schnell.


    Bevor wir die I²C- Befehle verwenden können, müssen wir dem Controller am Anfang noch mitteilen, welche beiden Pins wir als SDA und SCL verwenden möchten und den Bus freischalten. Viele Mikrocontroller verfügen bereits über einen vorbereiteten I²C- Bus. Die entsprechenden Pin´s des IC´s findet man leicht in der Zeichnung zu Anfang des entsprechenden Datenblattes. Diese werden dort mit den Bezeichnungen SDA (Datenleitung) und SCL (Clock, Takt) bezeichnet. Hier reicht es, zu Anfang des Programms den Befehl I2cinit auszuführen.
    Sollte der Mikrocontroller keinen I²C-Bus aufweisen, oder die vorgesehenen Anschlüsse bereits für andere Anwendungen verwendet werden, muss dies dem Controller zu Anfang mitgeteilt werden. Dadurch erweitern sich die Anfangsbefehle wie folgt (Portc.1 und c.5 sind hier natürlich nur als Beispiel genannt):

    config sda = Portc.1
    config scl = Portc.5
    I2cinit

    Bei der Hardware ist noch wichtig, dass von den Pins SDA und SCL jeweils ein Widerstand (4k7) nach Plus geschaltet wird. Die Adresseingänge sollten außerdem immer auf festem Potential liegen, also entweder GND (0) oder VCC (1)


    Wenn jetzt die SDA- Leitungen und die SCL- Leitungen von Mikrocontroller und den angeschlossenen IC´s verbunden sind, sollte einer erfolgreichen Datenübertragung nichts im Wege stehen.

    Wie setze ich beim PCF 8574 einen einzelnen Pin in meinem Programm?

    Oftmals liegt die Schwierigkeit am Anfang darin zu verstehen, wie jeder einzelne Pin im Programm angesprochen werden kann.
    Es gibt hierfür viele wesentlich kompaktere Möglichkeiten, aber die folgende, welche wiederum am PCF 8574 erklärt werden soll ist relativ einfach nachzuvollziehen:



    Für jeden Ausgang des PCF8574 wird ein Flagbit deklariert, welches im Programm frei gesetzt oder gelöscht werden kann. Nach einer Änderung eines Bits oder einer Bitfolge springt man in die Unterroutine und lässt die Änderungen ausführen.

    Der Vorgehensweise liegt folgende Überlegung zugrunde: Die Variable Ausgang_1 ist eine Bytevariable, verfügt daher über 8 Bit; also die 8 Einheiten die wir zum Setzten der 8 Ausgänge benötigen.

    Die binäre Zuordnung entspricht ja bekanntlich folgender Dezimalzahlkette:


    12864321684218 Bit
    000000000



    In unserer Beispieltabelle sind alle 8 Werte 0. Würden wir dies an den PCF 8574 übertragen, würden aufgrund der invertierten Logik alle LED´s eingeschaltet werden.
    Möchte man nun einen Ausgang gezielt ansprechen (in unserem Falle also eine LED ausschalten), so addiert man die entsprechende Dezimalzahl einfach zur Zielvariable (Ausgang_1).
    Beispiel: Ausgang 7 und 5 (von rechts) sollen geändert werden:

    12864321684218 Bit
    0101000064+16=80



    In unserem Beispiel müssen also die Werte 64 und 16 addiert werden. Unsere Variable Ausgang_1 erhält demnach nun den Wert 80.

    Wird dieser über die I²C-Routine an den PCF 8574 gesendet, wechseln nur diese beiden Ausgänge ihren Zustand; LED 5 und LED 7 gehen aus. Alle anderen Ausgänge bleiben unverändert, da deren Werte in der Tabelle wie zuvor 0 bleiben.
    Um nicht ständig schriftlich festhalten zu müssen, welche Ausgänge im Programm gerade 0 oder 1 sind und anhand von Tabellen binäre Befehlsketten zu erstellen, kann man die folgende Programmroutine benutzen.
    Dadurch ist es möglich, im laufenden Programm nur die Flagvariablen der Ausgänge neu zu setzen, welche auch tatsächlich geändert werden sollen. Für alle anderen Ausgänge bleiben die bestehenden Flagvariablen ja auf ihrem ursprünglichen Wert, sodass beim Ausführen der I²C- Subroutine alle Ausgänge korrekt gesetzt werden.

    Zur Verdeutlichung ist eine kleine Do- Loop- Schleife eingefügt, die das Blinken einer LED an einem PCF 8574 bewirkt.

    Am Anfang der Subroutine wird Ausgang_1 natürlich immer erst auf 0 gesetzt, um stets die gleichen Ausgangsbedingungen zu haben. Dies hat selbstverständlich keine Auswirkungen auf die Flagvariablen. Diese werden nur im Hauptprogramm bearbeitet.


    '===============================================================================
    'I²C Configuration und Dimensionierung
    '===============================================================================
    I2cinit
    Dim Ausgang_1 AsByte 'Variable zum Codieren der Ausgabeparameter (Platine 1)
    Const Pcf85741 =&B01000000 'Adresse des ersten PCF 8574 (ohne Adressjumper)
    'im Schreibzugriff (letzte Stelle ist 0)
    Dim Ausgang1_a AsBit 'Flag für Ausgang A von PCF8574
    Dim Ausgang1_b AsBit 'Flag für Ausgang B von PCF8574
    Dim Ausgang1_c AsBit 'Flag für Ausgang C von PCF8574
    Dim Ausgang1_d AsBit 'Flag für Ausgang D von PCF8574
    Dim Ausgang1_e AsBit 'Flag für Ausgang E von PCF8574
    Dim Ausgang1_f AsBit 'Flag für Ausgang F von PCF8574
    Dim Ausgang1_g AsBit 'Flag für Ausgang G von PCF8574
    Dim Ausgang1_h AsBit 'Flag für Ausgang H von PCF8574
    '===============================================================================
    'Fragment für Hauptprogramm '1 Hz Blinken von Ausgang1_a an PCF8574
    '===============================================================================
    Do
    Ausgang1_a = 1
    Gosub I2c_ausgaberoutine
    Waitms500 '
    Ausgang1_a = 0
    Gosub I2c_ausgaberoutine
    Waitms 500
    Loop
    '===============================================================================
    'Subroutine I²C- Ausgabe
    '===============================================================================
    I2c_ausgaberoutine:
    Ausgang_1 = 0
    If Ausgang1_a = 1 Then Ausgang_1 = Ausgang_1 + 1
    If Ausgang1_b = 1 Then Ausgang_1 = Ausgang_1 + 2
    If Ausgang1_c = 1 Then Ausgang_1 = Ausgang_1 + 4
    If Ausgang1_d = 1 Then Ausgang_1 = Ausgang_1 + 8
    If Ausgang1_e = 1 Then Ausgang_1 = Ausgang_1 + 16
    If Ausgang1_f = 1 Then Ausgang_1 = Ausgang_1 + 32
    If Ausgang1_g = 1 Then Ausgang_1 = Ausgang_1 + 64
    If Ausgang1_h = 1 Then Ausgang_1 = Ausgang_1 + 128
    I2cstart
    I2cwbyte Pcf85741
    I2cwbyte ausgang_1
    I2cstop
    Return
    '-----------------------------------------------------------------------------

    Der PCF 8574 als Eingang

    Wie Anfangs beschrieben lässt sich der PCF 8574 auch in umgekehrter Richtung als Eingang verwenden. Damit besteht die Möglichkeit mit nur einem IC den Mikrocontroller mit bis zu 8 weiteren Eingängen zu verbinden.
    Die Vorgehensweise ähnelt sehr stark der Verwendung als Ausgang:

    An der ersten Kommandozeile ändert sich nur wenig. „Strasse“ und „Hausnummer“ des IC bleiben erhalten, wenn wir hier keinen zusätzlichen Chip einsetzen, sondern unseren Schaltkreis aus dem ersten Versuch wieder einsetzen. Lediglich das letzte Bit wird von 0 auf 1 gesetzt, da wir ja nun auslesen wollen:
    I2cwbyte &B01000001 ´Unser neues Adressbit mit 1 am Ende für „Eingang“

    Rot eingefärbt ist hier außerdem ein Buchstabe im I2c- Befehl. Wie Ihr Euch vielleicht denken könnt steht dieses „w“ für write, da wir hier dem IC etwas mitteilen, ihm sozusagen eine Nachricht schreiben. Wenn wir den PCF 8574 als Ausgang verwenden ist auch die zweite Kommandozeile ein Schreibbefehl. Darum unterscheiden sich die beiden Befehle nicht, wenn wir den Chip als Ausgang verwenden.

    Anders sieht das allerdings aus, wenn wir wie hier, die Ausgangszustände auslesen möchten.. Wie bekannt bedeutet read im Englischen lesen. Folglich ändertsich unsere 2. Kommandozeile wie folgt:
    I2crbyte pcf8574 , Nack

    Nack? – was ist das schon wieder?

    Zuerst wollen wir einmal klären, warum hinter I2crbyte nicht wieder so etwas wie &B00110100 steht. Klar, da wir hier ja nicht schreiben, sondern etwas auslesen möchten, wissen wir noch nicht, welche Nullen oder Einsen an welcher Stelle stehen. Also fordern wir die Daten erst einmal in einer Bytevariable (8 Bit – zur Erinnerung) an und werten den Inhalt dann anschließend im Programm aus. Dort können wir anhand der Bitwerte sehen, welcher Eingang 0 und welcher Eingang 1 ist.

    Aber zurück zu Nack. Es gibt, wie Eingangs beschrieben die verschiedensten Bausteine für den I²C- Bus. Einige von ihnen kommen mit 8 Bit nicht aus und senden noch weitere Kommandozeilen. Aus diesem Grund gibt es neben der Anweisung Nack auch noch die Anweisung Ack und die Verwendung ist ganz einfach:

    Lediglich die letzte Kommandozeile wird mit Nack beendet und teilt dem Controller mit, dass die Informationen nunmehr komplett übertragen worden sind. Alle Kommandozeilen vor dieser letzten Zeile tragen anstatt der Anweisung Nack die Anweisung Ack.

    Nack ist sozusagen das Schlusslicht der Übertragung des soeben ausgelesenen I²C- Bausteins.

    Dies aber zunächst nur am Rande zum Verständnis, denn unser PCF 8574 begnügt sich ja mit zwei Kommandozeilen.

    Zusammenfassend halten wir fest, wie wir unseren PCF 8574 auslesen:

    I2cstart
    I2cwbyte &B01000001 ´Unser neues Adressbit mit 1 am Ende für „Eingang“
    I2crbyte pcf8574 , Nack `Auslesen der 8 Bit in die Variable pcf8574
    I2cstop

    Und so können die einzelnen Eingänge im Programm ausgewertet werden:

    76543 2108 Bit
    Eingang 7Eingang 6Eingang 5Eingang 4Eingang 3Eingang 2Eingang 1Eingang 00



    Die Ausleseroutine liefert (in unserem Fall in der Variablen pcf8574) den 8- Bit Wert zurück, der die Eingangszustände des Chips wiedergibt. Der oben stehenden Tabelle kann entnommen werden, welche Zahl (0-7) nach dem Punkt hinter der Variablen gesetzt werden muss, um den entsprechenden Eingang auszuwerten.

    Beispiel: Wir möchten wissen, ob bei unserer Abfrage am Eingang 4 unseres PCF 8574 eine 1 oder eine 0 angelegt war.

    Wir ergänzen unsere Abfragevariable (Pcf8574) um einen Punkt (.) und nachfolgend um die Ziffer 4, um den Wert des Eingangs 4 zu erfahren:
    Pcf8574 . 4

    Im Programm können wir diese Variable dann wie gewohnt verarbeiten:

    If PCF8574.4 = 1 then
    print „Türe geöffnet“

    end if

    Genau so können wir auch die 7 anderen Werte verwenden:
    Pcf8574 . 0 Pcf8574 . 1 Pcf8574 . 2 Pcf8574 .3 Pcf8574 . 5 Pcf8574 . 6
    Pcf8574 . 7


    Aber der PCF8574 kann noch ein wenig mehr: Um nicht ständig die Eingänge abfragen zu müssen, was das Programm unter Umständen erheblich belasten kann, verfügt der PCF 8574 über einen Interruptausgang. Das Verhalten von Interrupteingängen haben wir an anderer Stelle ja bereits besprochen. Aus diesem Grund hier nur eine kurze Ergänzung.

    Der Interruptausgang sendet erst ein Signal, wenn sich mindestens ein Eingangspegel am Schaltkreis geändert hat. Daraufhin kann man das Programm veranlassen, den Chip erneut auszulesen. So entgeht dem Programm keine Aktion an den Eingängen unseres PCF 8574.

    Es versteht sich von selbst, dass der Interruptausgang vom PCF 8574 für diese Funktion natürlich mit einem Interrupteingang des Mikrocontrollers verbunden sein muß.

    So „angeln“ wir bei der gemischten Anwendung des PCF 8574 gezielt einzelne Bit´s aus unserem Byte


    Wenn wir den PCF 8574 gemischt als Eingang und Ausgang verwenden, stoßen wir auf die Schwierigkeit, dass wir möglicherweise einzelne Bit´s nicht der Reihe nach verwenden können, sondern das beispielsweise durch das Platinenlayout Eingänge und Ausgänge in unregelmäßiger Reihenfolge gesetzt werden müssen.
    In diesem Falle wäre es sehr aufwändig, die Variable auseinander zu nehmen und die Bitwerte herauszufiltern, die als Eingang verwendet werden. Zum Glück gibt es auch hier eine Möglichkeit in BASCOM, um auf ein einzelnes Bit in der Bytvariable zurück zu greifen, wie wir bereits beim Auslesen des Chips gesehen haben.

    Um die gemischte Auswertung noch einmal zu verdeutlichen hier ein entsprechendes Beispiel:
    Angenommen wir haben folgende Verteilung für die Variable PCF8574:

    76543210Bit-Nr.
    1286432168421Dezimal
    EAAAEEAEE / A



    Die Verteilung der Ein- und Ausgänge in der Variable PCF8574 wäre im übertragenen Sinne folgende:

    PCF8574 &BEAAAEEAE

    wobei jedes „E“ für Eingang und jedes „A“ für Ausgang steht.
    In diesem Zusammenhang ist folgendes aus der Tabelle ersichtlich:

    Bit Nr. 7 wird als Eingang verwendet
    Bit Nr. 6 wird als Ausgang verwendet
    Bit Nr. 5 wird als Ausgang verwendet
    Bit Nr. 4 wird als Ausgang verwendet
    Bit Nr. 3 wird als Eingang verwendet
    Bit Nr. 2 wird als Eingang verwendet
    Bit Nr. 1 wird als Ausgang verwendet
    Bit Nr. 0 wird als Eingang verwendet
    Einen einzelnen Bitwert kannst Du nun mit folgendem Format direkt auslesen:

    Variable.X




    Auf unser Beispiel übertragen erhalten wir für unserer 4 Eingänge folgende Variablen:

    PCF8574.0 für den rechten Eingang (Tabellenbezogen)
    PCF8574.2 für den mittleren, rechten Eingang (Tabellenbezogen)
    PCF8574.3 für den mittleren, linken Eingang (Tabellenbezogen)
    PCF8574.7 für den linken Eingang (Tabellenbezogen)

    Die übrigen „Untervariablen“ von PCF8574 interessieren uns in diesem Zusammenhang nicht, da diese ja als Ausgänge geschaltet sind und über das Hauptprogramm ihre Werte zugewiesen bekommen.

    Dies bedeutet, dass wir den Baustein PCF 8574 über unser I²C- Kommando auslesen und anschließend in den Variablen PCF8574.0 , PCF8574.2 ,PCF8574.3 und PCF8574.7 die entsprechenden Werte finden, welche unter eben dieser Bezeichnung im Programm weiter verwendet werden können.

    Beispiel:
    If PCF8574.2 = 1 then
    print „Türe geöffnet“

    end if



    Um die Werte der Eingänge nicht unbeabsichtigt zu ändern, müssen wir darauf achten, dass die Pin´s nicht versehentlich überschrieben werden.
    Bei Verwendung unseres Beispielprogramms lassen wir die entsprechenden Zeilen in der IF- Abfrage (in unserem Beispiel für die Werte: 64, 32, 16, und 1) am Besten vollständig weg, oder kommentieren sie aus!

    I2c_ausgaberoutine:
    Ausgang_1 = 0
    If Ausgang1_b = 1 Then Ausgang_1 = Ausgang_1 + 2
    If Ausgang1_c = 1 Then Ausgang_1 = Ausgang_1 + 4
    If Ausgang1_d = 1 Then Ausgang_1 = Ausgang_1 + 8
    If Ausgang1_h = 1 Then Ausgang_1 = Ausgang_1 + 128
    I2cstart
    I2cwbyte Pcf85741
    I2cwbyte ausgang_1
    I2cstop
    Return


    Damit schließen wir die Beschreibung des PCF 8574 ab. Wie wir gelernt haben, können wir 8 dieser Bausteine an einem einzelnen Mikrocontroller verwenden und somit insgesamt 64 Eingänge oder 64 Ausgänge realisieren.
    Es gibt Anwendungen, für die auch diese schon recht hohe Anzahl an Ein- und Ausgängen nicht ausreicht. Für diesen Fall gibt es noch den Baustein mit der Bezeichnung PCF 8574-A.
    Dieser ist identisch aufgebaut, weist jedoch eine andere Basisadresse auf. Das heißt, um bei der Beschreibung vom Anfang zu bleiben, diese IC´s haben einen anderen „Straßennamen“.

    Bei unserem ersten. I²C-Kommando lauten die ersten 4 Ziffern also nicht 0010 sondern 0111.

    Somit stehen weitere 64 Ein- oder Ausgänge zur Verfügung, welche genauso einfach zu programmieren sind wie unser PCF 8574

    5,814 times viewed