Protocollo I2C: semplicità e potenza

Il protocollo I2C (I-quadro-C o I-due-C) è stato realizzato dalla Philips nel lontano 1982, e rilasciato come protocollo dopo circa dieci anni di utilizzo come bus proprietario. Per le sue caratteristiche di economicità, semplicità di realizzazione e flessibilità d’uso, nonostante gli anni, continua ad essere utilizzato con profitto da una moltitudine di progettisti ed appassionati di microelettronica.

La struttura più semplice di un sistema I2C è composta da un master ed uno slave, ovvero rispettivamente: un dispositivo “padrone” (che detta ad esempio la sincronia) ed un dispositivo “schiavo” (che segue il master inviando o ricevendo dati). In realtà, questi tipi di sistemi sono spessissimo utilizzati in multi-slave ed in multi-master (con alcune accortezze). La trasmissione dell’informazione sul bus avviene attraverso una comunicazione seriale su una linea bipolare. I due segnali sono chiamati rispettivamente: SDA il quale si occupa di trasmettere e ricevere i dati, SCL che si occupa di inviare il segnale di clock ai vari slave per sincronizzare la comunicazione. La presenza di SCL fa in modo che questo tipo di bus prenda il nome di sincrono. Come si può ben comprendere, la sincronia della comunicazione è data dal clock inviato dal master, in caso di multi master sarà quindi necessario gestire la rete di dispositivi I2C facendo “parlare” un master alla volta, evitando così di compromettere la comunicazione.

L’ovvio vantaggio di questo protocollo seriale è l’utilizzo di soli due fili per la comunicazione, il che alleggerisce notevolmente lo sforzo di cablaggio, oltre ad aumentare la distanza di connessione tra un dispositivo ed un altro, a discapito, tuttavia, della massima velocità di trasmissione compresa tra i 10 kbit/s fino ai 3.4 Mbit/s, (nelle ultime revisioni). Infatti, essendo l’I2C un sistema di comunicazione seriale, la sua velocità sarà notevolmente più bassa rispetto ad un'architettura di tipo parallelo. Caratteristica molto interessante di questo protocollo è che il limite inferiore sulla velocità non è definito, quindi, impostando una comunicazione a 1 Hz attraverso un LED, sarà possibile fare un debug visivo del protocollo. In Figura 1 è rappresentata una classica rete I2C single-master multi-slave. Come si può osservare dallo schema, sono presenti due resistenze di pull-up connesse tra la rete bifilare e l’alimentazione.

Figura 1: Esempio di struttura BUS I2C

Figura 1: Esempio di struttura bus I2C

Lo scopo di queste resistenze è di evitare un valore fluttuante dei due segnali, impedendo ai vari dispositivi di male interpretare gli eventuali disturbi sul bus. I valori di tali resistenze, oltre a garantire ai dispositivi connessi un segnale debolmente alto alle due linee, devono compensare gli effetti dovuti alle capacità parassite associate agli slave. Infatti, la capacità massima complessiva consentita dalla rete per il corretto funzionamento, è di 400 pF, ed essendo tali capacità equivalenti tutte in parallelo, verranno sommate in un’unica macro capacità il cui effetto dovrà essere compensato dalla resistenza di pull-up. Per conoscere il valore della capacità parassita dello specifico dispositivo, si potrà fare riferimento al foglio delle specifiche tecniche. In Figura 4 è raffigurata la curva per il dimensionamento della resistenza in funzione della capacità parassita totale vista dal master.

IL PROTOCOLLO

Come anticipato, il funzionamento di questo protocollo si basa sui due tipi di elementi che lo compongono:

  • master, che scandisce attraverso il clock quando la comunicazione deve avvenire;
  • slave, che comunica seguendo il "tempo" del master attraverso il clock, ricevendo o inviando dati dal/per il master.

In Figura 2 è riportata la sequenza di bit che descrive come il master e lo slave iniziano a comunicare.

Figura 2: Analisi del protocollo durante la “Start condition” e la”Stop condition”

Figura 2: Analisi del protocollo durante la "Start condition" e la "Stop condition"

Una regola fondamentale è che SDA inviato dal master può cambiare stato solo quando il valore logico di SLC è basso, le transizioni fuori da tale regola sono da considerasi transizioni speciali, come sarà descritto di seguito. I vari passi che costituiscono la comunicazione sono i seguenti:

  • inizio comunicazione: mentre l'SCL è allo stato logico alto, l’SDA passa dallo stato logico alto a quello basso, questa è definita “Start Condition” o “S”, da ora in poi il master invierà sul bus il nome del nodo con il quale vuole comunicare e a seguire l’informazione;
  • indicazione nodo: dopo il bit “S” si passa alla trasmissione del nome dell’interlocutore presente nella rete, infatti, i successivi 7 bit (BIT1, BIT2,..BIT7) indicheranno l’ID dello slave a cui è diretta l’informazione. Tipo di interazione con il nodo: segue il BIT8 che indica se il master vuole leggere o scrivere una determinata informazione (a condizione che lo slave sia predisposto per fare l’una, l’altra o entrambe).

- BIT8 = LOW  Scrittura su slave
- BIT8 = HIGH  Lettura da slave

Risposta dello slave: se l’indirizzo dello slave esiste, questo prende il controllo della linea dati portando a LOW il livello logico dell’SDA al successivo livello logico alto di SCL, questa operazione è detta ACK. A questo punto, il master ha capito che lo slave esiste ed ha risposto alla chiamata:

  • invio dati: se il master vuole inviare una serie di informazioni allo slave, tra un byte e l’altro eseguirà un ACK, ad eccezione dell’ultimo byte dove eseguirà l’operazione di fine comunicazione, descritta di seguito;
  • fine comunicazione: come il bit Start Condition, la medesima operazione (però invertita) delimita la comunicazione con “Stop Condition” o “P” dove l'SDA passa da livello logico basso ad alto quanto il bit SCL è alto. In Figura 3 è riportato un esempio di comunicazione.
Figura 3: Esempio di protocollo durante la fase di comunicazione

Figura 3: Esempio di protocollo durante la fase di comunicazione

 

Figura 4: Curva caratteristica per la compensazione delle capacità parassite su BUS

Figura 4: Curva caratteristica per la compensazione delle capacità parassite su bus

INDIRIZZAMENTO DEI DISPOSITIVI SLAVE

Come già anticipato, i vari dispositivi slave sono identificati univocamente da un indirizzo fisico denominato “Device Address”, il quale è strutturato come rappresentato in Figura 5.

Figura 5: Esempio di struttura del device driver

Figura 5: Esempio di struttura del device address

Dei 7 bit utilizzati per l’identificazione, i primi 4 rappresentano il tipo di periferica, che, ad esempio, potrebbe essere un Real Time Clock (RTC) oppure un Driver per un display LCD; i successivi tre bit B5, B6 e B7 permettono di identificare univocamente più dispositivi dello stesso tipo nella stessa rete, ad esempio più di un LCD, questo è possibile solo se il dispositivo slave permette la programmazione di questi ultimi bit. La Figura 5 mostra tre bit per questo tipo di programmazione, quindi 8 dispositivi dello stesso tipo potranno “convivere” nello stesso BUS. Ma nel caso in cui il numero di tali bit diminuisse a 2 oppure a 1, il numero di dispositivi dello stesso tipo sul BUS scenderebbe rispettivamente a 4 ed a 2. Naturalmente, il discorso appena fatto interessa quelle periferiche acquistate come moduli pronti all’uso (come LCD o RTC, EEPROM); se, invece, gli slave sono altri Micro, sarà possibile programmarne l’indirizzo a nostro piacimento, potendo così raggiungere il massimo numero di dispositivi concessi dello stesso tipo (27BIT=128).

I2C SU MICROCONTROLLORE

Molti microcontrollori oggi sul mercato hanno a bordo un’interfaccia I2C, dai PIC ai vari ATMEGA della famiglia Atmel. Grazie a questi ultimi e alla grande diffusione della piattaforma Arduino, il grande pubblico ha avuto modo di “riscoprire”, a molti anni dalla sua invenzione, questo elegantissimo protocollo, che a discapito di una discreta complessità di funzionamento, permette una eccezionale semplicità di integrazione di periferiche per la realizzazione di complessi sistemi modulari. La complessità cui si faceva riferimento prima, però, non sempre è percepita dal programmatore, grazie soprattutto a quelle che sono le librerie integrate nei vari sistemi di sviluppo, con le quali è sufficiente importare la libreria ed usare i metodi integrati per la scrittura/lettura sulle periferiche slave. Di seguito, saranno riportati degli esempi di sviluppo su piattaforma Arduino, dove l’architettura del software è molto simile a quella che possiamo trovare in qualsiasi sistema di sviluppo. La prima operazione da fare nel codice sarà aggiungere la libreria InWire.h, che si occuperà di gestire il protocollo I2C attraverso i due pin del micro (A4 e A5). Di seguito, si riporterà l’esempio di due microcontrollori, che mediante un semplicissimo codice permette la scrittura e la lettura di dati da parte del master sullo slave. Il seguente schema a blocchi in Figura 6 mostra quali interazioni saranno presenti nei codici dei due micro.

Figura 6: Schema a Blocchi software su micro master e su micro slave

Figura 6: Schema a blocchi software su micro master e su micro slave

I due software sono caratterizzati entrambi dalla presenza della libreria I2C; la differenza tra master e slave sta nell’inizializzazione del BUS; infatti, nello slave questa operazione verrà effettuata indicando il device driver come argomento Wire.begin(0x04), mentre nel master non sarà indicato alcun ID. Per semplicità, si è scelto di inviare i comandi tramite seriale, in funzione di quello che si invierà sul BUS seriale, sull’ I2C sarà inoltrata l’informazione per l’accensione o lo spegnimento di un LED connesso allo slave. A bordo del master l’operazione di invio dati seguirà queste fasi:

  • apertura canale di comunicazione: la connessione con lo slave di interesse sarà effettuata con il comando Wire.beginTransmission(0x04);
  • invio dati: operazione effettuata dall’istruzione Wire.write(byte);
  • chiusura canale di comunicazione: alla fine della routine di invio, la trasmissione viene chiusa dal comando Wire.endTransmission.

L’operazione di ricezione dati è determinata dalla pressione di un pulsante sullo slave, il quale invierà al master un byte per identificare lo stato dell’ingresso. I seguenti step descrivono quello che avviene per la ricezione nel software del master:

  • richiesta di invio dati: il master fa esplicita richiesta ad uno slave di inviare un certo numero di byte, indicando con Wire.requestFrom(0x04,1) il device address e quanti byte si vogliono ricevere;
  • verifica risposta slave: quando lo slave risponderà, il metodo Wire.available() ritornerà TRUE (altrimenti FALSE), attraverso un controllo con un ciclo while si gestisce l’eventuale assenza di comunicazione e la lettura di segnali spuria;
  • lettura dati: Wire.read() ritorna il byte letto, infatti, questo potrà essere direttamente spedito su seriale per verificare la corretta lettura dallo slave.

Sullo slave troviamo, invece, i seguenti contenuti software per la ricezione dei dati:

  • setup: dove avviene la configurazione dei PIN di ingresso ed uscita;
  • ricezione dati: la ricezione avviene attraverso la funzione: Wire.onReceive (RicezioneDati) , tale metodo verifica che il master abbia inviato dati sul BUS. Attraverso la funzione creata RicezioneDati (int data) i dati saranno fisicamente letti dal BUS con il comando Wire. read();
  • elaborazione: il dato ricevuto sarà quindi elaborato per far accendere o meno il LED. Riguardo l’invio di dati, lo slave gestirà nel seguente modo la comunicazione;
  • apertura canale: con il comando Wire.onRequest (InvioDati) si apre il canale di invio se e solo se il master ne ha fatto esplicita richiesta.

Al momento della richiesta da parte del master di ricezione dati, lo slave esegue la funzione argomento InvioDati(), attraverso il comando Wire.write(byte) vengono instradati sul bus i byte richiesti. All’altro capo del BUS, il master sta attendendo tali byte per processarli con le sue logiche. Le connessioni elettriche sono riportate in Figura 7, e come si può osservare non sono state inserite le due resistenze di pull-up dati sul bus, questo perché nel banco di prova realizzato i due microcontrollori erano alimentati dalla stessa sorgente di alimentazione. È stato, invece, necessario mettere una resistenza di pull-down (10k) sul pin del pulsante per evitare fastidiose fluttuazioni di tensione. In Figura 7 è esplicitamente descritto come il master, tramite la connessione USB, è in comunicazione con il serial monitor per l’invio e la ricezione dei dati con il PC.

Figura 7: Connessioni elettriche tra master e slave

Figura 7: Connessioni elettriche tra master e slave

CONCLUSIONI

Possiamo dire che questo protocollo, con una notevole storia alle spalle, continua ad attrarre, affascinare e risolvere una notevole quantità di problemi per programmatori e progettisti i quali, attraverso la semplicità di integrazione e la possibilità di espansione modulare, riescono a trovare in esso il giusto compromesso tra ottimizzazione e complessità.

Scarica subito una copia gratis

2 Commenti

  1. bob563 9 Agosto 2020
    • Mariangela.Mone Mariangela.Mone 10 Agosto 2020

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend