La libreria Wire

News

Scritto da Virgili Marco

La libreria Wire

Ancora un metodo per il colloquio seriali con dispositivi esterni. Questa volta i fii sono 2 e grazie alla libreria Wire Arduino dialoga in I2C con dispositivi anche complessi come ad esempio l'accelerometro a tre assi, cuore del nostro ultimo progetto che non mancherà di attirare l'attenzione se qualcuno vorrà prenderlo in mano e lo ruoterà nello spazio. 

La libreria wire nasce per la gestione di tutti quei dispositivi elettornici che negli anni sono stati sviluppati con la tecnologia I2C per comunicare su due soli fili con altri dispositivi digitali. Si tratta di una tecnologia ormai ben collaudata e nata dai laboratori Philips nel (tecnologicamente) lontano 1980. Lo scopo era quello di ridurre la complessità del cablaggio per il collegamento fra dispositivi digitali, e nello specifico, fra dispositivi master come un microcontrollore, e dispositivi slave come sensori di pressione, memorie EEPROM, sensori di temperatura, orologi in tempo reale,potenziometri digitali e molto altro. Il collegamento di un bus indirizzi e uno dati richiedeva non poche lineedi comunicazione, eventualmente condivise fra dati e indirizzi, ma comunque basate su almeno otto "fili". Questi richiedevano altrettanti piedini di collegamento sui circuiti integrati e quindi il tutto comportava costi e complessità non da poco. Se poi il sensore o il dispositivo non era direttamente sul medesimo circuito stampato del microcontrollore, allora il cablaggio rappresentava un ulteriore costo. Per risolvere tutto questo, Philips ha studiato e promosso un metodo di comunicazione seriale capace di gestire con il minor numero possibile di fili un nemro di dispositivi significativo. In pratica, a differenza dell'interfaccia seriale che collega due dispositivi direttamente, il collegamento I2C è paragonabile a quello con un bus vero e proprio. I2C gestisce infatti sia l'indirizzamento, sia il trasferimento dati bidirezionale, pur utilizzando solo una linea  per i Dati (SDA) e una per il Clock (SCL). Aggiungiamo una alimentazione e una massa e con quattro piedini è potenzialmente possibile avere tutto quello che serve. DI I2C è molto interessante la varietà dei dispositivi e la loro semplicità di interfacciamento  ed utilizzo: il protocollo prevede infatti una serie di meccanismi molto ben collaudati grazie ai quali i dispositivi  gesticono l'intera comunicazione direttamente con la loro logica presente a bordo, mentre il microcontrollore ha a disposizione un meccanismo per il dialogo semplice e lineare. Attraverso l'assegnazione di un indirizzo a ciascun dispositivo, sul bus I2C possono convivere numerosi dispositivi.

IL PROTOCOLLO I2C
Per poter utilizzare in modo opportuno i dispositivi I2C è necessario comprendere il funzionamento del protocollo. Innanzitutto, si tratta di una soluzione che prevede sul bus di collegamento dei ruoli precisi, ovvero quelli di un Master che controlla e di "n" Slave che eseguono i comandi impartiti dal Master; grazie a questo, la gestione della comunicazione sui due fili avviene in modo ordinato e le collisioni sono ridotte al minimo.Sul bus ci può essere in ogni momento un solo Master, ma una volta terminata la comunicazione, il dispositivo Master può lasciare il ruolo e consentire che altri lo prendano. In questo modo, il sistema può essere gestito da più microcontrollori che interrogano e interagiscono con i medesimi dispositivi I2C assumendone la gestione di volta in volta. Il modo per selezionare un dispositivo Slave è quello di inviare il suo indirizzo sul bus di comunicazione con le opportune modalità che vedremo più avanti. L'indirizzo può essere di sette o dieci bit e in un sistema non ci possono essere slave che condividono il medesimo indirizzo.La maggior parte dei dispositivi ha un indirizzo base, assegnato in fase di produzione, più alcuni piedini che permettono di gestire due, tre quattro bit bassi dell'indirizzo, cosi da poter avere sul medesimo bus più dispositivi uguali, ma con indirizzo diversificato se necessario. Alcuni dispositivi molto particolari, come gli orologi in tempo reale, hanno indirizzo predefinito, così che nel bus non ce ne possa essere più di uno (anche perchè non avrebbe senso averne). Tornando al protocollo, abbiamo detto che solo un master può essere attivo e dialogare con gli slave collegati al bus selezionandoli tramite l'indirizzo. E' previsto che si conosca già l'indirizzo di ciascun slave con cui dialogare e non ci sono meccanismi di scoperta, se non il provare  a "chiamare " in sequenza i vari indirizzi e vedere chi risponde. Questo sistema, però , può creare problemi se ci sono dispositivi con indirizzi a 7 e 10 bit in quanto quelli a 7 bit potrebbero rispondere sulla parte bassa degli indirizzi, creando dei falsi positivi. Una sessione tipica di comunicazione su I2C si svolge attraverso una serie precisa di fasi, parte delle quali è gestita direttamente dalla libreria WIre fornita nell'IDE. Dato che le due linee di comunicazioni sono bidirezionali, il Master inizia la propria attività verificando che non ci sia nessun altro dispositivo che sta usando il bus e nello specifico controlla che SDA e SCL siano entrambi ad alto livello. Dalle resistenze di pullup fanno parte della normale configurazione hardware così  che in assenza di dispositivi "attivi" entrambe le linee si presentino con un livello alto. I valori vanno da 2 a 10 Kohm. Con le linee a livello alto, il Master inizia la propria sequenza  di Start mandando bassa la linea SDA, senza che ci sia un segnale di clock. Gli altri dispositivi interpretano questa variazione e sanno che il bus è occupato con la linea SDA ancora bassa , il Master inizia ad inviare il segnale di clock e solo dopo il primo fronte di discesa su SCL, la linea SDA torna alta e viene predisposto l'invio dell'indirizzo del dispositivo con cui il Master vuole dialogare. Dopo aver inviato l'indirizzo allo Slave, il Master invia anche il bit che segnala se l'operazione da effettuare e di lettura o scrittura. La periferica a cui è destinata la comunicazione, a questo punto, risponde al Master indicando che ha correttamente ricevuto la richiesta. A questo punto parte lo scambio di dati a otto bit fra Master e Slave secondo quanto i due dispositivi intendono fare, ovvero leggere e scrivere celle di memoria, recuperare informazioni sui sensori e quant'altro. Le regole che vanno comunque rispettate nella comunicazione sono che SDA assume un valore valido solo se la linea SCL è a livello basso (ricordiamo che la resistenza di pullup tiene alta la linea e che quindi il livello basso e necessariamente generato da un dispositivo collegato). Questo meccanismo permette anche di poter liberare il bus con una sequenza di stop. i cicli di clok generati dal Master sono quindi quelli che scandiscono la comunicazione anche da parte dello Slave che, quando scrive i propri dati e le proprie risposte di acknowledgement, lo deve fare nella parte bassa dell'impulso di clock. L'acknowledgement è un bit che lo Slave invia sul bus a nono impulso di clock, ovvero al primo ciclo di clock dopo la ricezione dell'ottavo bit dal Master. Allo stesso modo lo Slave si aspetta un bit di conferma al ciclo di clock successivo al suo ottavo bit trasmesso. Senza questo bit ricevuto da parte di chi ha ricevuto il byte di dati , si ritiene  che la trasmissione non sia andata a buon fine generando una conduzione di errore nella comunicazione. Mentre il Master dialoga con un dispositivo, potrebbe aver bisogno di dialogare con un altro Slave e quindi ha due possibilità: chiudere la comunicazione con la sequenza di Stop e poi riprendere con uno Start e un nuovo indirizzo, con il rischio che qualche altro dispositvo assuma il ruolo di Master, oppure può inviare una nuova sequenza di Start che viene interpretata dallo Slave corrente come una chiusura della comunicazione. In questo modo, il bus non viene mai liberato e il Master non perde la sua posizione di controllo dello stesso. A parte le sequenze di Start e Stop, quello che succede fra Master e Slave durante il loro dialogo dipende in modo specifico dai dispositivi. Il protocollo definisce infatti come la comunicazione inizia, si instaura fra due dispositivi in modalità Master/Slave e lettura/scrittura e come termina, mentre non dice nulla sul significato dei dati scambiati.

UN ESEMPIO PRATICO                                                                                                                                                                        

Ecco come è possibile lavorare con una memoria EEPROM di tipo I2C, modello 24LC16B con 8 pagine da 256 byte (praticamente 16 kBit  o 2 kbyte). Innanzitutto questo dispositivo è solitamente disponibile in un contenitore di tipo DIL a 8 pin in una serie di varianti per SMD. nella versione Dual in Linegli otto pin anno i collegamenti di Fig. 1. Vss e Vcc sono rispettivamente la massa e il positivo dell'alimentazione, poi SCL e SDA sono i due pin con cui potremo comunicre con la memoria  con il protocollo I2C, mentre WP è un pin che se collegato al positivo inibisce le operazioni di srittura (Write Protect). A0, A1 e A2 sono non collegati, ma sono presenti per compatibilità con memorie EEPROM di dimensioni maggiori. Come abbiamo visto, se il master vuole parlare con questo dispositivo, inizia con il comando di start, quindi invia l'indirizzo dello Slave e successivamente comunica con lui. Il primo byte inviato sul bus è definito come Control Byte e contiene l'indirizzo del dispositivo assieme all'indirizzo della pagina da utilizzare, secondo lo schema di bit di Fig. 2. Praticamente questa memoria dispone di un indirizzo fisso sui primi quattro  dei sette bit e lascia tre bit per selezionare le otto pagine, seguiti da un bit che indica se le operazioni sono di lettura o scrittura e infine c'è il nono bit di acknowledgement che sarà generato dallo Slave a conferma della ricrezione degli otto bit precedenti. Evidentemente, salvo soluzioni che prevedono l'impiego dei circuiti logiciaggiuntivi, sul bus I2C è possibile mettere un solo chip  di memoria 24LC16 perchè non è possibile assegnarli un indirizzo diverso. Dopo il control byte che instaura la comunicazione, seguono i dati che possono essere letti o scritti dalla memoria secondo i quattro schemi  Fig 3 e Fig 4. Dopo il byte di controllo contenente l'indirizzo di tre bit della pagina e il bit di lettura o scrittura, quello che può avvenire si limita quindi a quattro possibilità: due di lettura e due di scrittura. Da notare che avendo ogni pagina  256 byte, basta un byte per l'indirizzamento, mentre la distinzione fra una modalità di scrittura a byte o a pagina viene desunta dal fatto che il Master invia due byte o un flusso di 16 byte prima dello stop. Ogni componente I2C ha la propria documentazione circa la modalità di colloquio e programmazzione, ma da quanto si può gia intuire con le memorie seriali, non si tratta mai di situazioni particolarmente complesse. Indispensabile, comunque, acquistare la documentazione con tutti i dettagli della comunicazione per i dispositivi che vorrete collegare, oppure potete cercare se in rete sono già disponibili librerie specifiche da usare con Arduino per lo specifico componente.

LA LIBRERIA WIRE                                                                                                                                                                                  

Ora che i meccanismi di I2C sono chiari, diventa abbastanza semplice capire le funzioni offerte dalla libreria Wire. Queste vi permettono di far assumere ad Arduino il ruolo di Master o Slave, di instaurare la comunicazione per il trasferimento di dati e di gestire il flusso della comunicazione stessa. Questa libreria eredita alcune funzioni della libreria Stream e per omogeneità con tutte le altre librerie che gestiscono flussi di dati in lettura e scrittura, sono state modificate le istruzioni di lettura e scrittura che cambiano da send() e receive() a write() e read(). Il numero di bit gestiti per gli indirizzi della libreria sono sette, con l'ottavo che è utilizzato per definire se l'operazione e in lettura o scrittura. Nel caso lo sketch usi indirizzi con l'ottavo bit valorizzato, questo sarà scartato dalla libreria.

begin()/begin(address)

 Questa funzione permette di inizializzare la libreria e attribuire il ruolo di Master o Slave alla scheda Arduino. Se non si specifica un indirizzo, Arduino acquisirà il ruolo di Master sul bus, mentre se si indica un indirizzo, sarà quello a cui risponde come Slave. Con questo meccanismo, è quindi anche possibile far comunicare fra loro delle schede  Arduino che possono assumere entrambi i ruoli.

requestFrom(address, count)

Se Arduino è stato configurato come Master, con questa funzione puo richiedere al dispositivo Slave con indirizzo address di inviargli un numero count di  bytes, che poi andranno letti effettivamente con la funzione read(). Per poter leggere i byte, questi dovranno essere anche disponibili nel buffer di comunicazione, da verificare con available().

beginTransmission(address)

Sempre come Master, con questa funzione Arduino inizia la procedura di trasmissione di dati al dispositivo caratterizzato sul bus dall'indirizzo address. Si tratta di un processo che prevede anche il write() dei byte e l'effettiva trasmissione degli stessi quando il pachetto da trasmettere viene indicato come completo attraverso la funzione endTransmission().

write()

Con questa istruzione si scrive in u buffer un byte alla volta; questo permette di creare l'intero vettore di byte da trasmettere in modo sincrono al dispositivo destinatario senza che ci siano pause fra i byte. Il protocollo I2C prevede infatti che la comunicazione avvenga con una cadenza precisa, senza "pause" fra i byte di un medesimo pacchetto. L'inizio della scrittura è determinato da beginTransmission().

endTranmission()

 Quando viene invocata questa funzione, la libreria provvede a inoltrare i byte scritti nel buffer di trasmissione al dispositivo Slave il cui indirizzo address è stato definito con la funzione beginTransmission(address). Questa funzione restituisce un byte che ha i seguenti significati:

  • 0: trasmissione andata a buon fine
  • 1: troppi dati per la dimensione del buffer di comunicazione
  • 2: ricevuto un NACK (errore di trasmissione) sulla trasmissione dell'indirizzo
  • 3: ricevuto un NACK sulla trasmissione dei dati 
  • 4: altro errore

 read()

Questa è la funzione con cui vengono letti i byte ricevuti come Master o come Slave. In entrambi i casi, i byte sono prelevati dal buffer di ricezione il cui contenuto può essere sempre controllato con available(). Inserendo la funzione read() in una struttura while (available()) continueremo a leggere dati fichè il buffer di ricenzione ne contiene, dato che solo quando il valore restituito da available() va a zero si ha l'uscita dal loop.

onReceive(handler)

Quando Arduino è configurato come slave, deve aspettarsi comunicazioni dal Master in qualsiasi momento. Per questo, grazie a onReceive(handler) è possibile definire la funzione "handler" che viene chiamata quando vengono ricevuti dati dal Master. Alla funzione viene passato  solo un byte che è il numero di byte ricevuti nella trasmissione dal Master. Una volta impostata la funzione nello sketch con una sintassi del tipo void myHandler(int numBytes), questa verrà chiamata immediatamente alla ricezione dei dati, interrompendo l'esecuzione dello sketch.

onRequest(handler)

Simile alla precedente, questa funzione definisce la funzione handler da chiamare quando lo Slave riceve una richiesta di dati da parte di un Master. Se Arduino è configurato come Slave, deve rispondere alla richiesta del Master e lo fa sttraverso il gestore (in inglese handler) definito con questa funzione. Come arriva la richiesta, l'esecuzione del codice passa al gestoreche deve onorare la richiesta per non creare un errore sul bus I2C 

IL NOSTRO PROGETTO

Con questa libreria potevamo scegliere fra una serie di dispositivi e abbiamo optato pern un accellerometro della Freescale: il tipo MMA7455. Questo piccolo ed economico dispositivo utilizza delle minuscole masse sospese fra degli elettrodi, realizzando dei condensatori la cui capacità