Raspberry Pi GPIOs mittels I2C Port Expander erweitern – Teil 1

29. March 2014
45 Comments

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.

45 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
  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
  9. Also bei mir scheint das i2cdetect nicht zu funktionieren. Die Ausgabe mit -y 1 bzw. 2,3 ist „error Could not open file. … no such file or directory „

    Antworten
    • Welches Raspberry Pi Modell hast du und wofür soll 2,3 stehen? Klingt sehr danach, dass die Installation nicht (vollständig) geklappt hat.

      Antworten
  10. Ich habe auch diese meldung. habe den pi2

    habe aber eig alles befolgt was hier steht, wie kann ich den Fehler finden?

    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.

    Antworten
  11. ich habe „sudo i2cdetect -y0“ sowie mit „-y1“ getestet.
    meine blacklist ist komplett leer, keine einträge.
    i2c tools auf auf neuster version.
    python-smbus auf neuster version.
    die datei /etc/modules hat in den letzten beiden zeilen den diese befehle „i2c-bcm2708
    i2c-dev“

    Antworten
  12. unter lsmod
    ist i2c_dev sowie i2c_bcm2708 geladen.
    spi_bcm2708 ist nicht geladen, aner benötige ich auch nicht oder

    Antworten
  13. howdy-ho
    das tutorial ist jetzt zwar schon ein wenig älter.. aber obwohl ich es haarklein befolgt habe und sogar exakt den selben expander-chip verwende, habe ich nur probleme..

    auffällig hierbei ist folgendes:
    – beim i2cdetect tauchen „UU“s auf.. nach recherchen handelt es sich hier um reservierte adressbereiche die allerdings nicht mit dem 0x20 kollidiern..
    – durchschnittlich jedes dritte mal beim verwenden eines der i2c befehle, gibts probleme.. soll heißen.. beim i2c detect wird der 0x20’er nichtmehr gefunden.. oder beim setzen der pins, oder setzen als input/output wird nen error beim write rausgehaun

    – hin und wieder wirkt es auch so beim setzen der pins.. als wäre dort ein kurzer.. sowohl die powerLED am PI als auch eine kontroll LED die ich angebracht habe, ist für den bruchteil einer sekunde aus.. allerdings für so kurze zeit das das board normal weiterläuft und alles..

    – voller zuversicht hatte ich natürlich direkt 2 chips verkabelt.. die adresse war(wenn es mal angezeigt wurd) 0x22.. wenn ich raten müsste würde ich sagen das 0x21 der erste ist.. und 0x22 der zweite.. während 0x20 vlt. sogar beide gleichzeitig anspricht??

    da ich echt am verzweifeln bin(da es bei etlichen anderen leuten scheinbar problemlos funktioniert) und mir nicht ein chip nach dem andern(oder das PI selbst) zerschießen will.. brauch ich also echt unterstützung und wäre extrem dankbar..

    Antworten
    • Hey,
      kannst du einen Wackelkontakt ausschließen? Das Problem sagt mir in der Form leider nichts, ich selbst hatte es noch nicht und bisher wohl auch keiner der das Tutorial befolgt hat.
      Die Adresse (0x20) gibst du – wie im Tutorial beschrieben – mit den drei A0-A2 Kontakten an. Möchtest du einen Chip mit der Adresse 0x22 ansprechen, so muss A0 und A2 an GND aber A1 an 3.3V.
      Ich würde dir wirklich gerne besser helfen, aber das klingt sehr merkwürdig. Klappen denn andere I2C Anwendungen bei dir (gibt einige Tutorials hier mit I2C)?

      Antworten
    • das mit der pin adressierung hab ich soweit schon verstanden.. und der 0x22 wurd ja auch angezeigt als ich , wie beschrieben , voller zuversicht direkt zwei chips angestöpselt habe..

      wackelkontakt kann ich ziemlich gut ausschließen.. zudem ich das problem(mit dem das er mal angezeigt wird und mal nicht) auch habe wenn ich definitiv außer reichweite bin um an dem raspi irgendwas zu bewegen..

      ich würde das ganze gerne mit fotos mit dir abgleichen.. in skype/steam heiß ich ebenfalls „sgtigram“.. ansonsten kannst du mich gern mal unter der hinterlegten email anschreiben

      grüße
      – sgtigram

      Antworten
  14. Erstmal muss ich sagen dass das Tutorial großartig ist allerdings verstehe ich nicht wie ich 4 Expander anschließen kann. Könntest du oder wer anderes mir das nochmal erklären? Eventuell mit einem Bild der Schaltung?

    Antworten
    • Du hast doch die Pins A0, A1 und A2. Jeder dieser Pins kann entweder Zustand AN oder Zustand AUS haben (=ein Bit). Wir haben also 2^3 = 8 Möglichkeiten eine Nummer zu vergeben, sprich du kannst bis zu 8 Expandern eine eindeutige Nummer zuweisen.
      Schau dir am besten mal das Binärsystem genauer an.

      Antworten
    • Okay, das habe ich verstanden aber der Pi hat ja nur 2 I2C Anschlüsse, dann ist es doch nicht möglich 4 Expander anzuschließen. Sorry, wenn ich das nicht verstehe aber ich kenne mich damit nicht wirklich aus…

      Antworten
  15. Der I2C ist ein Bussystem. Alle Expander hören also immer zu, reagieren aber nur auf ihre Adresse. D.h., dass alle Expander quasi parallel angeschlossen werden.

    Antworten
  16. Vielen Dank für die ausführliche Beschreibung! Funktioniert fehlerlos.

    Für die Besitzer des Raspi 3 ist es noch einfacher: In den Einstellungen – I2C aktivieren.
    Dazu noch ein Tip falls Python (3) den Import des smbus Moduls verweigert:
    sudo apt-get install python3-smbus

    Eine Frage habe ich noch:
    Sollte man an den Ausgängen zum Schalten von 8 Relais (Relaiskarte) noch ein ULN2003 anhängen oder schafft das die 5V Leitung des Raspi’s ?

    Antworten
    • Müsstest du mal schauen, wie viel Strom das 8er Relais braucht. Mit gutem Netzteil kann der Pi einige Hundert mA über den 5V Anschluss abgeben.

      Antworten
  17. Hallo Felix,
    Danke für den tollen Artikel, via einer I2C Verbindung kann ich also max. 8 MCP23017 Bausteine ansteuern, die ich durch A0, A1, A2 adressiere.
    Ich möchte mehrere MCP23017 auf einer Leiterplatte unterbringen. Auf der einen Seite in Verbindung mit dem Raspberry über den I2C, GND, und 3,3 V Anschluss, auf der anderen Seite via Anschlußklemmen zum Schalten von Relais.
    Hast Du oder hat jemand einen Tipp, wie ein Platinenentwurf für 2 oder 1 MCP23017 auf einer Platine aussehen muß. Wie wäre ein kreuzungsfreier Entwurf oder gibt es so etwas zum Kaufen.
    Vielen Dank, André

    Antworten
  18. Hey, als erstes Kompliment für die vielen Ideen und Anregungen auf dieser Website. Ich habe mich hier mal querbeet durchgelesen aber eine Sache habe ich noch nicht explizit gefunden: Wie schließe ich andere Geräte an den Pi an, die mehr Spannung brauchen als die GPIOs zur verfügung stellen? Da geht die eigentliche Stromversorgung nicht mehr über die GPIOs sondern über eine externe Quelle, zB Batterien und ich weiß nicht wie man die mit dem Pi verbindet. Und/oder gibt es sonst eine möglichkeit, solche Motoren mit einer Powerbank zu verbinden, zwecks einfacherer Lagerung?
    Gruß, Aaron

    Antworten
  19. Hey Felix,
    erstmal vielen vielen Dank und zwar für alle deine Tutorials.
    Ich sag mal leise mein kleines Projekt baue ich quasi auf deinen Tutorials auf ;-).

    So nun habe ich aber ebenfalls eine kleine Frage:
    Ich möchte gerne das KeyPad (4×4) via dem MCP betreiben.
    In deiner KeyPad Datei / Script wird via GPIO die jeweiligen Eingänge auf pull up bzw. down gelegt.
    Abwechselnd für row und für col um festzustellen welche Taste betätigt wird.

    Nun ich wollte jetzt nicht die ganze KeyPad Datei umschreiben.

    Gibt es einen einfachen Ansatz das via MCP zu realisieren?
    Vielen Dank schon mal für deine Mühen und nochmals Vielen Vielen Dank für die Tutorials.

    Gruß

    Alex

    Antworten
    • Hallo Felix,

      Also ich habe nun, wie manch anderer, ebenfalls meine Gedanken weiter schweifen lassen.
      Ich würde sagen das es vermutlich mit bus.write_byte_data(DEVICE,GPIOA,1) [aus Tutorial 2 erweitert] klappen könnte …..

      Liege ich damit falsch ?!?

      Antworten
      • Ja, du kannst mit read und write ja die zusätzlichen Pins auslesen und beschreiben. Das müsstest du allerdings im Code der Keypad Matrix anpassen.
        PS: Danke für das Lob 🙂

  20. Hi Felix

    wie auch alle die Anderen eine super Beschreibung. Es funktioniert bei mir auch so wie du beschrieben hast, nur nach einiger Zeit kann ich den MCP nicht mehr ansprechen und er hat die gesetzten Bits vergessen? Ich habe die Verkabelung mehrfach kontrolliert. Wenn ich den Raspi neu starte geht alles wieder und dann nach eine halben Stunde oder so geht nix mehr.
    Hast du eine Idee. PS ich habe schon 2 mal den MCP gewechselt.

    Antworten
  21. Hallo,

    erstmal vielen Dank für die tolle Anleitung.
    Eine Frage bezüglich der 3,3V Versorgung. Der MCP23017 kann ja auch problemlos an einem Arduino mit 5V betrieben werden. Wenn ich nun gerne 5V Ausgangssignal am Pi hätte könnt ich den MCP23017 doch einfach mit 5V statt 3,3V versorgen.
    Damit der I2C Bus am Pi dann nicht zu viel Spannung vom MCP23017 bekommt kommt vor dem Pi ein Pegelwandler in den I2C-Bus. (z.B. der hier: http://bit.ly/2xXZEiN )

    Funktioniert das so ohne das etwas kaputt geht?

    Antworten

Hinterlasse einen Kommentar

Deine Email Adresse wird nicht veröffentlicht.

Blog abonnieren

Abonniere Tutorials-RaspberryPi, um kein Tutorial mehr zu verpassen!