I2C Bus
Der I2C Bus besteht aus 2 Datenleitungen. Einer Taktleitung (CLK) und einer Datenleitung zur Übertragung der seriellen Daten (SDA). Beide Leitungen haben einen Open Drain Ausgang. Aktiv können die Busteilnehmer den Ausgang daher nur mit GND verbinden. Über integrierte PullUp-Widerstände werden die Leitungen im Leerlauf (Idle) mit VCC verbunden. Neben den beiden Datenleitungen ist übrigens eine gemeinsame Masse der Busteilnehmer unbedingt erforderlich.
Meist hat der Bus nur einen Master und viele über Adressen mit 7 Bit (128) ansprechbare Slaves (Adressen 0-7, 120 und 127 sind reserviert). Nur der Master kann eine Kommunikation beginnen. Entweder er sendet Informationen an den Slave oder er fordert vom Slave Daten an.
Information an den Slave senden
Start
Beim Start wird vom Master die SDA-Leitung auf GND gezogen, noch währen die CLK Leitung in Idle auf HIGH liegt. Damit "hören" alle Slaves zunächst einmal zu.
Adresse
Als nächstes wird mit 7 Bit die Adresse des Slaves mit dem kommuniziert werden soll, übertragen . Außer beim Start- und Stopp-Bit werden alle Signale zum Zeitpunkt der positiven CLK Taktflanke abgefragt.
R/W
Direkt im Anschluss an die Adresse, quasi als 8’tes Bit wird vom Master mitgeteilt, ob er Daten zum Slave senden oder vom Slave erhalten möchte. Hier bedeuten 0=write und 1=read.
Acknowledge Bit
Hat der Master jeweils ein Byte gesendet entlässt er die SDA Leitung wieder in den Idle-Modus und wartet. Sobald
der Slave ein vollständiges Byte empfangen hat, zieht er den Bus für einen Takt auf LOW und signalisiert so, dass
das Byte angekommen ist.
Benötigt der der Slave zur Verarbeitung des Bytes Zeit, signalisiert er das dem Master, indem er das CLK Signal
auf GND legt und so die benötigte Zeit aussetzt (Clock Stretching).
Stopp
Am Ende der Übertragung beendet der Master die Unterhaltung in dem er CLK in den Idle-Modus entlässt und daraufhin auch die SDA Leitung.
Information vom Slave erhalten
Der Aufbau des Protokolls ist ähnlich und wird auch wieder vom Master eingeleitet. Nach dem Start-Bit und der
7-Bit-Adresse des Slave wird das R/W Bit als 1 (=read) gesendet. Danach beginnt der Slave ein oder mehrere Byte zu
senden. Zwischen den Bytes zieht der Master den SDL kurz auf GND und sendet so das Acknowledge Bit.
Hat der Master die erwartete Anzahl an Bytes erhalten, zieht er die SDA Leitung nicht zur Bestätigung auf GND,
sonder lässt sie HIGH (= not Acknowledge NAK) und schalte die Übertragung mit dem folgenden Stopp-Bit dann ab.
Zwischen dem vom Master eingeleiteten Start- und Stop-Bit ist die Kommunikation für andere Master gesperrt. Erst nach dem Stop-Bit kann ein andere Master eine Kommunikation beginnen. Oft ist es so, dass zunächst der Master in ein Register des Slaves schreibt welche Informationen er abrufen möchte und in einer zweiten Kommunikation diese Daten dann anfordert. Damit zwischen dem ersten Schreiben und dem zweiten Anfordern kein andere Master eine Kommunikation beginnt wird zwischen dem Schreiben und Anfordern kein Stop-Bit sondern nur ein erneutes Startbit gesendet.
Daten übertragen mit der Wire.h Bibliothek
Programmcode für den Master
// SDA=A4, SCLK=A5
#include "Wire.h"
void setup() {
Wire.begin(); // der Master hat keine I2C Adresse
Serial.begin(9600);
}
void loop() {
// Senden von Daten über I2C
Wire.beginTransmission(5); // an Adresse 5 schreiben
Wire.write(B00000101); // sendet ein byte
Wire.write('c'); // ein Buchstabe
Wire.write("Hallo vom Master!"); // einen String in mehreren Bytes hintereinander
Wire.endTransmission(); // beendet die Übertragung mit dem Stop-Bit
// Anfordern von Daten über I2C
Wire.requestFrom(5, 10); // 10 Bytes werden von der Slave Adresse 5 angefordert
while (Wire.available()) {
char c = Wire.read();
Serial.print(c);
}
Serial.println();
delay(500);
}
Programmcode für den Slave
#include "Wire.h"
void setup() {
Wire.begin(5); // beim Slave muss die Adresse angegeben werden
Wire.onRequest(datenSenden);
Wire.onReceive(datenVerarbeiten);
Serial.begin(9600);
}
void loop() {
// kein Code notwendig
}
void datenSenden(){
Wire.write("Servus vom Slave!");
}
void datenVerarbeiten() {
while (Wire.available()) {
char c = Wire.read();
Serial.print(c);
}
Serial.println();
}