Il bus I2C (o IIC Inter Integrate Communication) è un bus di comunicazione seriale sincrono ideato dalla Philips che utilizza due sole linee di comunicazione, una linea di clock e una linea dati bidirezionale. In questo articolo vedremo come configurare e utilizzare la libreria maxqi2c per interfacciare periferiche con porta di comunicazione I2C ai microcontrollori della famiglia MAXQ. La libreria è formata da 2 file maxqi2c.h e maxqi2c.c, il software si può scaricare dal sito MAXIM Integrated ed è scritto interamente in linguaggio C. Con questa libreria è possibile realizzare un driver I2C con funzione master per bus funzionanti in standard mode fino a 100KHz e in fast mode fino a 400KHz.
IL BUS I2C
Il bus I2C è un bus di comunicazione seriale con architettura master slave, il dispositivo master controlla la comunicazione sul bus e genera il segnale di clock. Le periferiche I2C si collegano al bus con un driver open collector, per il corretto funzionamento le linee del bus devono essere collegate a due resistenze di pullup che realizzano l’and logico di tutte le periferiche. Le specifiche prevedono tre velocità massime predefinite per la trasmissione sul bus I2C: standard mode (100Kbps) fast mode (400Kbps) e high speed mode (3.4Mbps). Ogni dispositivo con interfaccia I2C definisce, tra le sue caratteristiche, la velocità massima a cui può comunicare. Il dispositivo più lento collegato al bus determina la massima velocità di comunicazione. Il numero massimo di dispositivi collegabili al bus I2C è limitato dalla capacità di carico che non deve superare i 400pF per garantire i fronti corretti nella commutazione del segnale. Anche le resistenze di pullup del bus devono essere dimensionate in base alla velocità di comunicazione e ai dispositivi collegati come indicato in [2]. Philips ha a catalogo circa 400 dispositivi diversi con interfaccia I2C e molti altri costruttori hanno realizzato su licenza diverse periferiche. Per citare le più comuni: I/O expander, RTC, EEprom, LCD controller. Poiché il bus dispone di una sola linea dati la comunicazione è necessariamente half duplex. L’indirizzamento dei dispositivi avviene via software, ogni byte è trasferito dal bit più significativo (MSB). Chi trasmette invia il bit sulla linea dati quando il clock è basso, mentre con il clock alto la linea dati deve rimanere stabile. Per il sincronismo della comunicazione ci sono due condizioni di controllo definite START e STOP:
- Nella condizione di START il dispositivo master commuta bassa la linea dati quando il clock è alto.
- Nella condizione di STOP il dispositivo master commuta alta la linea dati quando il clock è alto.
Entrambe le condizioni non si possono manifestare mai durante la normale comunicazione perché la linea dati dev’essere stabile e quando il clock è alto. Il master segnala a tutti gli salve collegati l’inizio di una comunicazione generando la condizione di START. In ogni istante può interrompere la comunicazione in corso generando la condizione di STOP che riporta il bus nella condizione di riposo. Lo START è sempre seguito da un byte inviato dal master che rappresenta l’indirizzo del dispositivo slave a cui è destinata la comunicazione. Il bit meno significativo dell’indirizzo determina il verso della comunicazione se 1 si tratta di una lettura (il master trasferisce dati dallo slave) se 0 si tratta di una scrittura (il master invia dati allo slave). Il dispositivo slave indirizzato risponde a questo byte inviando il bit di ACK che consiste nel pilotare bassa la linea dati per un ciclo di clock. Dopo aver ricevuto l’ACK il master continua a generare il clock mentre i dati sono trasmessi dal master in caso di scrittura o dallo slave in caso di lettura. Dopo la trasmissione di ogni byte il dispositivo che riceve il dato deve inviare il bit di ACK come risposta. Se non viene inviato l’ACK chi trasmette interpreta la condizione come NAK e termina la comunicazione in errore. Al termine il master genera la condizione di STOP per concludere la comunicazione. La comunicazione master/slave non dev’essere necessariamente continua ma può interrompersi anche per un tempo indefinito (il master blocca il segnale di clock), il dispositivo indirizzato rimane selezionato finché non rileva la condizione di STOP. Anche lo slave può bloccare la linea di clock forzandola a livello basso per un certo tempo. In questo modo segnala al master la necessità di ritardare il trasferimento dei dati. Quando lo slave rilascia la linea di clock il master riprende la comunicazione dal punto in cui è stata interrotta.
LA FAMIGLIA MAXQ
I microcontrollori MAXQ di Maxim sono dei microcontrollori RISC (solo 33 istruzioni) a 16bit con architettura Harvard. Alcuni modelli sono: il MAXQ2000 che include un moltiplicatore hardware, un LCD driver in grado di pilotare fino a 132 segmenti e numerose periferiche di comunicazione seriale/SPI. Poi si sono aggiunti il MAXQ3120 con convertitore sigma-delta 16bit e il MAXQ3210. La frequenza di clock può arrivare a 20MHz con consumi relativamente ridotti. Sono in grado di eseguire un’istruzione per ciclo raggiungendo così i 20Mips.
LA LIBRERIA I2C PER IL MICROCONTROLLORE MAXQ2000
Le funzioni che compongono la libreria sono quattro [1] [3]:
- i2cInit: la funzione di inizializzazione del driver software del bus I2C
- i2cIsAddrPresent: verifica se l'indirizzo slave, passato come parametro, è collegato al bus
- i2cSend: invia dati allo slave indirizzato
- i2cRecv: riceve dati dallo slave indirizzato
la libreria utilizza quattro variabili globali da configurare prima di ogni chiamata alle funzioni di libreria
- i2cData: puntatore al primo dato del buffer dati TX/ o RX
- i2cDataLen: Lunghezza del buffer dati da trasmettere o ricevere (byte indirizzo escluso)
- i2cDataAddr: Indirizzo del dispositivo slave
- i2cDataTerm: Comportamento del driver al termine della comunicazione
I ritardi tra le diverse fasi della comunicazione sul bus I2C sono stati realizzati all’interno della libreria con dei loop di ritardo software. I loop di ritardo sono stati calcolati supponendo di utilizzare un quarzo a 20MHz per il microcontrollore e dovranno essere modificati se si utilizza un quarzo diverso. Prima di utilizzare la libreria si devono definire i parametri di configurazione hardware che si trovano all’inzio del file maxqi2c.h. Le costanti SDA_PORT, SDA_PORT_BIT, SCLK_PORT e SCLK_PORT_BIT definiscono quali pin del microcontrollore sono dedicati al bus I2C, nella configurazione standard rispettivamente P0.0 e P0.1. La definizione I2C_400_KHZ o I2C_100_KHZ permette di scegliere la massima velocità di comunicazione in funzione dei dispositivi collegati e del bus realizzato. L’ultima costante da definire è I2C_CLOCK_STRETCHING da utilizzare nel caso di periferiche slave particolarmente lente. Abilitando questa costante la procedura di lettura dati da una periferica i2cRecv controlla lo stato della linea di clock prima di iniziare la lettura dei dati. Si deve fare attenzione perché il controllo è bloccante e la procedura rimane in loop infinito finché la periferica non rilascia la linea di clock.
LE FUNZIONI DI LIBRERIA
Come si può vedere esaminando il file maxqi2c.c le funzioni di basso livello implementate nella libreria realizzano tutte le azioni principali della comunicazione I2C. La tabella 1 elenca tutte le funzioni utilizzate nella libreria.
Funzione i2cInit()
Non ha nessun parametro in ingresso. Si limita a configurare le linee hardware del microcontrollore dedicate al bus I2C. Si veda il riquadro porte pushpull e bus I2C.
Funzione i2CIsAddrPresent()
Invia sul bus l’indirizzo del dispositivo slave e attende il bit ACK. Restituisce poi lo stato del bit ACK ricevuto.
In caso di ricezione corretta restituisce 1 o meglio I2C_XMIT_OK nel caso in cui non riceva l’ACK restituisce 0 o I2C_XMIT_FAILED.
Funzione i2cSend()
Configurazione delle variabili prima della chiamata alla procedura:
- i2cData: Puntatore al primo byte del buffer dati da trasmettere
- i2cDataLen: Lunghezza del buffer dati da trasmettere (byte indirizzo escluso)
- i2cDataAddr: Indirizzo del dispositivo slave a cui inviare i dati
- i2cDataTerm: Comportamento del driver al termine della comunicazione.
Se l’indirizzo i2cDataAddr è diverso da zero, la funzione invia l’indirizzo del dispositivo slave seguito dal buffer dati selezionato. Ad ogni byte inviato controlla la ricezione dell’ACK (funzione_i2c_low_level_recv_ack) se riceve la risposta corretta invia il byte successivo altrimenti termina anticipatamente la comunicazione inviando sul bus la condizione di STOP e segnalando al software l’errore restituendo il valore I2C_XMIT_FAILED. Nel caso di comunicazione corretta invia sul bus la condizione di STOP e restituisce il valore I2C_XMIT_OK. Il comportamento della procedura può essere modificato utilizzando le variabili i2cDataTerm e i2cDataAddr. Se si assegna il valore I2C_TERM_NONE alla variabile i2cDataTerm la procedura non termina inviando la condizione di STOP sul bus ma lascia sospesa la comunicazione con lo slave che rimane selezionato. Successive chiamate alla i2CSend con variabile i2cDataAddr uguale a zero permettono di inviare altri dati allo salve selezionato senza riaprire la comunicazione con la procedura di START seguito da indirizzo e bit di direzione. La tabella2 riassume le configurazioni ammesse per le due variabili.
Funzione i2CRecv()
Configurazione delle variabili prima della chiamata alla procedura.
- i2cData: Puntatore al primo byte del buffer dati da ricevere
- i2cDataLen: Lunghezza del buffer dati da ricevere (byte indirizzo escluso)
- i2cDataAddr: Indirizzo del dispositivo slave da cui leggere i dati
- i2cDataTerm: Comportamento del driver al termine della comunicazione.
Se l’indirizzo i2cDataAddr è diverso da zero, la funzione invia l’indirizzo del dispositivo slave attende l’ACK da parte dello slave e inizia a leggere dati. Ad ogni byte ricevuto invia il bit di ACK (funzione _i2c_low_level_send_ack) e ripete il ciclo di lettura del byte successivo. Se la procedura non riceve l’ACK dallo slave dopo aver inviato l’indirizzo allora invia sul bus la condizione di STOP e termina segnalando al software l’errore restituendo il valore I2C_XMIT_FAILED. Se la comunicazione termina correttamente il master invia sul bus la condizione di STOP e restituisce il valore I2C_XMIT_OK. Il comportamento della procedura può essere modificato utilizzando le variabili i2cDataTerm e i2cDataAddr. se si assegna il valore I2C_TERM_ACK alla variabile i2cDataTerm la procedura non termina inviando la condizione di STOP sul bus ma restituisce l’ACK all’ultimo dato ricevuto e lascia sospesa la comunicazione con lo slave che resta selezionato. Successive chiamate alla i2CSend con variabile i2cDataAddr uguale a zero permettono di leggere altri dati allo salve indirizzato senza riaprire la comunicazione con la procedura di START seguito da indirizzo e bit di direzione. Se invece alla variabile i2CDataTerm viene assegnato il valore I2C_TERM_NACK_AND_STOP la procedura termina chiudendo la comunicazione con lo slave indirizzato. La tabella3 riassume le configurazioni ammesse per le due variabili.
Bibliografia
[1] Maxim appnote 3588 (www.maxim-ic.com/appnotes.cfm/appnote_number/3588)
[2] The I2C bus specifications v 2.1-january 2000 (link)
[3] Esempio di progetto: http://www.maxim-ic.com/tools/other/appnotes/4267/4267_software.zip
PORTE PUSHPULL E I2CNon si può utilizzare un driver pushpull per le porte collegate al bus I2C. Utilizzando un driver pushpull, infatti, a riposo tutti i dispositivi devono lasciare le linee del bus nello stato high e quindi il transistor alto del driver pushpull dev’essere in saturazione. Se un dispositivo pilota il bus I2C a livello basso si crea un cortocircuito tra la porta pushpull e il driver I2C che porta bassa la linea del bus. Se il dispositivo da collegare, come il MAXQ2000, non ha I/O configurabili come open collector si deve adottare una tecnica di controllo particolare per evitare di danneggiare il driver d’uscita. Si simula il comportamento di una porta open collector utilizzando solamente il transistor basso del driver pushpull. Il bit corrispondente nel registro di output della porta dev’essere sempre impostato a zero. Lo stato della porta viene modificato utilizzando il registro di direzione in/out anziché il registro di output che deve sempre rimanere a zero. Per trasmettere un bit a 0 si configura la porta in output e la linea del bus si porta bassa, per trasmettere un bit a 1 si configura la porta in input. Quando la porta è in input il pullup del bus può mantenere alta la linea dati. Gli altri dispositivi connessi al bus interpretano questa condizione come trasmissione di un bit a 1. La procedura i2cInit configura a zero i bit del registro di output dei pin selezionati per la comunicazione I2C e configura le line in input per lasciare il bus nello stato di riposo. Si osserva che le macro SDA e SCL definite nel file maxqi2c.h sono riferite al registro direzione della porta anziché al registro di output |
MAXQ2000 è particolarmente adatto per il mercato medical con il monitoraggio del glucosio nel sangue, ma può essere utilizzato in qualsiasi applicazione che richiede prestazioni a bassa potenza.