Raspberry Pi GPIOs mittels I2C Port Expander erweitern – Teil 1

Wer mehrere oder ein größeres Projekt hat, wird schnell feststellen, dass die GPIO Pins des Raspberry’s schnell zu wenig werden. Für all jene wird in diesem Tutorial gezeigt, wie man mittels eines I2C Port Expander sehr einfach die GPIO Pins um ein vielfaches erhöhen kann.

 

In manchen Situationen kann es vorkommen, dass man mehr GPIO Pins benötigt, als es Ausgänge hat und man an die physikalischen Grenzen stößt. Doch dafür gibt es die sehr nützlichen Port Expander. Auf die Benutzung einer dieser gehen wir in diesem Beitrag näher ein.

 

Zubehör & Allgmeines

Für dieses Tutorial benötigst du folgendes Zubehör:

 

Zu erst einmal der Aufbau der Mikrochips:

 

mcp23017_pinout-300x243

Wie man sieht sind die Unterschiede minimal, für dieses Tutorial ist es egal, welcher verwendet wird.

 

Vorbereitung

Eine kleine Erklärung der wichtigsten Pins:

  • GPA0-7 und GPB0-7 sind die GPIO Pins
  • A0, A1, A2 werden an + (3.3V) bzw. – (GND) angeschlossen und legen intern den Namen fest. Sind mehrere Port Expander angeschlossen, muss jeder dadurch eindeutig identifizierbar sein. Beim ersten I²C würde man alle an GND anschließen, beim nächsten A0 an 3.3V und die beiden anderen an GND. Beim dritten a! an 3.3V und die anderen zwei an GND usw. Es ist also mögich bis zu 2³ also 8 Port-Expander anzuschließen.
  • VDD (Pin 9) bekommt die Eingangsspannung (3.3V)
  • VSS (Pin 10) wird an GND angeschlossen
  • SCL (Pin 12) wird an den GPIO Pin 5 des Pi’s angeschlossen
  • SDA (Pin 13) wird  an den GPIO Pin 3 des Pi’s angeschlossen

Dementsprechend habe ich eine kleine Schaltung mit 3 LEDs gebaut (als Vorwiderstände 330Ω).

i2c_Steckplatine

(Den Taster benötigen wir erst in Teil 2 des Tutorials.)

Als erstes muss nun im Pi die Ntzung des I2C freigeschaltet werden.Am einfachsten geht dies mittels

sudo raspi-config

Unter „Advanced Options“ > „I2C“ wird es aktiviert.
Bei älteren Raspbian Versionen muss man zusätzlich eine Datei bearbeiten

sudo nano /etc/modules

und fügt an das Ende diese beiden Zeilen an:

i2c-bcm2708
i2c-dev

Mit STRG + O und STRG + X speichern und beenden.

 

Jetzt müssen die Module aus der Blacklist Datei genommen werden, da sie sonst nicht funktionieren.

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

und vor die beiden Einträge eine Raute # setzen.

#blacklist spi-bcm2708
#blacklist i2c-bcm2708

Erneut mit STRG + O und STRG + X speichern und beenden.

Damit wir den I2C nun auch ansprechen können, müssen wir noch ein paar Pakete installieren.

sudo apt-get install python-smbus i2c-tools

Anschließend den Pi noch herunterfahren, einige Sekunden warten und vom Strom trennen.

sudo shutdown now

Hardware testen

Nachdem alles angeschlossen ist und alle Verbindungen nochmals überprüft worden sind, starte den Pi und warte bis er hochgefahren ist.

Ich nutze einen Raspberry Pi Rev.2, daher teste ich es mit

sudo i2cdetect -y 1

Wer einen Pi Rev.1 hat, muss anstelle der 1 eine 0 eingeben.Die Ausgabe sieht folgendermaßen aus:

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Unter der Adresse 0x20 (hexadezimal) befindet sich also der I2C. Wäre A2 z.B. an 3.3V angeschlossen (A1 und A0 an GND), so wäre er unter der Adresse 0x24 ansprechbar. Das ist, wie bereits erwähnt, wichtig, wenn man mehrere Port Expander angeschlossen hat, um sie eindeutig ansprechen zu können.

Um die LEDs anzusprechen, müssen die Ports entweder als Input oder Output deklariert werden (Rev1 Benutzer bitte wieder anpassen).

sudo i2cset -y 1 0x20 0x01 0x00

Ich gebe ein paar Beispiele, die erläutern, wie der Befehl funktioniert:

i2cset -y 1 0x20 0x01 0x00   #alle Pins von GPB sind Output
i2cset -y 1 0x20 0x01 0x04   #GPB2 ist Input, der Rest von GPB Output (da 0x04 in binär 00000100)
i2cset -y 1 0x20 0x00 0x80   #GPA7 ist Input, der Rest von GPA Output

Als erstes wird also die Adresse angesprochen, die man mittels i2cdetect herausgefunden hat. Der zweite Wert ist in dieser Tabelle (aus dem Datenblatt):

register

Nachdem wir also die Richtung (IODIRB) angegeben haben (0 = Output, 1 = Input), wollen wir die drei LEDs leuchten lassen (Binär 00000111 = 0x07):

sudo i2cset -y 1 0x20 0x15 0x07

Würden wir die GPA Pins benutzen, stände statt 0x15 eben 0x14.
Damit die LEDs aufhören zu leuchten, müssen wir den Pegel der Pins wieder auf 0 setzen:

sudo i2cset -y 1 0x20 0x15 0x00

Im nächsten Tutorial schreiben wir ein Skript, dass automatisch auf die Pins zugreift und lesen Input Werte ein.

60 Kommentare

  1. Guten Tach,
    sehr schönes Tutorial! Kompliment!
    Da ich leider nur sehr begrenzte Kenntnisse im Bereich Elektronik besitze, habe ich da eine Frage:

    sudo i2cset -y 1 (1) 0x20 (2) 0x15 (3)0x07

    (zur besseren Orientierung habe ich die Zahlen in den Klammern hinzugefügt)
    Also (1) gibt die Adresse des Slave an (zu finden mit „sudo i2cdetect -y 1“)
    (2) ist die „HEX“-Adresse der Zeile, die aussagt, was passieren soll (15=OLATB ==>Zustand der B-Pins soll verändert werden).
    (3) in welchem Zustand die Pins, nach dem Kommando sind ( 07 in HEX-Rechner => 111 = 00000111 => GPB 0 – GBP 2 auf High, GBP 3 – GBP 7 auf LOW)

    So, wie ich das gerade schreibe, und dabei nochmal dein Tutorial lesen, klingt das irgenwie logisch(für mich). Eventuell sitze ich auch einfach nur lange genug dran, um mir mein Geschreibsel schön zu reden 😉
    Kannst Du mir bitte sagen, ob ich richtig stehe und das Licht an geht, oder nicht?
    Vlt. könntest Du dem Tutorial noch ein paar mehr Beispiele hinzufügen, damit so Knalltüten wie ich, das noch besser verstehen?

    Danke, und schöne Grüße,
    Rogon….

    Antworten
    • Hi Rogon,
      vielen Dank für das Lob 🙂
      ja deine Ausführung stimmt soweit. Was ist noch genau unklar? Waren die 3 Beispiele nicht hilfreich? Verbesserungsvorschläge sind immer willkommen.

      LG Felix

      Antworten
  2. Hallo Felix,
    zunächst einmal vielen Dank, für die schnelle Antwort.

    —!!!Heureka!!!—

    Unklar ist eigentlich nichts, ich habs dann ja doch verstanden.
    Bei der Elektrik ist bei mir so:
    Ich sitze in einem dunkeln Raum. Jemand gibt mir einen Würfel in die Hand, und erst wenn er das Licht anschaltet, glaube ich auch wirklich, dass es ein Würfel ist.
    Also Unsicherheit auf Grund völliger Unkenntnis.
    Deine Tutorials sind super! Kurz, knapp und knackig und am Ende funktioniert es sogar auch noch 😉
    Vergiss bitte die Bemerkung mit den zusätzlichen Beispielen…
    Zu leicht soll man es auch nicht machen, ein bisschen muss der Grips schon arbeiten.
    Werde, aus Freude, jetzt erstmal ne Runde mit den LED´s blinken 😉
    Nochmals vielen Dank für Deine Hilfe. Keep on running…
    LG Rogon…

    Antworten
    • Hi Rogon,
      nochmal danke für das Lob! Freut mich, dass es dir gefällt.
      Sollte bei irgendeinem Tutorial etwas unklar sein, schreib es in die Kommentare oder mir per Mail. Ich versuche so schnell es geht zu antworten.

      LG Felix

      Antworten
  3. Hallo,

    ich habe das Problem, dass der Chip im Pi nur ganz kurz sichtbar ist. Wenn ich versuche zu schreiben kommt nur Error: Write failed
    wenn ich im 0,5s Takt den Befehl sudo i2cdetect -y 1 ausführe wird nur in 10s einmal die 20 in der „Tabelle“ angezeigt, ansonsten wieder nur die — .
    Ich habe schon überprüft, ob nicht ein Kurzschluss oder manche Pins ausversehen verbunden werden, aber das ist nicht der Fall. Habe auch den Chip ausgewechselt, jedoch bleibt das Problem bestehen
    Was mache ich hier falsch? :/

    Antworten
    • Wenn du den IC schon gewechselst hast, kann man einen Defekt davon ausschließen. Es muss also entweder an der Verkabelung oder am Pi.
      Du könntest z.B. nochmal ein sauberes Image probieren, vielleicht hast du etwas installiert, was Konflikte verursacht.

      Antworten
    • Das Problem hatte ich auch.
      In meinem Fall musste ich einfach ganz fest auf den Chip drücken (obwohl er meiner Meinung nach bereits richtig drin war).
      Ich benutze ein Breadboard, aber die Beinchen von dem Chip sind halt kürzer als gewöhnliche Kabel, daher muss man da wohl einfach fest drücken.

      Jetzt funktioniert es jedenfalls. 🙂

      Antworten
      • Ok, ich ziehe das mit dem Wackelkontakt zurück.
        Es ist wie beim Kommentar von Stefan: Die RESET-Leitung muss auf VDD stehen, sonst kommt es manchmal spontan zu einem Reset und es klappt dann nichts mehr!

        In der Anleitung des Microchips ist die Signalrichtung der RESET-Leitung falsch eingezeichnet (der Pfeil müsste in Richtung des Microchips zeigen). Im beschreibenden Text dazu im Datenblatt ist es aber richtig (must be externally biased).

  4. hallo,
    bei mi funktioniert es nicht. Er zeigt mir keinen angeschlossen IC an in der Tabelle.
    habe alles genauso wie gesagt gemacht.
    bitte um Hilfe^^

    Antworten
  5. Moin moin, also ich muss die ganzen Tutorials hier auch mal loben und den Verfasser. Ich vermute mal das Felix derjenige ist der hier immer schnellstmöglich alle Fragen beantwortet und die Tuts einstellt. Vielen Dank dafür.
    Ich hätte allerdings auch mal wieder eine Frage zu der Schaltung. Wie kann ich einen einzelnen PIN wieder auf 0 setzen? Mit 0x20 0x15 0x08 kann ich PIN 4 direkt auf 1 setzen, aber wie kann ich den jetzt wieder 0 setzen ohne die anderen PINs der Bank anzusprechen? Mit 0x20 0x15 0x00 werden ja alle pins auf 0 gesetzt.
    Gruß
    Holger

    Antworten
    • Hallo Holger,
      vielen Dank für die lieben Worte 🙂
      Es gibt zwei Möglichkeiten: Entweder du schreibst eine Funktion, der du einen Pin und den Zustand angibst, allerdings diese deren Zustände vorher ausließt (falls Output) und entsprechend wieder setzt. Dann änderst du nur den Wert des Pins, der angegeben wurde und setzt die ganze Reihe.
      Der „einfachere“ Weg ist eine Bibliothek zu nehmen, wie z.B. diese hier: https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
      Ich vermute, dass die dortigen Funktionen ebenfalls alle Ports gleichzeitig ansprechen, aber eben die Werte vorher auslesen.

      mcp = Adafruit_MCP23017(busnum = 1, address = 0x20, num_gpios = 16)
      mcp.config(0, mcp.OUTPUT)
      mcp.config(1, mcp.OUTPUT)
      mcp.config(2, mcp.OUTPUT)
      mcp.output(0, 1)  # Pin 0 High
      Antworten
    • Hi Felix, danke für den Tip. Ich denke ich werde es selbst auslesen und dann neu schreiben. Habe ich gerade schon einmal versucht und hatte die Befürchtung, dass es bei den Pins wo es keine Änderung gibt, eine kurze Unterbrechung gibt. Augenscheinlich ist dies aber nicht so. Gemessen habe ich es allerdings noch nicht 😀
      Gruß
      Holger

      Antworten
  6. Hallo,

    ich habe da auch ein Problem. Und zwar lief bis gestern noch alles reibungslos. Nun habe ich heute 2 weitere Boards angeschlossen und seit dem kann ich keinen i2c Port mehr schalten. Er lässt sich zwar über Putty schalten aber das Relay Board reagiert nicht. Hast du eine Idee woran es liegen könnte?

    Das ist der Code mit dem ich es vorhin nochmal versucht habe:
    i2cset -y 1 0x20 0x12 0xFF
    Er soll alle Port A Pins des ersten Boards einschalten.

    Hoffe du kannst mir helfen.
    Ansonsten ein sehr gutes Tutorial 😉 Alles sehr gut erklärt !!! TOP-Arbeit!
    Danke für deine Hilfe und das Tutorial.

    Antworten
  7. HI! Danke für das Tutorial! Wenn ich das jetzt richtig verstehe müsste ich wenn ich diese Art von Pin Erweiterung benutze auf Dauer alles auf Platinen löten, richtig? Kennst du evtl. noch andere Möglichkeiten die vll. ähnlich klein sind aber ohne Löten auskommen?
    Viele Grüße, Sebastian

    Antworten
  8. hallo wenn ich das ganze probiere mit
    sudo i2cdetect -y 1
    bekomme ich eine fehlermeldung:
    Error: No i2c-bus specified!
    Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
    i2cdetect -F I2CBUS
    i2cdetect -l
    I2CBUS is an integer or an I2C bus name
    If provided, FIRST and LAST limit the probing range.

    was mache ich nur falsch? hbe alles schritt für schrit so gemacht 🙁

    Antworten