Der I²C-Bus


Grundlagen

Das I²C-Busprotokoll (Engl. Inter-Integrated~Circuit) wurde Anfang der 80er Jahre von der Firma Philips entwickelt und als serieller Bus für die Kommunikation zwischen Mikrocontrollern, ICs, sowie Ein- und Ausgabekomponenten konzipiert. Die steigende Verwendung von Mikrocontrollern in Consumer-Produkten erforderte damals eine neue Lösung, um die bislang verwendeten 8 oder 16 Bit breiten Datenbusse zu ersetzen. Durch den Einsatz des I²C-Standards konnten günstigere Controller mit weniger IOs verwendet werden. Dies machte platzsparende Platinen-Layouts möglich und führte damit zu einer Reduzierung der Produktionskosten. Mittlerweile wurde nicht nur für Consumer-Produkte zu einem Standard, sondern auch für Steuerungs- und Diagnoseaufgaben in eingebetteten Systemen im industriellen Umfeld.


I²C Konzept


I²C ist ein hierarchisches Bussystem, welches mit nur zwei Leitungen auskommt: dem Taktsignal SCL (Serial Clock) und einer weiteren Leitung zur Übertragung der Nutzdaten SDA (Serial Data). I²C ist ein bidirektionaler Zweidrahtbus mit Master-Slave-Konzept und Software-Adressierung. Die Datenübertragung erfolgt bitseriell und synchron. Jedes Datenbit auf der Datenleitung SDL wird mit einem Bit auf der Taktleitung SCL synchronisiert. Dadurch müssen die Taktzeiten nicht konstant sein, und es können sowohl langsamere als auch schnellere Busteilnehmer am gleichen Bus kommunizieren.

In der ursprünglichen Spezifikation war eine Datenübertragung mit bis zu 100 kbit/s (Standard Mode) möglich. Durch eine Anpassung an die wachsenden Anforderungen wurde dieser Wert im Jahre 1992 auf 400 kbit/s (Fast Mode) erhöht. Die Buslänge wird durch die maximal erlaubte Buskapazität von 400 pF auf nur zwei bis drei Meter begrenzt. Durch Verringerung der Kapazität, durch einen niedrigeren Bustakt oder durch Verwendung anderer physikalischer Übertragungsmedien ist es aber möglich, diese Grenze zu verschieben und damit Buslängen von bis zu 100 m und mehr zu erreichen.


Steuersignale

Für die Koordination des Datentransfers stehen bei I²C sog. Steuersignale zur Verfügung. Durch eine fallende Flanke auf SDA während SCL=1 wird der Beginn einer Sequenz dargestellt. Durch diese Vorgabe wird der Bus vom Master als belegt gekennzeichnet, sodass sich kein anderer Baustein als Master anmelden kann. Eine steigende Flanke auf SDA während SCL=1 beendet eine Übertragung und gibt den Bus frei. Wichtig ist dabei, dass während des Schreibens einer Start- oder Stop-Bedingung SCL dauerhaft auf 1 liegt, um die Steuersignale korrekt interpretieren zu können.


I²C Start-Stoppbedingung


Wurde nach dem Senden der Startbedingung das erste Byte vom Master übermittelt, so quittiert der Slave den Erhalt durch ein Bestätigungs-Bit (Acknowledge). Hierbei handelt es sich eigentlich um ein Steuersignal. Das Acknowledge-Bit wird jedoch wie ein Datensignal über SDL als logisch 0 übertragen und liegt direkt als neuntes Bit nach acht übertragenen Datenbits an. Bleibt die Bestätigung aus (SDA=1), so signalisiert der Slave damit, dass er an der Kommunikation nicht mehr teilnimmt und kein weiteres Byte empfangen kann.


I²C Übertragung


Adressierung

Die Baustein-Adressierung erfolgt in der Regel mit sieben Bits, welche der Master zusammen mit der Lese- oder Schreibinformation als erstes Byte einer Nachricht sendet. Laut I²C-Spezifikation ist ebenfalls eine Adressierung mit zehn Bits vorgesehen, um bis zu 1024 Bausteine an einem Bus betreiben zu können. Tatsächlich ist dies in der Praxis aber kaum relevant, da nur relativ wenige 10-Bit-Bausteine existieren.


I²C Adressierung


Von den mit sieben Bits theoretisch möglichen 128 Adressen sind tatsächlich nur 112 verfügbar, da einige Adressen reserviert sind. Für die Bausteinadressierung stehen in der Regel die drei niederwertigen Bits der Adresse zur Verfügung, welche oftmals auf Pins herausgeführt sind und über Jumper gesetzt werden können. Die oberen vier Bits der Bausteinadresse sind meist durch den Hersteller fest vorgegeben und definieren die jeweilige Bausteinfamilie. Für manche Bausteine existieren verschiedene Ausführungen dieser 4-Bit-Codierung, um auch einen Betrieb von mehr als acht Bausteinen des gleichen Typs an einem Bus zu ermöglichen. Sollten die Adressen nicht ausreichen, so kann der Bus mit Multiplexer-Bausteinen aufgesplittet werden.


I²C-Programmierung unter Linux

Bei Geräten mit nativer I²C-Schnittstelle wie bspw. bei der NSLU2 wird diese üblicherweise als Gerätedatei /dev/i2c-0 repräsentiert. Die notwendigen Treiber zur Umsetzung der Bitfolgen auf die physikalischen Leitungen sind bereits geladen, sodass ein Zugriff auf Dateisystemebene mit herkömmlichen Lese- und Schreiboperationen erfolgen kann.

Das freie Beispiel (siehe Embedded Linux Toolbox in Downloads) /examples/iic/iic_native zeigt den einfachen I²C-Zugriff mit C-Funktionen:

#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

int main(){

  int fd,n;
  char buf[10];

  // use this address for NSLU2 RTC clock
  int addr = 0x6F;

  // use this address for Alekto RTC clock
  //int addr = 0x68;

  if ((fd = open("/dev/i2c-0", O_RDWR)) < 0) {
    printf("open error! %d\n",fd);
    return 1;
  }

  if (ioctl(fd, I2C_SLAVE, addr) < 0) {
    printf("address error!\n");
    return 1;
  }

  buf[0] = 0x00;

  printf("write: ");
  n = write(fd, buf, 1);

  if (n != 1)
  printf("error! %d\n",n);
  else
  printf("0x%x, 0x%x\n", buf[0], buf[1]);

  printf("read: ");
  n=read(fd, buf, 1);

  if (n != 1)
    printf("read error! %d\n", n);
  else
    printf("0x%x\n", buf[0]);

  close(fd);
  return 0;
}

Außer der Header-Datei für die Standard-Dateioperationen sind für den Zugriff auf den I²C-Bus die Header-Dateien linux/i2c-dev.h und linux/i2c.h einzubinden. Nach dem Öffnen des Gerätes mit open() wird im Erfolgsfall (Rückgabewert größer 0) ein Filedescriptor zurückgegeben, der das Gerät referenziert. In diesem Fall wird der Bus mit der Option O_RDWR für Lese- und Schreibzugriffe geöffnet. Das Setzen der aktuellen Slave-Adresse geschieht mit dem ioctl()-Befehl. Diese Zuordnung bleibt aktiv bis eine neue Adresse übermittelt wird. Ein Rückgabewert = 0 bestätigt den Erfolg der Aktion. Wird anstatt der Option I2C_SLAVE der Wert I2C_SLAVE_FORCE verwendet, so fordert die Anwendung Zugriff auf einen I²C-Baustein, auch wenn der Kerneltreiber diesen gerade in Bearbeitung hat.

Nun können mit den Befehlen read() und write() Daten-Bytes geschrieben bzw. gelesen werden, wobei die I²C-Adresse nicht mehr angegeben werden muss. Als Attribute werden Filedescriptor fd, ein Zeiger buf vom Typ char* und die Anzahl Bytes übergeben. I²C definiert keine maximale Länge der Nachrichten. Je nach Komplexität der Befehle reichen aber oft acht Daten-Bytes für das Senden und Empfangen aus. Als Rückgabewert dient die Anzahl der verschickten Bytes. Das Beispiel kann an der Kommandozeile mit folgenden Befehlen übersetzt und gestartet werden:

 $ make
 $ sudo ./iic_native

Im Buch finden sich ausführliche Erklärungen zur I²C-Bibliothek und den zahlreichen Beispielen.


Zurück