Il bus Inter-Integrated Circuit (I2C) è un meccanismo di comunicazione seriale a livello di chip che funziona su soli due fili. Alcuni sviluppatori pronunciano il nome del bus eye-two-see, altri eye-squared-see, ma entrambi si riferiscono alla stessa cosa. Ideato da Philips nei primi anni 1980, I2C è stato istituito per standardizzare le comunicazioni tra i chip della società, ma da allora è diventato uno standard de facto supportato da molti dispositivi microcontrollori da schede Arduino al Raspberry Pi e, naturalmente, imps.,
Il Bus fisico
I2C si compone di due fili. Una linea I2C trasmette i dati, l’altro i segnali di clock che sincronizzano la conversazione tra i dispositivi. La linea dati è chiamata ‘SDA‘, la linea di clock’SCL’.
Tipicamente, sia SDA che SCL sono collegati a una linea di alimentazione da 3,3 o 5 V attraverso un singolo resistore di “pull-up”, uno su ciascuna linea. Questo è necessario perché le connessioni SDA e SCL dei dispositivi sono linee ‘open drain‘: possono forzare la tensione sulla linea a 0V, o’ low‘, ma non possono aumentarla a 3.3 V, o’high’., Alto e basso sono le rappresentazioni elettriche degli 1 e 0 che sono i componenti fondamentali dell’informazione digitale. L’aggiunta di questi due resistori — e il bus ne ha bisogno solo due, indipendentemente dal numero di dispositivi collegati ad esso-assicura che la tensione torni a 3,3 V senza cortocircuiti.
I2C insiste che i dispositivi hanno linee di scarico aperte per garantire che le correnti elevate che danneggiano i componenti siano in grado di fluire quando due dispositivi cercano di segnalare simultaneamente.
Nella maggior parte dei casi, ci si aspetta di aggiungere questi resistori da soli, ma alcuni dispositivi, in genere quelli che operano a 3.,3V, includerli per essere compatibili con i dispositivi che forniscono 5V. Ricorda, hai solo bisogno di un paio di resistori pull-up per bus, quindi potrebbe essere necessario rimuovere i resistori pull-up collegati ad altri dispositivi sul bus. Sebbene l’imp abbia resistori di pull-up interni, questi sono troppo deboli per essere utili per I2C e quindi vengono automaticamente disabilitati quando i suoi pin sono impostati per gestire i segnali I2C.
Controller e periferiche
Il bus I2C separa i dispositivi in ‘controller’ e ‘periferiche’., Solo un dispositivo può inviare impulsi di temporizzazione sulla linea SCL alla volta, e questo è quello scelto per essere il controller. Tutti gli altri sincronizzano i loro tempi al controller, e sono quindi considerati periferiche. Il controller — tipicamente l’imp-e le sue periferiche possono trasmettere e ricevere dati, ma solo il controller può dire a una periferica quando trasmettere i dati.
Indirizzamento
Affinché un dispositivo I2C possa comunicare con un altro su base one-to-one, entrambi i dispositivi devono essere identificabili in modo univoco. Questa identità è l’indirizzo I2C del dispositivo., Gli indirizzi I2C sono solitamente numeri a 7 bit, quindi un bus può comprendere fino a 127 dispositivi in tutto. Un byte comprende otto bit; il bit in più viene utilizzato per indicare se il segnale viene inviato dal controller alla periferica — una “scrittura” — o nell’altra direzione — una “lettura”. Questo ottavo bit è in realtà bit zero nel byte di indirizzo inviato sul bus. L’indirizzo a 7 bit viene inserito in bit da uno a sette del byte dell’indirizzo.
In linea con questo formato, l’API imp prende un indirizzo I2C come valore a 8 bit., I fornitori di dispositivi di solito forniscono gli indirizzi dei loro prodotti come un numero a 7 bit, quindi è necessario convertire l’indirizzo da sette bit a otto. Questo è un risultato ottenuto con l’operatore <<
di Squirrel, che sposta i singoli valori di bit di un numero a sinistra. Questo processo è l’equivalente di moltiplicare per due. Nel codice, questo processo si presenta così:
local sevenBitAddress = 0x39;local eightBitAddress = sevenBitAddress << 1;
Squirrel imposta automaticamente bit zero al valore corretto definito da I2C: 0 per un’operazione di scrittura, 1 per una lettura., Ora sei pronto per utilizzare uno dei metodi i2c di imp per scrivere dati sul bus:
i2c.write(eightBitAddress, dataInStringForm);
L’indirizzo a 7 bit del dispositivo con cui vuoi che il tuo imp comunichi sarà fornito dal produttore del componente ed elencato nella scheda tecnica del dispositivo. Potrebbe non essere fisso, ma selezionato da un intervallo di indirizzi in base alla tensione applicata a un altro dei pin dei dispositivi., Ad esempio, un sensore di luce TAOS TSL2561 ha tre possibili indirizzi: 0x29
, 0x49
o 0x39
, a seconda che il suo pin ADDR sia fisso a 0V, 3.3 V o “fluttuante” tra i due. Diciamo che il valore è fluttuante perché non è stato selezionato attivamente per funzionare a una tensione specifica; potrebbe essere qualsiasi cosa da 0 a 3,3 V inclusi.
Segnalazione
Il controller del bus I2C utilizza l’indirizzo a 7 bit di una periferica per identificare il componente con cui vuole parlare., In effetti, la segnalazione è più complessa di quella, ma fortunatamente tutti i dettagli sono gestiti dall’imp in modo che sia necessario fornire solo l’indirizzo come valore a 8 bit.
Se stai scrivendo sulla periferica, devi anche fornire i dati da scrivere, che spesso includono valori di registro che istruiscono la periferica su cosa fare con i dati. La documentazione dell’API imp si riferisce a questi registri come ‘sotto-indirizzi’:
i2c.write(eightBitAddress, dataString);
write() richiede dati in forma di stringa. Di conseguenza, potrebbe essere necessario convertire il valore memorizzato in altri tipi di variabile in forma di stringa.,
La lettura delle informazioni da un dispositivo può richiedere un comando per indicare al dispositivo quali dati recuperare. Dire all’imp di leggere i dati dal bus I2C implica anche fornire un terzo parametro, il numero di byte che ci si aspetta di ricevere:
i2c.read(eightBitAddress, controlDataString, numberOfBytesToRead);
Dietro queste operazioni ci sono i segnali elettrici dell’I2C, applicati alla linea SDA sincronizzati con gli impulsi di temporizzazione applicati alla linea SCL. La scrittura sul bus comporta un indicatore di avvio: la caduta di SDA a 0V mentre SCL è 3.3 V. La modifica della tensione SDA quando la tensione di SCL è alta definisce i marcatori di avvio e arresto., Se la tensione SDA non cambia mentre SCL è alta, i dispositivi I2C sanno che i dati, piuttosto che i marcatori, vengono inviati.
SDA ora va alto o basso per inviare ogni bit del byte di indirizzo: l’indirizzo del dispositivo a 7 bit seguito dal bit di lettura / scrittura. I bit del byte vengono inviati al bit più a sinistra-il “bit più significativo” — prima, con SDA che va alto se il valore del bit è 1 o basso se è zero. La periferica di destinazione ora tirerà SDA basso per segnalare la conferma di ricezione dei dati, e quindi esce otto bit di informazioni o dati di controllo, seguiti da più dati se necessario., C’è una pausa di riconoscimento ” ack ” a singolo impulso su SDA tra ogni otto bit inviati, temporizzata su un nono impulso SCL. Se la periferica non conferma ricevuta in questo modo, il controller rileverà che SDA è rimasto alto e segnalerà un errore.,
Quando i dati fluiscono dalla periferica al controller, quest’ultimo riconosce anche la ricezione di otto bit tirando SDA low sul nono impulso SCL a meno che questo non sia il byte finale di un batch, nel qual caso il controller non tira SDA low — fa un segnale ‘nak’, o ‘no acknowledgement’ — per far sapere alla periferica che ha finito.
Quando abbiamo finito, SDA va in alto come un marcatore di arresto.,
L’inizio della trasmissione è indicato da SDA che scende dall’alta alla bassa tensione (il rettangolo a sinistra),
si ferma dal retro (il rettangolo a destra). SCL deve essere alto quando questo avviene
Timing
La velocità di clock standard per le comunicazioni I2C è 100kHz — 100.000 impulsi SCL al secondo. È possibile andare più velocemente, fino a 400kHz. Alcuni dispositivi potrebbero non essere in grado di supportare questa velocità; controllare la scheda tecnica che accompagna il dispositivo che si desidera collegare al imp., Tuttavia, molti dispositivi lenti utilizzano una tecnica chiamata ‘clock stretching’ per forzare i dispositivi più veloci a lavorare alla loro velocità. L’imp supporta i dispositivi che fanno uso di questa tecnica. Essenzialmente, tengono SCL basso mentre stanno recuperando i dati che vuoi che inviino all’imp. L’imp lo rileva, rilascia la linea SCL e attende che SCL torni alto prima di continuare.
Tuttavia, potrebbe essere necessario abbassare la velocità I2C se le caratteristiche elettriche del set-up rallentano la velocità della transizione tra 0V e 3.3 V, chiamata “tempo di salita”., Questo è spesso causato da fili lunghi, che aumentano la capacità del circuito. Affinché i dispositivi possano rilevare con successo la trasmissione di ciascun bit, il bus deve funzionare più lentamente. Corruzione dei dati o risultati imprevisti sono gli indizi che si dovrebbe guardare fuori per. Ridurre la velocità del bus I2C fino a quando i dati vengono letti con successo.
L’API imp attualmente fornisce quattro valori di clock predefiniti: 10, 50 100 e 400kHz., Sono stati selezionati dal passaggio di una costante per I2C metodo di configurazione come parametro:
i2c.configure(speedConstant);
in cui il valore di speedConstant è uno dei
- CLOCK_SPEED_10_KHZ
- CLOCK_SPEED_50_KHz
- CLOCK_SPEED_100_KHZ
- CLOCK_SPEED_400_KHZ
Impostazione di Un imp I2C
L’i2c oggetto nell’esempio di righe di codice di cui sopra non è fornito direttamente da imp, ma scelto da voi, secondo la quale la scelta dell’imp pin potrai utilizzare per I2C comunicazioni., Ogni tipo di imp ha più bus I2C, tutti resi disponibili all’avvio. Controlla il pin mux per il tipo di imp che stai usando per vedere quali oggetti I2C sono disponibili per te. Qui, assumeremo che tu stia usando un imp001. I due bus I2C di imp001 si trovano sui pin 1 e 2 e sui pin 8 e 9, rispettivamente istanziati come proprietà i2c12 e i2c89 dell’oggetto hardware all’avvio dell’imp. I pin 1 e 8 sono assegnati a SCL, 2 e 9 a SDA.,
È normale fare riferimento alla tua scelta con una variabile globale:
i2c <- hardware.i2c12;i2c.configure(CLOCK_SPEED_100_KHZ);
Codice di esempio
Il seguente codice funziona con il sensore di luce visibile e infrarossa TAOS TSL2561, un dispositivo 3.3 V che utilizza I2C per comunicare con il suo microcontrollore host. La scheda tecnica del chip può essere scaricata dal sito web di Adafruit. Adafruit vende il chip su una scheda breakout a basso costo che include resistori pull-up adatti sul pin di alimentazione, VCC. Ciò significa che è pronto per connettersi direttamente ai pin I2C di un imp.,
Scheda breakout TSL2561 di Adafruit
Nota Dopo che questo articolo è stato scritto, Adafruit ha aggiornato la scheda sensore TSL2561. La nuova versione funziona con il codice corrente.
Ecco il codice, per l’agente e quindi il dispositivo:
Cosa fa il codice
Il codice dell’agente risponde a una richiesta HTTP in arrivo notificando al dispositivo che richiede una lettura. Per semplicità, la lettura viene semplicemente visualizzata nel registro.
Il codice del dispositivo legge il sensore e calcola un valore lux in base alla matematica impostata nella scheda tecnica TSL2561., La prima parte del programma imposta le costanti per i registri e le impostazioni dei tasti del TSL2561, incluse le opzioni di indirizzo I2C. Altre costanti vengono utilizzate nel processo di conversione della luminosità.
Cablando l’imp
Al punto di avvio del programma, il codice alias uno dei pin I2C dell’imp impostato come variabile globale, configura la velocità del bus a 100kHz e quindi sposta l’indirizzo I2C del TSL2561 di un bit verso sinistra in modo che sia pronto per essere utilizzato con le funzioni I2C imp. Quindi imposta il sensore., Giusto per verificare che questo abbia funzionato, leggiamo il registro di controllo: il valore di ritorno dovrebbe essere 3 se il TSL2561 è appena stato avviato, o 51 se il dispositivo è già stato acceso.
Successivamente, il codice imposta la risoluzione ADC del sensore attraverso il registro di temporizzazione del TSL2561, quindi imposta il guadagno del segnale su alto.
Infine, diciamo all’imp come rispondere alle notifiche"sense"
dall’agente. Questo chiama una funzione che legge i due sensori di luce del TSL2561, i cui valori digitali sono memorizzati nei quattro registri ADC a 8 bit del chip., I primi due danno una lettura ottica e infrarossa combinata a 16 bit, la seconda coppia un valore solo IR a 16 bit. Le funzioni readSensorAdc0() e readSensorAdc1 () convertono i singoli valori del registro in numeri a 16 bit moltiplicando il byte più significativo per 256 e aggiungendo il valore del byte meno significativo. La moltiplicazione viene eseguita spostando i bit del numero a otto bit lasciati in otto posizioni con l’operatore <<.,
Il codice fornisce quindi entrambe queste letture a una terza funzione per calcolare una luminosità finale, il valore ‘lux’.
Ulteriori letture
- Errori I2C-Come eseguire il debug dei problemi di lettura e scrittura I2C