Komunikacja I2C w Arduino
Magistrala I2C (inter-integrated circuit), zwana również TWI (two wire interface), I2C, IIC to urządzenie do przesyłania danych na jednej linii (half duplex)
Interfejs I2C (Two-Wire) jest przydatny do łączenia wielu urządzeń, ponieważ wszystkie mogą dzielić te same dwa piny (oraz wspólny pin uziemienia).
Urządzenia w sieci I2C są „adresowalne”.
Adresy są przekazywane za pomocą 7 bitów, stąd zakres od 0 do 127.
Każde urządzenie musi mieć unikalny adres z zakresu od 8 do 119.
Adres 0 jest zarezerwowany jako adres „rozgłoszeniowy”, adresy od 1 do 7 są zarezerwowane do innych celów, a adresy od 120 do 127 są zarezerwowane na przyszłość.
Domyślny rozmiar bufora transmisji w bibliotece Wire wynosi 32 bajty. Oznacza to, że całkowity rozmiar danych, które można przesłać w jednej transmisji I2C (między Wire.beginTransmission() a Wire.endTransmission()), nie może przekroczyć 32 bajtów.
Połączenie I2C
I2C jest połączeniem open drain, to znaczy, że linie są normalnie w stanie wysokim, a komunikacja rozpoczyna się po zmianie stanu na niski.
Analog port 4 (A4) = SDA (serial data)
Analog port 5 (A5) = SCL (serial clock)
Piny SDA i SCL wymagają pull-up rezystora, aby utrzymywać je w stanie wysokim, zazwyczaj jest to 4.7K.
Atmega328 jest skonfigurowany z wewnętrznym pull-up rezystorem więc dla niewielkich sieci, na krótkich odległościach dodatkowe rezystory nie są niezbędne.
Interfejs I2C pozwala na komunikację synchroniczną, ponieważ jeden z przewodów SCL dyktuje taktowanie zegara.
Skaner urządzeń I2C
Funkcja Wire.endTransmission() zwraca kod statusu, który może być jednym z poniższych:
0: Sukces (success)
1: Przepełnienie bufora transmitera (data too long to fit in transmit buffer)
2: Adres NACK (received NACK on transmit of address)
3: Dane NACK (received NACK on transmit of data)
4: Inny błąd (other error)
#include <Wire.h>
#define WIRE Wire
void setup() {
WIRE.begin();
Serial.begin(9600);
}
void loop() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for (address = 1; address < 127; address++) {
WIRE.beginTransmission(address);
error = WIRE.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16)
Serial.print("0");
Serial.print(address, HEX);
Serial.println(" !");
nDevices++;
} else if (error == 4) {
Serial.print("Unknown error at address 0x");
if (address < 16)
Serial.print("0");
Serial.println(address, HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(5000); // wait 5 seconds for next scan
}
Wizualizacja stanów logicznych linii CLK i DATA
Wizualizacja stanów logicznych linii CLK i DATA podczas wysyłania wartości 0x06 na adres 0x20 z częstotliwością 100kHz
Przesyłanie danych za pomocą interfejsu I2C
#include <Wire.h>
void setup() {
Wire.begin(); //Aktywacja magistrali
Wire.beginTransmission(0x30); //Inicjalizacja komunikacji z podanym adresem
Wire.write(data); // Zapisanie 1 bajta danych do bufora
Wire.endTransmission(); // Wysłanie danych i zakończenie komunikacji
}
void loop() {}
Dane odbiera się za pomocą eventu, który powinien być prosty i szybki w realizacji. W funkcji receiveEvent można pobierać ilość przesłanych bajtów.
Wire.onReceive(receiveEvent);
void receiveEvent(int howMany){
myData = Wire.read();
bool a = bitRead(myData, 0);
digitalWrite(13, a);
}
Przykład przesłania tablicy danych
#include <Wire.h>
void setup() {
Wire.begin();
}
void loop() {
Wire.beginTransmission(0x50);
byte data[] = {1, 2, 3, 4, 5};
Wire.write(data, sizeof(data));
Wire.endTransmission();
delay(1000);
}
Przesyłanie danych I2C pomiędzy 2 płytkami Arduino
//Master Arduino
#include <Wire.h>
const int SLAVE_ADDR = 0x20;
// 0x20 - 0b00100000
int counter = 0;
void setup() {
delay(500);
}
void SendData(byte data){
Wire.begin();
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(data);
Wire.endTransmission();
delay(500);
}
void loop() {
SendData(counter);
counter++;
}
//Slave Arduino
#include <Wire.h>
const byte MY_ADDRESS = 0x20;
const int LED = 2;
void setup() {
Wire.begin(MY_ADDRESS);
pinMode(LED, OUTPUT);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}
void loop() {}
void receiveEvent() {
while(Wire.available()){
byte incData = Wire.read();
Serial.println(incData);
digitalWrite(LED, incData%=2);
}
}
Zobacz: Komunikacja I2C w Arduino
Zobacz: Komunikacja SPI w Arduino