Raspberry Pi: I2C-Konfiguration und -Programmierung

Prof. Jürgen Plate

Raspberry Pi: I2C-Konfiguration und -Programmierung

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 ist ein serieller Datenbus, der über eine Datenleitung (SDA) und eine Taktleitung (SCL) verfügt und ein einfaches Übertragungsprotokoll verwendet (siehe auch Serielle Schnittstelle, USB, SPI, I2C, 1-Wire). Es handelt sich um einen Master-Slave-Bus, der mit einer 7-Bit-Adresse 112 Geräte (Slaves) ansprechen kann (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 1
Ab 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 pi
Sie 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="i2cdev*", 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

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.

Konfiguration

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-dev
In 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_dev
Mit 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_compress
Tauchen 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=on
Siehe auch das Device Tree Kapitel.

Einsatz der I2C-Tools

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 C
Danach 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

Mit dem Befehl i2cdetect -y 1 wird der I2C-Bus 1 nach angeschlossenen Geräten durchsucht:

i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- 21 -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Es wird genau ein Slave mit der Adresse 0x21 angezeigt.

Mittels i2cdetect können Sie sich auch ganz allgemein die Busse auflisten lassen:

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. Der Parameter "-y" sorgt (auch bei den anderen Tools) dafür, dass das Kommando sofort ausgeführt wird. Im anderen Fall will das Programm noch eine Bestätigung an der Tastatur.

Die Register des Slave können mit den I2C-Tools auch direkt geschrieben und gelesen werden. Mit den beiden Befehlen i2cget (lesen) und i2cset (schreiben) kann direkt über den I2C-Bus auf ein Device zugegriffen werden. Ein Lese-Befehl sieht beispielsweise folgendermaßen aus:

i2cget -y 1 0x21
Die "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.

Soll in das Device geschrieben werden, lautet der Befehl:

i2cset -y 1 0x21 0x00
Die "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".

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

Hardware-Tipp

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.

Standard-Steckerbelegung für Projekte

Bei Raspberry-Pi-Projekten, die den I2C-Bus verwenden wird eine Standard-Steckerbelegung verwendet - außer, es sprechen wirklich sehr wichtige Gründe dagegen. Das macht es möglich, für mehrere I2C-Boards eine Busplatine einzusetzen, auf die alle Boards gestreckt werden. Außerdem gibt es weniger Verdrahtungsfehler beim Anstecken neuer Platinen. Es wird eine einreihige Pfostenleiste (gewinkelt) verwendet, deren Stifte mit dem Rand der Platine abschließen. Das folgende Bild zeigt die Lage der Stiftleiste und Vorschläge für Standard-Platinengrößen (50 x 80 mm2 oder 100 x 80 mm2).

Die Anschlussleiste der Platine ist dann folgendermaßen belegt (Zählung beginnt unten):

1INTInterruptleitung oder anderes
2GNDMasse
1SCLserial clock
1SDAserial data
1+3,3 V3,3 V vom Raspi-Board oder extern
1+V VV V vom Raspi-Board oder extern

Am Raspberry Pi GPIO-Port werden diese Pins dann folgendermaßen angeschlossen: +5 V an Pin 2, GND an Pin 6, SDA an Pin 3 und SCL an Pin 5.

Die Busplatine wird extern mit +5 V versorgt. Ein Low-Drop-Spannungsregler auf der Platine erzeugt dann die 3,3 V. Optional sind auch zwei Abschlusswidertände für SCL und SDA vorgesehen, die beim RasPi aber nicht bestückt werden, da sie sich schon auf den Board des RasPi befinden. Insofern ist die Schaltung sehr überschaubar:

Mit dem Raspberry-Pi-GPIO-Port wird das Bus-Board dann nur noch über vier Pins angeschlossen: GND an Pin 6, SDA an Pin 3, SCL an Pin 5 und INT nach Bedarf. Das Layout ist hier großzügig bemessen, damit man gegebenenfalls noch mit einem Oszi-Tastkopf an die einsteckten Boards kommt - auch wenn mehrere Boards stecken.

Die Eagle-Dateien für den Bus sind über die folgenden Links abrufbar. Weil die Platine zunächst auf die Schnelle entworfen wurde, ohne vorher den Schaltplan mit Eagle zu zeichnen, funktioniert hier die Forward-Back-Annotation nicht.

Programmierung in Python und C

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-smbus
Im 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)

if __name__ == "__main__":
   main()

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:

Vorsicht ist auch bei den 16-Bit-Operationen geboten. Die ARM-Architektur verwendet little endian. Sollte mit big endian gearbeitet werden, müssen höherwertiges- und niederwertiges Byte vertauscht werden. Das kann man durch folgende Anweisung erreichen:
Wert = ((Wert << 8) & 0xFF00) + (Wert >> 8)

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

if __name__ == "__main__":
   main()

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.

Links


Copyright © Hochschule München, FK 04, Prof. Jürgen Plate
Letzte Aktualisierung: