Come collegare due Arduino usando il bus I2C.

Una caratteristica che reputo molto interessante è la possibilità di espandere Arduino tramite l’uso di shield. La maggior parte di queste schede aggiuntive sono però progettate per compiere una funzione specifica, non programmabile.
Esistono alcuni metodi che permettono di comunicare con altri dispositivi utilizzando come canale di trasmissione dati i protocolli seriali (I2C, SPI o RS232). Questo rende possibile demandare ad una scheda slave funzioni di elaborazione gravose, che non vogliamo far eseguire alla scheda master. Ho realizzato questo piccolo tutorial usando il bus I2C per creare uno scambio dati tra un Arduino ed un chip ATmega328 standalone.

Bus i2c

Il bus I2C, basandosi su due fili, non permette la comunicazione contemporanea tra Master e Slave. Lo scambio dati deve essere gestito dal Master tramite gli indirizzi (univoci) degli slave. Il flusso può essere sintetizzato in questo modo

  1. Il Master invia sul bus un bit di start
  2. Il Master invia sul bus l’indirizzo dello slave con cui vuole comunicare
  3. Il Master decide se scrivere o leggere dal dispositivo
  4. Lo Slave legge o scrive in base alla richiesta del Master

La libreria Wire dispone di tutte le funzioni necessarie alla realizzazione Master-Slave tra due schede Arduino. Lo schema del circuito che ho realizzato è visibile nella figura seguente.

Collegamento Master Slave tra due Arduino UNO

Come detto precedentemente lo schema si basa su Arduino Uno impiegato come Master e con un ATmega328 impiegato come slave.

Il codice permette di inviare un dato numerico allo slave, che provvederà ad incrementarlo per poi rispedirlo al Master.

[c]
//MASTER
#include <Wire.h>

byte x = 0;
byte num = 0;

void setup()
{
//inizializzo la libreria Wire come Master
Wire.begin();

//init seiale
Serial.begin(9600);
//avviso che il programma è avviato
Serial.println("START");

}

void loop()
{
//invio sul bus I2C un byte al device
//che ha come indirizzo il valore 0x04
//start trasmissione
Wire.beginTransmission(0x04);
//invio un byte
Wire.write(x);
//fine trasmissione
Wire.endTransmission();

delayMicroseconds(500);

//richiedo un byte allo slave che ha indirizzo 0x04
Wire.requestFrom(0x04, 1);

//attendo la disponibilità di dati sul bus i2c
while(Wire.available())
{
//quando è presente un dato avvia
//la lettura
num = Wire.read();
}

//incrementa il valore del byte
x++;

//verifico che il byte letto dallo slave sia stato
//incrementato
if(num != x)
Serial.println("ERRORE");

delay(5);

}

[/c]

Il codice seguente è relativo allo Slave

[c]
//SLAVE
#include <Wire.h>

byte x = 0;

void setup()
{
//inizializzo la libreria
//imposto l’indirizzo dello slave
Wire.begin(0x04);

//eventi per la ricezione del dato
//e per la richiesta del dato
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
}

void loop()
{
//esegui qualcosa
delay(1000);
}

void receiveEvent(int data)
{
//questo evento viene generato quando sul bus
//è presente un dato da leggere

//eseguo la lettura
x = Wire.read();

//elaboro il dato letto
x++;
}

void requestEvent()
{
//questo evento viene generato quando il master
//richiede ad uno specifico slave
//una richiesta di dati

//spedisco il dato al Master
Wire.write(x);

}

[/c]

I commenti presenti nel codice permettono di capire ciò che avviene tra i due dispositivi.

Di seguito uno screenshot dei dati scambiati sul bus

Scambio dati master slave su bus i2c

Di seguito invece la trasmissione del Master

Trasmissione i2c Master