Magistrala I2C zwana też TWI (twin wire interface) to opracowana przez firmę Philips metoda na wymianę danych między różnymi urządzeniami elektronicznymi za pomocą dwóch przewodów. I2C zwdzięcza swoją popularność możliwości przyłączenia kilku urządzeń do jednej magistrali przy użyciu jedynie dwóch przewodów.
Magistrala pracuje podobnie jak 1-wire jako "otwarty dren" i w przypadku łączenia urządzeń które nie mają wewnątrz rezystorów podciągających, podciągnięcia obu szyn (zwanych SDA i SCL) do Vcc przy uzyciu oporników 4,7KOhm.
W jednej magistrali znajdować się może tylko jedno urządzenie Master zarządzające nią i wszystkie inne muszą być urządzeniami typs Slave. Urządzenia mają przypisane identyfikatory za pomocą których można prowadzić z nimi komunikację. Identyfikatory mogą mieć 7 lub 8 bitów. W Arduino przyyjmuje się krótsze identyfikatory przez co mogą one przyjmować wartości 0 - 127.
Zaimplementowana w Arduino biblioteka Wire.h pozwala na łatwą obsługę magistrali. Trzeba pamiętać, że biblioteka korzysta z przerwań i ma zaimplementowany bufor na 32 bajty. Częsta komunikacja w szkicach mocno używających przerwań może prowadzić do problemów, podobnie jak próba przesłania większej ilości danych - paczki powyżej 32 bajtów mogą się "zgubić".
| Płytka | SDA | SCL |
|---|---|---|
| Uno | A4 | A5 |
| Mega2560 | 20 | 21 |
| Leonardo | 2 | 3 |
| Due | 20 | 21 |
| NodeMCU | D2 | D1 |
Arduino Mega ma rezystory podciągające przy pinach 20 i 21 więc przy łączeniu tej płytki z innym Arduino po I2C nie ma potrzeby stosowana zewnętrznych oporników.
Poniższy szkic przydaje się by określić adresy urządzeń podpiętych do magistrali jeśli nie mamy informacji od producenta
#include <Wire.h>
byte result;
byte address;
byte foundDevices;
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Skaner kodow I2C");
Serial.println();
}
void loop() {
foundDevices = 0;
for(address = 1; address < 127; address++) {
Wire.beginTransmission(address);
result = Wire.endTransmission();
switch(result) {
case 0:
Serial.print("Znaleziono urzadzenie o adresie 0x");
if(address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
foundDevices++;
break;
case 4:
Serial.println("Nieznany blad przy adresie 0x");
if(address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
break;
}
}
if(foundDevices == 0) {
Serial.println("Do magistrali nie sa podpiete zadne dzialajace urzadzenia I2C");
Serial.println("Kolejne skanowanie rozpocznie się za 5 sekund");
Serial.println();
}
delay(5000);
}
W tym przykładzie Arduino będzie wysyłało po I2C do innego Arduino (o identyfikatorze I2C = 1) kolejne litery alfabetu
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Kontroler przekazuje alfabet na I2C");
Serial.println();
}
void loop() {
char c = 'a';
for(int i = 0; i < 26; ++i) {
Wire.beginTransmission(1);
Wire.write(c);
Wire.endTransmission();
Serial.println(c);
c++;
delay(1000);
}
delay(5000);
}
Program pozwala odbierać kolejne dane z magistrali I2C zaadresowane do urządzenia o identyfikatorze I2C = 1. Arduino na którym pracuje szkic ma podpięte pod odpowiednie przerwanie funkcję receiveI2C(), podpięcie odbywa się w sekcji setup za pomocą instrukcji Wire.onReceive(receiveI2C);.
#include <Wire.h>
void setup() {
Wire.begin(1);
Wire.onReceive(receiveI2C);
Serial.begin(9600);
Serial.println("Kontroler odczytuje znaki otrzymane przez I2C i przekazuje je na port szeregowy:");
Serial.println();
}
void loop() {
delay(100);
}
void receiveI2C() {
char c = Wire.read();
Serial.println(c);
}