![]() |
Raspberry Pi: I2C-Konfiguration und -ProgrammierungProf. Jürgen Plate |
In Embedded-Systemen sind Sensoren und Aktoren oft mit dem I2C-Bus angebunden. Linux auf dem Raspberry Pi unterstützt dies mit einem eigenen Subsystem. I2C (Aussprache: "i-quadrat-c", machmal auch "i-zwei-c")ist ein serieller Master-Slave-Datenbus, der für die Kommunikation über kurze Distanzen konzipiert wurde, also hauptsächlich innerhalb von Platinen oder Geräten. Die Technik stammt aus den frühen 1980er-Jahren und wurde ursprünglich von Philips (heute NXP Semiconductors) für den Einsatz in der Unterhaltungselektronik entwickelt. Die Datenübertragung erfolgt synchron über zwei Leitungen: eine Datenleitung (SDA) und eine Taktleitung (SCL), auf denen ein einfaches Übertragungsprotokoll abläuft (siehe auch Serielle Schnittstelle, USB, SPI, I2C, 1-Wire).
Beide Leitungen werden durch Pullup-Widerstände auf ein positives Potenzial gezogen, die I2C-Chips haben Open-Collector-Ausgänge. Ein Bus-Master gibt Takt und Betriebsmodus vor und initiiert die byteweise erfolgende Kommunikation. Der I2C-Master kann bis zu 112 Geräte (Slaves) mit einer 7-Bit-Adresse ansprechen (eine Erweiterung des Busses verwendet 10 Bit für bis zu 1136 Geräte). Für den Bus existieren auf dem Markt die unterschiedlichsten Devices, darunter Temperatursensoren, Echtzeituhr, Portexpander, A/D- und D/A-Wandler sowie Bausteine der Unterhaltungselektronik.
Eine Abwandlung von I2C ist der SMBus, der System Management Bus. Er ist hardwaretechnisch identisch mit I2C, definiert darauf aber ein anderes Übertragungsprotokoll. Er findet sich auf fast allen modernen PC-Boards, um z. B. die Temperatur der CPU zu messen. Linux stellt für diese Variante eine Reihe zusätzlicher Zugriffsfunktionen bereit.
Die I2C-Devices (Clients) nehmen Daten vom Master entgegen und schicken abhängig vom übertragenen Kommando eine Antwort zurück. Kommandos, Parameter und Antworten sind für jedes Device unterschiedlich - insofern gibt es da auch kein einheitliches Programmierschema. Der Linux-Kernel bedient sie, sofern vorhanden, mit einem eigenen I2C-Client-Treiber (Beispiel RTC mit DS1307). Neben diesen I2C-Client-Treibern gibt es auch noch die I2C-Adapter-Treiber. Diese sind für Versand und Empfang der Daten über die beiden Leitungen zuständig. Da I2C recht weit verbreitet ist, unterstützt die Hardware vieler Prozessoren diesen Vorgang. Fehlt diese Unterstützung, kann man aber auch zwei GPIO-Pins verwenden und die seriellen Signale selbst generieren. Der Fachmann spricht von "Bit-Banging".
Der erste I2C-Kanal des RasPi (i2c-0) ist mit zwei Lötaugen des Steckers P5 verbunden, er wird auch für die Raspberry-Pi-Kamera verwendet. Bleibt für allgemeine Aufgaben der zweite Kanal (i2c-1), der an den Pins 3 (SDA) und 5 (SCL) der Doppelstiftleiste P1 angeschlossen ist (siehe Bild). Er ist gut zu erreichen und ermöglicht den Anschluss beliebiger I2C-Peripherie. Beachten Sie dabei, dass auch diese beiden Anschlüsse der Stiftleiste wie auch alle anderen nur mit maximal 3,3 V beaufschalgt werden dürfen.
Mit i2c_dev besitzt der RasPi-Kernel einen generischen Treiber, über den man auf I2C-Devices zugreifen kann. Auf dem Raspberry Pi unter Raspbian lädt man den Treiber für Tests über das Kommando modprobe i2c_dev in den Kernel. Weiter unten wird beschrieben, wie man das Laden des Treibers beim Bootvorgang automatisieren kann. Ist der Treiber geladen, stellt er für jeden I2C-Kanal im Verzeichnis /dev eine Gerätedatei zur Verfügung (/dev/i2c-0 und /dev/i2c-1). Im Notfall kann man die Gerätedateien als Root auch von Hand anlegen:
mknod /dev/i2c-0 c 89 0 mknod /dev/i2c-1 c 89 1Ab Revision B des Raspberry Pi ist immer der Bus 1 (/dev/i2c-1) die richtige Wahl.
Diese Gerätedateien kann man als Programmierer per open() öffnen. Über das Filehandle gibt man dann per iooctl()-Aufruf die gewünschten Spezifikation einer bestimmten I2C-Adresse dem I2C-Core bekannt. Das folgende C-Beispiel dient hier nur der Erläuterung, weiter unten wird die Programmierung dann ausführlicher behandelt:
if ((file = open("/dev/i2c-1", O_RDWR)) < 0) { perror("Failed to open the i2c bus"); exit(1); } if (ioctl(file, I2C_SLAVE, 0x20) < 0) { perror("Failed to acquire bus access and/or talk to slave.\n"); exit(1); }
Anmerkung: Wenn es beim Öffnen des Device eine Fehlermeldung gibt, liegt es meist an den Zugriffsrechten. Tragen Sie den User "pi" (oder den User, der mit I2C arbeiten soll) in der Gruppe "i2c" ein:
sudo usermod -aG i2c piSie können aber auch dauerhaft dafür sorgen, dass die Zugriffsrechte beim Bootvorgang von System festgelegt werden. Hier stützt man sich auf das udev-Subsystem. Normalerweise erstellt es für jedes neue Gerät eine Gerätedatei im Verzeichnis /dev. Man kann aber auch weitere Regeln angeben, indem man im Verzeichnis /etc/udev/rules.d eine Steuerdatei anlegt. Der numerische Präfix des Dateinamens regelt dabei die Reihenfolge der Abarbeitung der Dateien. Für I2C legen Sie im o. g. Verzeichnis die Datei 51-i2c.rules an und tragen darin die folgende Regel ein:
KERNEL=="i2c*", GROUP="users", MODE="0660"Damit sind die entsprechenden Devices für die Gruppe "users" mit Lese- und Schreibrecht versehen. Jetzt müssen Sie nur noch den udev-Daemon von den Änderungen wissen lassen (beim nächsten Reboot passiert das dann automatisch):
sudo service udev restart(Andere Distributionen mit systemd verwenden stattdessen das Kommando systemctl restart systemd-udevd)
Anschließend lassen sich I2C-Pakete per read()- und write()-Funktion zwischen dem Treiber und dem am Bus hängenden Device verschicken. Dabei muss man das Protokoll selbst implementieren. Will z. B. ein Programm Daten lesen, muss es zuerst das passende I2C-Kommando per write() an das device senden. Danach kann die Antwort per read() gelesen werden. Das Demoprogramm zur RTC mit DS1307 zeigt das sehr schön.
Das Kernelmodul für die I2C-Funktionen des Raspberry Pi ist standardmäßig deaktiviert. Um I2C nutzen zu können, müssen Sie es zunächst aktivieren. Alle Aktionen sind nur für den Root-User erlaubt, deshalb wechseln Sie gleich mal mittels sudo su in die Administrator-Identität.
Zuerst wird die Datei /etc/modprobe.d/raspi-blacklist.conf mit einem Editor bearbeitet, indem Sie das Kommentarzeichen (#) vor den Eintrag setzen. Die Datei sollte danach folgenden Inhalt haben:
$ cat /etc/modprobe.d/raspi-blacklist.conf # blacklist spi and i2c by default (many users don't need them) # blacklist spi-bcm2708 # blacklist i2c-bcm2708
Anschließend erweitern Sie die Datei /etc/modules um folgenden Eintrag:
i2c-devIn der Regel ist bis dahin nur die Zeile snd-bcm2835 enthalten. Nun können Sie die Modul schon einmal probeweise laden:
modprobe i2c-bcm2708 modprobe i2c_devMit dem Befehl lsmod wird nun überprüft, ob beide Module geladen wurden. Es sollte sich in etwa folgende Ausgabe ergeben:
Module Size Used by i2c_dev 5557 0 spi_bcm2708 4728 0 i2c_bcm2708 3997 0 snd_bcm2835 16165 0 snd_soc_bcm2708_i2s 5474 0 regmap_mmio 2806 1 snd_soc_bcm2708_i2s snd_soc_core 131268 1 snd_soc_bcm2708_i2s regmap_spi 1897 1 snd_soc_core snd_pcm 81593 2 snd_bcm2835,snd_soc_core snd_page_alloc 5156 1 snd_pcm regmap_i2c 1645 1 snd_soc_core snd_compress 8076 1 snd_soc_core snd_seq 53769 0 snd_timer 20133 2 snd_pcm,snd_seq snd_seq_device 6473 1 snd_seq leds_gpio 2059 0 led_class 3688 1 leds_gpio snd 61291 7 snd_bcm2835,snd_soc_core,snd_timer,snd_pcm,snd_seq,snd_seq_device,snd_compressTauchen beide Module in der Liste auf, ist alles in Ordnung. Durch den Eintrag in der Datei /etc/modules erfolgt das Laden der Kernelmodule automatisch beim Booten. Nach dem Booten (bzw dem Laden der Module von Hand) sollten auch zwei neue Gerätedateien sichtbar sein:
$ ls -l /dev/i2c-* crw-rw---T 1 root i2c 89, 0 Feb 25 16:10 /dev/i2c-0 crw-rw---T 1 root i2c 89, 1 Feb 25 16:10 /dev/i2c-1
Angeschlossen wird der I2C-Bus an der Stiftleiste des Boards (siehe auch RasPi_GPIO) und zwar an den Pins 3 (I2C SDA), 5 (I2C SCL) und 6 (Masse). Beachten Sie, dass dies seit Raspberry Pi Revision 2.0 der I2C-Bus Nummer 1 ist. Bei älteren Boards hat er dagegen die Nummer 0. Beim Raspberry Pi Revision 2.0, gibt es einen weiteren GPIO-Anschluss, P5, bei dem der I2C-Bus Nummer 0 an den Pins 3 (I2C SDA) und 4 (I2C SCL) herausgeführt ist.
Beim Raspberry Pi 2 ab Kernelversion 3.18 sind zusätzlich zwei Zeilen in der Datei /boot/config.txt notwendig, um Zugang zum I2C-Bus zu erhalten:
dtparam=i2c1=on dtparam=i2c_arm=onSiehe auch das Device Tree Kapitel.
Will man den User "pi" und eventuell auch den Webserver ohne Root-Gefickel auf den Bus schreiben lassen, kann man beide User der Gruppe "i2c" hinzufügen. Das kann entweder durch Bearbeiten der Datei /etc/group geschehen oder durch die folgenden Kommandos (zur Erinnerung, wie sind immer noch Root):
adduser pi i2c adduser www-data i2c
Der I2C-Treiber der Raspbian-Version vom Oktober 2017 meldet I/O-Errors beim Zugriff auf manche Sensoren. Meine Theorie ist, dass entweder das I2C-Device "repeated starts" nicht unterstützt oder dass der I2C-Treiber das "Clock Stretching" nicht implementiert hat. Für genauere Analyse fehlte mir die Zeit. Es gibt aber eine Lösung:
dtoverlay=i2c1-bcm2708
Um die Verbindung testen zu können, wird die Tool-Sammlung i2c-tools benötigt, die mit folgendem Befehl installiert wird (ein Update der Paketlisten vorab lohnt sich immer). Es sind auch noch zwei weitere Pakete aufgelistet, die Sie eventuell brauchen könnten:
apt-get update apt-get install i2c-tools # I2C-Toolkit fuer die Kommandozeile apt-get install python-smbus # Python-Bibliothek fuer I2C apt-get install libi2c-dev # Bibliothek fuer CDanach finden Sie vier neue Programme im Verzeichnis /usr/sbin/:
ls -l /usr/sbin/i2c* -rwxr-xr-x 1 root root 14912 Jul 26 2012 /usr/sbin/i2cdetect -rwxr-xr-x 1 root root 19328 Jul 26 2012 /usr/sbin/i2cdump -rwxr-xr-x 1 root root 14444 Jul 26 2012 /usr/sbin/i2cget -rwxr-xr-x 1 root root 18268 Jul 26 2012 /usr/sbin/i2cset
i2cdetect versucht, den I2C-Bus nach Devices abzuscannen. Es gibt eine Tabelle mit der Liste der erkannten Geräte auf dem angegebenen Bus aus. Da es keinen Standard-I2C-Erkennungsbefehl gibt, verwendet i2cdetect verschiedene SMBus-Befehle (primär SMBus quick write and SMBus receive byte), um Devices zu erkennen. Standardmäßig wird immer ein Befehl verwendet, der vermutlich der sicherste für die jeweilige Chip-Adresse ist. Die Optionen -q und -r ändern dieses Verhalten. i2cdetect kann auch verwendet werden, um die Funktionalitäten eines I2C-Busses abzufragen.
Kommandosyntax:
i2cdetect [-y] [-a] [-q|-r] i2cbus [first last] i2cdetect -F i2cbus i2cdetect -V i2cdetect -l
Optionen
Option | Aufgabe |
---|---|
-V | Version anzeigen and exit. |
-y | Deaktivieren des interaktiven Modus. Standardmäßig wartet i2cget auf eine Bestätigung des Benutzers bevor es auf den I2C-Bus zugreift. Wird diese Option verwendet, beginnt die Ausführung sofort. |
-F | Zeigt die Liste der vom Adapter implementierten Funktionalitäten an. |
-l | (Kleinbuchstabe "l") Zeigt die Liste der installierten Busse an. |
-q | Verwendet den SMBus "quick write"-Befehl zum Sondieren. Nicht empfohlen. Unter anderem werden damit die Atmel AT24RF08 EEPROMs auf IBM Thinkpad Laptops beschädigt. |
-r | Verwendet SMBus "receive byte"-Befehl zum Sondieren. Nicht empfohlen. Blockiert u. U. den SMBus auf verschiedenen "write-only"_Chips (insbesondere Chips an der Adresse 0x69). |
-a | Erzwingt das Scannen von nicht regulären Adressen. Nicht empfohlen. |
Der Parameter i2cbus gibt die Nummer oder den Namen des zu scannenden I2C-Busses an. Diese Nummer sollte einem der von i2cdetect -l aufgeführten Bus entsprechen (beim Raspberry Pi ist das immer "1").
Die optionalen Parameter first und last schränken den Scanbereich ein (Default: 0x03 bis 0x77).
Jede Zelle in der Ausgabetabelle enthält eines der folgenden Symbole:
Beispiele:
i2cdetect -F 1 Functionalities implemented by /dev/i2c-1: I2C yes SMBus Quick Command yes SMBus Send Byte yes SMBus Receive Byte yes SMBus Write Byte yes SMBus Read Byte yes SMBus Write Word yes SMBus Read Word yes SMBus Process Call yes SMBus Block Write yes SMBus Block Read no SMBus Block Process Call no SMBus PEC yes I2C Block Write yes I2C Block Read yes i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- 21 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- i2cdetect -l i2c-0 i2c bcm2708_i2c.0 I2C adapter i2c-1 i2c bcm2708_i2c.1 I2C adapter
Das zweite Programm, i2cdump, ist ein Hilfsprogram, das es erlaubt alle Register eine Slave über den Bus auszulesen. Je nach angesprochenem Chip klappt das mal mehr mal weniger gut.
Kommandosyntax:
i2cdump [-f] [-r first-last] [-y] i2cbus chip-address [mode [bank [bankreg]]] i2cdump -V
Optionen
Option | Aufgabe |
---|---|
-V | Version anzeigen and exit. |
-f | Erzwingen des Zugriffs auf das Device, auch wenn es bereits belegt ist. Normalerweise weigert sich i2cdump, auf ein Gerät zuzugreifen, das bereits unter der Kontrolle eines Kernel-Treibers ist. Zugriff mit dieser Option birgt die Gefahr, dass der Konflikte mit dem entsprechenden Kerneltreiber auftreten oder dass i2cdump fehlerhafte Werte zurückgibt. Verwendung auf eigenes Risiko! |
-y | Deaktivieren des interaktiven Modus. Standardmäßig wartet i2cget auf eine Bestätigung des Benutzers bevor es auf den I2C-Bus zugreift. Wird diese Option verwendet, beginnt die Ausführung sofort. |
-r first-last | Beschränkt den Bereich der Register, auf die zugegriffen wird. Diese Option ist nur mit den mode-Angaben 'b', 'w' und 'c' möglich. |
Es gibt zwei obligatorische Parameter für i2dump. i2cbus gibt die Nummer oder den Namen des zu scannenden I2C-Busses an. Diese Nummer sollte einem der von i2cdetect -l aufgeführten Bus entsprechen (beim Raspberry Pi ist das immer "1"). chip-address spezifiziert die Adresse des Chips auf diesem Bus und ist eine Ganzzahl zwischen 0x03 und 0x77.
Der optionale Parameter mode ist einer der Buchstaben 'b', 'w', 's' oder 'i', je nachdem, ob ein Byte (8 Bit) oder ein Wort (16 Bit) gelesen werden soll. 's' steht für das Lesen eines SMBus-Blocks und 'i' für ein I2C-Blocklesen. Wird als mode der Buchstabe 'c' angegeben, liest das Programm fortlaufend. Das ist nur sinnvoll bei Chips mit automatischem Register-Inkrement, wie z. B. EEPROMs. Mit Ausnahme von 'i' kann auch noch ein 'p' angehängt werden, um PEC zu aktivieren.
Die optionalen Parameter bank und bankreg sind Beim W83781D und ähnlichen Chips nützlich. bank ist eine Ganzzahl zwischen 0 und 7 und bankreg ist eine Ganzzahl zwischen 0x00 und 0xFF (Standardwert: 0x4E).
Die Register des Slave können mit den I2C-Tools auch direkt geschrieben und gelesen werden. Mit demBefehl i2cget kann direkt lesend über den I2C-Bus auf ein Device zugegriffen werden.
Kommandosyntax:
i2cget [-f] [-y] i2cbus chip-address [data-address [mode]] i2cget -V
Optionen
Option | Aufgabe |
---|---|
-V | Version anzeigen and exit. |
-f | Erzwingen des Zugriffs auf das Device, auch wenn es bereits belegt ist. Normalerweise weigert sich i2cget, auf ein Gerät zuzugreifen, das bereits unter der Kontrolle eines Kernel-Treibers ist. Zugriff mit dieser Option birgt die Gefahr, dass der Konflikte mit dem entsprechenden Kerneltreiber auftreten oder dass i2cget einen ungültigen Wert zurückgibt. Verwendung auf eigenes Risiko! |
-y | Deaktivieren des interaktiven Modus. Standardmäßig wartet i2cget auf eine Bestätigung des Benutzers bevor es auf den I2C-Bus zugreift. Wird diese Option verwendet, beginnt die Ausführung sofort. |
Es gibt zwei obligatorische Parameter für i2cget. i2cbus gibt die Nummer oder den Namen des zu scannenden I2C-Busses an. Diese Nummer sollte einem der von i2cdetect -l aufgeführten Bus entsprechen (beim Raspberry Pi ist das immer "1"). chip-address spezifiziert die Adresse des Chips auf diesem Bus und ist eine Ganzzahl zwischen 0x03 und 0x77.
Der optionale Parameter data-address legt die (Register-)Adresse auf dem Chip fest, von der gelesen werden soll. Es ist eine Ganzzahl zwischen 0x00 und 0xFF. Fehlt data-address, wird aus dem aktuell eingestellten Register gelesen (wenn das für den betrachteten Chip sinnvoll ist).
Der optionale Parameter mode ist einer der Buchstaben 'b', 'w' oder 'c', je nachdem, ob ein Byte (8 Bit) oder ein Wort (16 Bit) gelesen werden soll. der Buchstabe 'c' steht für eine Write/Read-Operation. Wird ein 'p' an die mode-Angabe angehängt, dient dies zur Aktivierung von PEC. Fehlt der Parameter mode, wird standardmäßig ein Byte gelesen.
Beispiele:
Ein Lese-Befehl sieht beispielsweise folgendermaßen aus:
i2cget -y 1 0x21Die "1" besagt, dass auf den I2C-Bus Nummer 1 zugegriffen wird und und "0x21" ist die Adresse des Device. Als Rückgabewert erhält man dann auf der Konsole das ausgelesene Byte.
Bei komplexen Chips versagt das einfache Verfahren, aber bei einfachen Komponenten klappt es wunderbar. So kann beispielsweise die Echtzeituhr auf Adresse 0x68 ausgelesen werden:
i2cget -y 1 0x68 0 # Sekunde i2cget -y 1 0x68 1 # Minute i2cget -y 1 0x68 2 # StundeDen Temperatursensor LM75 kann man auch ganz leicht auslesen:
$ i2cget -y 1 0x48 0x00 w 0x8019Hier wird eine 16-Bit-Variable zurückgeliefert, die noch zu analysieren ist. Das MSB (0x80) besagt, dass zum Temperaturwert im LSB noch 0,5 Grad hinzuaddiert werden müssen. Der LSB-Wert von 0x19 ergibt dezimal 25. Also haben wir 25,5 Grad Celsius.
Die Register des Slave können mit den I2C-Tools auch direkt geschrieben werden. Mit dem Befehl i2cset kann direkt über den I2C-Bus auf ein Device schreibend zugegriffen werden.
Kommandosyntax:
i2cset [-f] [-y] [-r] i2cbus chip-address data-address [value] ... [mode] i2cset -V
Optionen
-V | Version anzeigen and exit. |
-f | Erzwingen des Zugriffs auf das Device, auch wenn es bereits belegt ist. Normalerweise weigert sich i2cset, auf ein Gerät zuzugreifen, das bereits unter der Kontrolle eines Kernel-Treibers ist. Zugriff mit dieser Option birgt die Gefahr, dass der Konflikte mit dem entsprechenden Kerneltreiber auftreten oder dass i2set in ein falsches Register schreibt. Verwendung auf eigenes Risiko! |
-y | Deaktivieren des interaktiven Modus. Standardmäßig wartet i2cset auf eine Bestätigung des Benutzers bevor es auf den I2C-Bus zugreift. Wird diese Option verwendet, beginnt die Ausführung sofort. |
-r | Zurücklesen des Werts direkt nach dem Schreiben und Vergleichen des Ergebnisses mit dem geschriebenen Wert. Dies war früher das Standardverhalten. |
Es gibt drei obligatorische Parameter für i2cset. i2cbus gibt die Nummer oder den Namen des zu scannenden I2C-Busses an. Diese Nummer sollte einem der von i2cdetect -l aufgeführten Bus entsprechen (beim Raspberry Pi ist das immer "1"). chip-address spezifiziert die Adresse des Chips auf diesem Bus und ist eine Ganzzahl zwischen 0x03 und 0x77. data-address legt die (Register-)Adresse auf dem Chip fest, auf welche geschrieben werden soll. Es ist eine Ganzzahl zwischen 0x00 und 0xFF.
Der optionale Parameter value ist der Wert, der in das angegebene Register geschrieben wird. Wenn dieser Parameter weggelassen wird, dann handelt es sich um ein "short write". Bei den meisten Chips setzt dies einfach einen internen Zeiger auf das Zielregister, ohne wirklich in dieses Register zu schreiben.
Der optionale Parameter mode ist einer der Buchstaben 'b', 'w', 's' oder 'i', je nachdem, ob ein Byte (8 Bit) oder ein Wort (16 Bit) geschrieben werden soll. 's' steht für das Schreiben eines SMBus-Blocks und 'i' für ein I2C-Blockwrite. Für die Blockwrite-Operationen wird die Blockgröße durch die Anzahl der angegeben Werte festgelegt. Mit Ausnahme von 'i' kann auch noch ein 'p' angehängt werden, um PEC zu aktivieren. Fehlt der Parameter mode, wird standardmäßig ein Byte geschrieben.
Beispiel:Soll in das Device geschrieben werden, lautet der Befehl:
i2cset -y 1 0x21 0x00Die "1" besagt wieder, dass auf den I2C-Bus Nummer 1 zugegriffen wird und und "0x21" ist wieder die Adresse des Device. Am Schluss folgt der zu schreibende Wert, im Beispiel "0x00".
Der Baustein PCF8574 ist ein Ein-/Ausgabechip. Abweichend von anderen I2C-Chips hat der PCF8574 keine Register. Ein Byte, das in den PCF8574 geschreiben wird beeinflusst sofort die digitalen Ausgänge.
i2cset -y 1 0x20 0xEFSetzt den P4-Pin auf LOW und alle anderen Pins des Chips auf HIGH.
Aus Copyrightgründen ist die Bezeichnung "I2C" geschützt, weshalb oft vom "SMBus" (System Management Bus) die Rede ist. Es gibt zwar einige marginale Unterschiede, aber wenn auf I2C-Hardware zugegriffen wird, sollte man nach Möglichkeit die SMBus-Kommandos verwenden. Beachten Sie auch, dass die Adresse ein 7-Bit-Wert ist (0 ... 127), der bei der Übertragung um eine Stelle nach links geschoben und um das Read-Write-Bit ergänzt wird.
Das SMBus-Protokoll von Linux bietet einen relativ starren Funktionsumfang, mit dem manche Anwendungsfälle nicht umgesetzt werden können, für Standard-Devices reicht er aber auf jeden Fall aus. Es gibt sowohl für C wie für Python passende Bibliotheken, welche die Programmierung vereinfachen. Trotzdem ist es an dieser Stelle unmöglich, eine wirklich allgmeingültige Anleitung zu geben - ganz einfach, weil jedes Device anders angesprochen werden muss. Wer nach Beispielen sucht, muss sich gegebenenfalls durch die diversen Raspberry-Pi-Projekte durcharbeiten.
Bei der Python-Programmierung benötigen Sie die Klasse smbus. Deshalb muss gegenbenfalls das Paket python-smbus nachinstalliert werden mit:
sudo apt-get install python-smbusIm Programm wird dann mittels import smbus die Klasse geladen und ist verwendbar. Vor dem Zugriff auf I2C-Devices muss mit der Anweisung dev = smbus.SMBus(1) die Verbindung zum I2C-Bus hergestellt werden. Bevor auf die einzelnen Methoden eingegangen wird, möchte ich ein kurzes Beispiel aus dem Expander-Projekt vorstellen, aus dem die Zusammenhänge deutlich werden. Grundsätzlich wird ein Device immer über seine Adresse angesprochen. Jedoch enthalten die meisten Bausteine eine ganze Anzahl von Registern, die einzeln adressiert werden müssen. Deshalb besteht der Schreibbefehl für ein Byte in der Regel aus der Angabe von Device, Register und Daten. Der Lesebefehl hat entsprechend Device und Register als Parameter.
#!/usr/bin/python import smbus import time # I2C-Adresse des MCP23017 address = 0x20 # Erzeugen einer I2C-Instanz und Öffnen des Busses mcp23017 = smbus.SMBus(1) # Konfiguration des MCP23017 mcp23017.write_byte_data(address,0x00,0x00) # Bank A Output mcp23017.write_byte_data(address,0x01,0xFF) # Bank B Inputs # Blinkeschleife while True: # alle LEDs aus mcp23017.write_byte_data (address,0x12,0x00) time.sleep(1) # alle LEDs an mcp23017.write_byte_data (address,0x12,0xFF) time.sleep(1)
Die Python-Klasse besteht aus einem guten Duzend von Methoden, die im Folgenden knapp behandelt werden. Die Namen sind übrigens von den C-Funktionen abgeleitet, so dass diese Liste auch für die C-Programmierer interessant ist:
Wert = ((Wert << 8) & 0xFF00) + (Wert >> 8)
Der oben erwähnte Temperatursensor LM75 läßt sich nicht nur mit i2cget, sondern natürlich auch mit Python auslesen:
#!/user/bin/env python import time import smbus bus = smbus.SMBus(0) data = bus.read_i2c_block_data(0x48, 0) # Daten kommen binaer - [Lowbyte, Highbyte] TempMSB = data[0] TempLSB = data[1] Temp = (((TempMSB << 8) | TempLSB) >>7) * 0.5 # negativ? if Temp > 125: Temp = Temp - 256) print Temp
Das folgende Beispiel demonstriert den Zugriff auf ein EEPROM vom Typ 24C02. Hier sehen Sie, wie das Blockweise Schreiben angewendet wird.
#!/usr/bin/python import smbus import sys import time # EEPROM-Adresse, Register address = 0x50 reg = 0x01 # "Hello World\0" data = [ 0x48, 0x6e, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x00 ] eeprom = smbus.SMBus(1) print "I2C: Schreiben auf Device 0x%02X" % address try: eeprom.write_i2c_block_data(address, reg, data) except IOError, err: print "Fehler beim Schreiben auf Device 0x%02X" % address exit(-1) # Erholungszeit fuer EEPROM time.sleep(0.2) print "I2C: Lesen von Device 0x%02X" % address for i in range (len(data)): try: ret = eeprom.read_byte_data(address, reg + i) except IOError, err: print "Fehler beim Lesen von Device 0x%02X" % address exit(-1) print "%c" % ret
Bei C läuft es beinahe genauso geschmeidig ab wie in Python. Nun bei Öffnen des Device gibt es eine kleine Hürde. Nach dem open() muss mittels ioctl()-Aufruf die Kommunikation frei gegeben werden. Wichtig ist die Headerdatei i2c-dev.h, die für die I2C-Funktionen benötigt wird. Dieser Header ist eigentlich nur dann verfügbar, wenn das Paket libi2c-dev mittels apt-get installiert wurde. Ist nur die Headerdatei da, hagelt es Fehlermeldungen beim Compilieren.
Das Öffnen des Device sieht dann folgendermaßen aus:
/* die ueblichen Headerdateien */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/i2c-dev.h> int fd; /* Device oeffen */ if ((fd = open(expander.I2CBus, O_RDWR)) < 0) { perror("Failed to open the i2c bus\n"); exit(1); } /* Spezifizieren der Slave-Adresse -> Kommunikation frei geben */ if (ioctl(fd, I2C_SLAVE, expander.address) < 0) { perror("Failed to acquire bus access and/or talk to slave\n"); exit(1); }
Im Prinzip könnte man jetzt sogar mittels write() und read() auf dem Bus herumfuhrwerken. Das geht sogar gut, wenn die Kommunikation mit dem Device sehr einfach ist, wie beim Uhrenbaustein DS1307, wo ein einziges Kommando und ein Blockread genügen, um die Uhrzeit auszulesen. Das Setzen der Uhr oder das Schreiben in das EEPROM des Bausteins wäre dann aber schon eine Herausforderung. Für die beiden Funktionen write() und read() existiert nämlich ein Wrapper in der Bibliothek, wobei jeder Aufruf der Funktionen immer eine komplette Transaktion aus Startbedingung, Datentransfer und Stoppbedingung erzeugt. Insofern ist es ratsamer, die etwas komfortableren Bibliotheksfunktionen einzusetzen. Alle Funktionen liefern im Fehlerfall einen Status ungleich Null zurück. errno wird ebenfalls gesetzt. Es lohnt sich auch, das Kapitel 5.5 in den SMBus-Spezifikationen durchzulesen. Die Funktionsliste ähnelt jener von Python, so dass ich mir hier die einzelerklärungen sparen kann:
Einige Lese-Funktionen liefern einen Long-Wert (32 Bit) zurück, es ist also gegebenenfalls ein Typecast notwendig. Hier hilft ein Blick in die Headerdatei linux/i2c-dev.h, wo alles gut kommentiert ist.
Das folgende Beispiel i2ctest.c erweitert die Open-Funktionalität noch etwas. Das Programm prüft, ob auch alle notwendigen I2C-Bibliotheksfunktionen zur Verfügung stehen (über ioctl-Aufrufe). Die Funktion scan_i2c_bus() scannt den Bus nach I2C-Devices ab. Nicht so komfortabel wie das Programm aus den I2C-Tools, aber im Prinzip mit dem gleichen Ergebnis.
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <linux/i2c-dev.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* Suche nach I2C-Adressen */ void scan_i2c_bus(int device) { int port, res; /* Adressbereich 7 Bit */ for (port = 0; port < 127; port++) { if (ioctl(device, I2C_SLAVE, port) < 0) perror("ioctl() I2C_SLAVE failed\n"); else { /* kann gelesen werden? */ res = i2c_smbus_read_byte(device); if (res >= 0) printf("i2c chip found at: %x, val = %d\n", port, res); } } } int main(void) { int device; unsigned long funcs; /* Geraetedatei oeffnen */ printf("Opening device..."); if ((device = open("/dev/i2c-1", O_RDWR)) < 0) { perror("open() failed"); exit (1); } printf(" OK\n"); /* Abfragen, ob die I2C-Funktionen da sind */ if (ioctl(device,I2C_FUNCS,&funcs) < 0) { perror("ioctl() I2C_FUNCS failed"); exit (1); } /* Ergebnis untersuchen */ if (funcs & I2C_FUNC_I2C) printf("I2C\n"); if (funcs & (I2C_FUNC_SMBUS_BYTE)) printf("I2C_FUNC_SMBUS_BYTE\n"); /* und Bus abscannen */ scan_i2c_bus(device); return 0; }
Ein weiteres Beispiel bildet die Bibliothek zum I2C-IO-Expander mit MCP23017, bei der aufbauend auf den Grundfunktionen passende Funktionen für den Baustein MCP23017 geschrieben wurden. Wie schon gesagt, sind allgemein gültige Beispiele schwierig, da jedes Device anders angesprochen werden muss.
Eine weitere Unterstützung bei der C-Programmierung bietet die WiringPi-Bibliothek, deren Gebrauch und Installation an anderer Stelle beschrieben wurde. Diese Bibliothek bietet auch Funktionen für den I2C-Bus, deren Syntax sich an die entsprechenden Arduino-Funktionen anlehnt. Der Zugriff auf ein Device ist dem Zugriff auf eine Datei nachempfunden. Mit der Funktion wiringPiI2CSetup(Device-Adresse) erzeugen Sie zunächst ein Datei-Handle, auf das dann mit den anderen Funktionen zugegriffen wird.
Funktion | Aufgabe |
---|---|
int wiringPiI2CRead(int handle) | einfaches Lesen |
int wiringPiI2CWrite(int handle, int data) | einfaches Schreiben |
int wiringPiI2CReadReg8(int fd, int reg) | Lesen von 8-Bit-Werten aus einem Register |
int wiringPiI2CWriteReg8(int fd, int reg, int data) | Schreiben von 8-Bit-Werten in ein Register |
int wiringPiI2CReadReg16(int fd, int reg) | Lesen von 16-Bit-Werten aus einem Register |
int wiringPiI2CWriteReg16(int fd, int reg, int data) | Schreiben von 16-Bit-Werten in ein Register |
Manchmal ist es notwendig die Übertragungsrate des I2C-Busses zu ändern - wenn beispielsweise die Peripheriebausteine das erfordern oder man längere Kabelverbindungen benötigt. Auch bei elektromagnetischen Störungen kann eine geringere Datenrate mitunter hilfreich sein (siehe auch Serielle Schnittstelle, USB, SPI, I2C, 1-Wire).
Die Geschwindigkeit der I2C-Schnittstelle des Raspberry Pi lässt sich über das Kernel-Modul i2c_bcm2708 einstellen. Der Default-Wert der Übertragungsrate beträgt 100 kHz. Mit dem folgenden Befehl können Sie die aktuelle Übertragungsrate des Moduls auslesen. Erhalten Sie hier eine "0", handelt es sich um eine Übertragungsrate von 100 kHz.
sudo cat /sys/module/i2c_bcm2708/parameters/baudrateUm die Übertragungsrate zu ändern, muss zuerst das Kernel-Modul entladen werden. Anschließend wird es mit neuen Parametern wieder geladen. Das funktioniert nicht nur für I2C-Treiber so, sondern für fast alle ladbaren Kernel-Module. Die beiden folgenden Kommandos senken die Geschwindigkeit auf 40 kHz ab.
sudo modprobe -r i2c_bcm2708 # Modul entladen sudo modprobe i2c_bcm2708 baudrate=40000 # und wieder neu ladenAchten Sie beim Herabsetzen der Busgeschwindigkeit auch darauf, dass die Übertragungsrate für Ihre Anwendung noch ausreicht. Der Busmaster kann die Übertragungsgeschwindigkeit frei wählen. Es muss sich also nicht wie oben um einen glatten Wert handeln. Für die Fehlersuche mit dem Oszilloskop sollte man aber einen glatten Wert wählen, der sich als Zeitbasis am Oszilloskop einstellen lässt.
Sie erinnern sich vielleicht - der Raspberry Pi arbeitet mit 3,3 V. Bei der Verwendung von 5-V-Devices am Bus (oder auch 1,8-V-Devices) ist eine Pegelwandlung notwendig. Das kann man mit zwei MOSFETs und einigen Widerständen erledigen, wie es im Schnittstellen-Skript beschrieben ist, aber es gibt auch massenhaft integrierte Bausteine für diesen Zweck, etwa den I2C Level Shifter TCA9406 von Texas Instruments. Er arbeitet bidirektional, daher ist es egal, auf welcher Seite welche Spannung verwendet wird. Über einen Output-Enable-Pin kann er entweder vom Master aktiviert werden (s. Bild) oder man legt den Pin einfach auf High-Potential.