Raspberry Pi, I2C Portexpander mit dem MCP23017

raspi-pi-i2c-mcp23017

Wenn man nach einen I2C-Chip für den Raspberry googelt, also einen der mit 3V läuft, landet man fast zwangsläufig beim MCP23017.

Aufbau der Schaltung:

chip

Zunächst muss man aber den Raspberry für I2C fit machen.

sudo nano /etc/modprobe.d/raspi-blacklist.conf

eine # vor den Eintrag blacklist i2c-bmc2708 machen

blacklist

sudo nano /etc/modules

in eine neue Zeile schreiben:  i2c-dev

modules

jetzt ein Update machen kann nicht schaden

sudo apt-get update

nun installieren wir die eigentlichen Programme

sudo apt-get install i2c-tools

und fügen der Gruppe i2c zwei neue User hinzu

sudo adduser pi i2c
sudo adduser www-data i2c

der User www-data ist nötig damit der Apache später per exec Befehle ausführen kann

nun ein Neustart

sudo shutdown -r now

Der Raspberry verfügt jetzt über vier neue Programme

  • i2cdetect
  • i2cdump
  • i2cget
  • i2cset

Als erstes scannen wir den Bus mit i2cdetect und schauen unter welcher Adresse der MCP23017 zu finden ist. Der Raspberry hat zwei I2C-Busse (0 und1) Daher müssen wir dem Programm sagen welchen Bus wir absuchen wollen. Bei den alten Raspis war der Bus 0 an den Pins herausgeführt, bei den Neuen ist es der Bus 1.
alt:

sudo i2cdetect -y 0

neu:

sudo i2cdetect -y 1

i2cdetect

Wenn die Pins 15, 16 und 17 mit GND verbunden sind sollte das Ergebnis 0×20 betragen.

Nun können wir mit dem Programm i2cdump einen Blick in das Innere des MCP23017 wagen.

i2cdump -y 1 0x20

i2cdump

Das -y überspringt übrigens die Sicherheitsabfrage die oben auf dem Bild zu sehen ist.

Im Gegensatz zu einem einfachen I2C-Chip wie dem PCF8574 besitzt der MCP23017 noch diverse Register und man muss beim Zugriff auf den MCP23017 angeben auf welches man sich bezieht. Entscheidend sind hier zunächst mal die Adressen 0×00 und 0×01 um die Pins jeweils als Ein- oder Ausgang zu setzen.

Mit dem Programm i2cset schreiben wir nun auf:
i2c-Bus 1, Adresse des Chips 0×20, Register 0×00 (IODIRA), den Hex-Wert 0×00
Dieses Register ist für alle A-Pins zuständig, also die Pins 21 bis 28 an denen die LEDs ansgeschlossen sind. Eine 0×00 setzt alle Pins als Ausgang.  Für die B-Pins (1 bis 7) ist das Register 0×01 (IODIRB) zuständig.

Für unseren Testaufbau führen wir diesen Befehl aus:

i2cset -y 1 0x20 0x00 0x00

Da die Ports per default Eingänge sind. Müssen die Ausgänge nach jeder Stromunterbrechung neu gesetzt werden.
Wer Ein- und Ausgänge mischen will sollte am Besten zunächst in dieses Tool eine binäre Zahl eintippen und diese dann in Hex umrechnen lasen.

Nun kann man schon auf der Konsole eine LED schalten. Dazu schreiben wir in das Register 0×12(GPIOA ) für die A-Pins b.z.w. 0×13(GPIOB ) für die B-Pins.

z.B. die LED an Pin 7 (GPA0) einschalten:

i2cset -y 1 0x20 0x12 0x01

Für das Lauflicht muss man den Befehl nacheinander mit diesen  Hexzahlen aufrufen:

0×01, 0×02, 0×04, 0×08, 0×10, 0×20, 0×40, 0×80

In die Register 0×14(OLATA) und 0×15(OLATB) könnte man ebensogut schreiben. Sie sind sog. “Output Latches” und  0×12  und 0×13  übernehmen dann die Werte von 0×14 und 0×15.
Wenn man also z.b in Register 0×14 schreibt wird 0×12 ebenfalls diesen Wert annehmen, schreibt man in Register 0×12 nimmt 0×14 dessen Zustand an.

Interne Pullup-Widerstände (100KOhm) kann man mit den Registern 0x0C (GPPUA) und 0x0D (GPPUB) einschalten. Da wir in unserem Aufbau keine externen Pullup-Widerstände verwenden, führen wir diesen Befehl aus um sie für die B-Pins einzuschalten:

i2cset -y 1 0x20 0x0D 0xff

für die A-Pins wäre der Befehl:

i2cset -y 1 0x20 0x0C 0xff

Ruft man jetzt noch mal i2cdump auf, sieht man dass Register 0×13 den Wert 0xff angenommen hat.
Will man aber beim weiteren Programieren nicht ständig mit dieser negativen Logik arbeiten (0xff = kein Schalter gedrückt; 0xfe = ein Schalter gedrückt u.s.w.) kann man die Logik auch direkt im MCP23107 umpolen.

Die Register 0×02(IPOLA) und 0×03(IPOLB) helfen dabei. Für unsere Schaltung führen wie diesen Befehl aus:

i2cset -y 1 0x20 0x03 0xff

Nun gibt das Register 0×13 eine 0×00 aus solange kein Taster gedrückt ist.

Erwähnenswert wären noch diese Register:

INTF – Interrupt ein/aus
INTCAP – Zustand der Ports wird zwischengespeichert wenn ein Interrupt ausgelöst wird

aber da verweise ich gerne auf das Datenblatt

 

Nochmal als Zusammenfassung, kurzes Setup nach einem Neustart:

i2cset -y 1 0x20 0x00 0x00 // alle A-Pins als Ausgang
i2cset -y 1 0x20 0x0d 0xff   // Pullups an den B-Pins ein
i2cset -y 1 0x20 0x03 0xff    //Logik der B-Pins umkehren

(diese Einstellungen kann man natürlich auch in das PHP-Script auslagern)

Nun kann man über den Webbrowser die Pins abfragen oder schalten. (Diese Aussage basiert auf der Grundannahme das auf dem Raspberry dieses Paket installiert ist)
Dazu habe ich ein kleines Demoscript geschrieben. Es erzeugt ein Lauflicht an den A-Pins und fragt die Zustände der Schalter an den B-Pins ab.

(die Datei dann in index.php umbenennen)

raspberry-pi-MCP23017-index-php

Im Gegensatz zur Konsole muss man bei den exec-Befehlen den Pfad zu dem entsprechenden Programm angeben. Sollte es sich, wider Erwarten, doch woanders befinden kann man sich den Pfad mit which / dateiname anzeigen lassen und im PHP-Script entsprechend ändern.
Eventuell muss man der PHP-Datei noch per chmod 777 die entsprechenden Rechte geben.

i2c-browser

hier noch ein Video wie das Ganze dann aussieht