Salta la barra di navigazione [1] - Vai alla barra di navigazione [3] - Scrivimi una mail [4]

Rapsberry Pi: I2C con Bash 

Raspberry Pi - Note di hardware - Sommario - Novità - Tutorial - Progetti - Risorse - Non solo elettronica

Raspberry Pi collegato a MCP23017

Raspberry possiede un bus I2C accessibile all'utente. Questa interfaccia permette di collegare numerosi circuiti integrati adatti ad interagire col mondo reale: misurare temperature o tensioni, gestire led, LCD e pulsanti, generare tensioni sono solo alcuni esempi. Internet è ricca di materiale su questo bus e quindi non verrà qui descritto.

Configurare Raspberry Pi

Innanzitutto occorre verificare che il kernel sia configurato per usare l'hardware I2C e che sia presente il software necessario. Se usate una versione di Raspbian decentemente aggiornata i moduli relativi sono già presenti nell'installazione standard, anche se non sono attivi.

La strada più semplice è l'attivazione tramite raspi-config (Advanced Options → I2C)

pi@raspberrypi:~ $ sudo raspi-config

Dopo il riavvio:

pi@raspberrypi:~ $ ls -l /dev/i2c-1
crw-rw---- 1 root i2c 89, 1 Mar 6 13:44 /dev/i2c-1

Serviranno inoltre alcuni pacchetti:

vv@vvrpi ~ $ sudo aptitude install libi2c-dev i2c-tools

Infine, se serve, occorre aggiungere gli utenti che utilizzeranno il bus I2C al gruppo automaticamente creato.

vv@vvrpi ~ $ sudo usermod -a -G i2c vv

L'hardware

Il circuito utilizzato per le prove è costituito da due dispositivi, tra di loro assolutamente indipendenti e scelti solo perché "erano nel cassetto":

Ovviamente siete liberi di usare anche altri circuiti integrati, purché con interfaccia I2C a 3.3 V: dovrete solo fare modiche al software, più o meno grandi a seconda della tipologia dell'integrato stesso.

MCP23017

Il circuito è costituito dai due integrati con le linee SDA e SCL collegate direttamente al connettore GPIO del Raspberry Pi. Le due resistenze di pull-up su SDA e SCL, obbligatorie, sono già presenti sul circuito stampato del Raspberry Pi e quindi non vanno aggiunte.

Non sono state disegnate le resistenze di pull-up relative agli interruttori di MCP23017; non si tratta di un errore in quanto, come verrà detto più avanti, queste resistenze possono essere configurate internamente al circuito intergrato stesso. I valori delle resistenze R2... R9 non sono critici: possono avere un valore compreso tra 100 e 470 ohm. Neppure è critico il valore di R1.

I due dispositivi devono avere indirizzi di 7 bit diversi, impostati in parte dal costruttore e in parte dall'utilizzatore.

Per MCP23017 è stato scelto l'indirizzo 0x20 impostando i tre bit esterni A2-A1-A0 tutti a zero. Infatti la parte di indirizzo del dispositivo decisa dal costruttore e reperibile sui fogli tecnici, i 4 bit più significativi, è 0100. L'indirizzo complessivo è quindi 010 0000, 7 bit in totale. Ovviamente è possibile scegliere altri indirizzi, per esempio se è necessario collegare allo stesso bus più MCP23017.

Analogamente, la parte fissa dell'indirizzo di LM77 è 10010, la parte variabile, in base allo schema, 11. L'indirizzo completo è quindi 100 1011, cioè 0x4B.

Cercare i dispositivi presenti

Innanzitutto occorre sapere come Raspberry Pi vede un dispositivo I2C.

Sono disponibili uno o due bus, a seconda della versione di Raspberry Pi in uso, visibili attraverso i file:

vv@vvrpi ~ $ ls -l /dev/i2c-*
crw-rw---T 1 root i2c 89, 0 Jun 29 04:23 /dev/i2c-0
crw-rw---T 1 root i2c 89, 1 Jun 29 04:23 /dev/i2c-1

Di questo solo uno è presente sul connettore GPIO. Purtroppo, a seconda della revisione è uno o l'altro...

  1. Il Raspberry Pi della revisione 1 rende disponibile il bus 0
  2. Il Raspberry Pi della revisione 2 rende disponibile il bus 1. Gli esempi seguenti sono riferiti a questa seconda versione
  3. Il Raspberry Pi B+ rende disponibile il bus 1

Il comando per individuare la presenza e, in parte, il corretto funzionamento dei dispositivi è i2cdetect. I due comandi per leggere e scrivere dal bus sono, rispettivamente, i2cget e i2cset.

Innanzitutto verifichiamo se i chip connessi al bus vengono visti, attraverso il comando i2cdetect:

vv@vvrpi ~ $ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- 4b -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Esempio 1: LM77

Il primo esempio utilizza LM77, un sensore di temperatura piuttosto diffuso. Abbiamo già verificato che il suo indirizzo è 0x4B.

Passiamo quindi ad interrogare il dispositivo, leggendo una word dal registro 0x00 dal dispositivo 0x4B sul bus 1 (anche qui si rimanda ai fogli tecnici la spiegazione dettagliata):

vv@vvrpi ~ $ i2cget -y 1 0x4b 0x00 w
0x6001

Qualche dettaglio sul comando, ben sapendo che man i2cget è più esaustivo; come dice il nome, i2cget serve per leggere il contenuto di un registro della periferica. I cinque parametri sono rispettivamente:

La risposta ovviamente deve essere interpretata... Per chi ha fretta, 0x6001 contiene la temperatura in gradi centigradi, in complemento a due (in tutto 9 bit utili più il segno ripetuto 4 volte, con precisione di mezzo grado), seguita da tre bit di stato. Per conoscere la temperatura occorre riordinare i due byte (nell'esempio dobbiamo ottenere 0x0160), togliere i tre bit di destra e dividere per due. Qui sotto un codice esemplificativo (devo dire di non immediata comprensione, Bash non si presta molto a manipolare numeri).

Esempio 2: MCP23017

Come secondo esempio utilizziamo MCP23017 che mette a disposizione 16 ingressi e uscite digitali.

Per leggere il valore dei bit in ingresso alla porta A occorre effettuare nell'ordine le seguenti tre operazioni (si rimanda al foglio tecnico del MCP23017 per i dettagli):

  1. Configurare i pin come ingresso, come peraltro già predefinito all'accensione, scrivendo 1 in ciascun bit del registro IODIRA (0x00)
  2. Attivare la resistenza di pull-up, operazione opzionale, ma comoda per esempio per leggere lo stato di interruttori e simili. Occorre scrivere 1 in ciascun bit del registro GPPUA (0x0c) per inserire una resistenza fiacca (termine che utilizza Microchip... meglio resistenza di pull-up) collegata alla tensione di alimentazione, e poter così leggere un valore alto nel caso di pin scollegato (altrimenti la lettura è casuale)
  3. Leggere il valore dal registro GPIOA (0x12)

Di seguito i tre comandi, utilizzando la porta A:

vv@vvrpi ~ $ i2cset -y 1 0x20 0x00 0xFF
vv@vvrpi ~ $ i2cset -y 1 0x20 0x0C 0xFF
vv@vvrpi ~ $ i2cget -y 1 0x20 0x12 b
0x3f

La risposta, tradotta in binario ( 0011 1111 ) indica che i primi due ingressi sono collegati a massa, gli altri 6 sono lasciati non collegati.

Qualche dettaglio su i2cset: come dice il nome, serve per impostare il contenuto di un registro. I cinque parametri (tutti in esadecimale) sono rispettivamente:

Simili sono le operazioni che occorre compiere per scrivere, nel nostro caso accendere i LED. Di seguito si utilizzarà la porta B del MCP23017 per acendere un LED si e l'altro no. Occorre:

  1. Configurare i pin come uscita, scrivendo 0 in ciascun bit del registro IODIRB
  2. Scrivere il valore che si intende visualizzare nel registro GPIOB

 vv@vvrpi ~ $ i2cset -y 1 0x20 0x01 0x00
vv@vvrpi ~ $ i2cset -y 1 0x20 0x13 0xAA

Qui sotto, un breve script esemplificativo che accende 8 LED in sequenza (supercar.sh).

I segnali

La figura seguente mostra lo stato di SDA (in color ocra) e SCL (in rosso) durante la scrittura del byte 0x40 nel registro 0x13 del dispositivo con indirizzo 0x20.

I2C: scrittura di un byte

Due osservazioni:

L'immagine seguente mostra, in tempo reale, come il bus evolve durante l'esecuzione del programma "supercar".

Scrittura sul bus i2c

Infine un dettaglio del diagramma temporale relativo alla condizione di start e alla trasmissione dei primi due bit: si può osservare come il tempo di salita di entrambi i segnali sia molto più alto rispetto a quello di discesa; inoltre la curva esponenziale è quella tipica della carica di un condensatore attraverso una resistenza. Si tratta di un effetto della resistenza di pull-up del bus I2C, che carica le capacità parassite presente sul bus.

I2C: un dettaglio

La velocità di clock può essere modificata agendo sui parametri passati al modulo. E' necessario, come root, prima rimuove il modulo (se già caricato al boot) e poi ricaricarlo con la nuova frequenza. Per esempio, per aumentare la frequenza a 400 kHz (Fast Speed, secondo lo standard) le operazioni da fare sono le seguenti:

vv@vvrpi ~ $ sudo modprobe -r i2c_bcm2708
vv@vvrpi ~ $ sudo modprobe i2c_bcm2708 baudrate=400000

Ovviamente la modifica è temporanea. Se si vuole renderla permanente è sufficiente agire sul file /etc/modules.

In teoria è possibile impostare anche la cosiddetta Fast Speed Plus (1 MHz) e anche oltre. Purtroppo la scelta operata per le resistenze di pull-up rendono piuttosto difficile salire a questa velocità, a meno di realizzare con molta cura i collegamenti, riducendo al minimo le capacità parassite.

Il grafico seguente è relativo ad un circuito operante ad 1 MHz; pur funzionando correttamente, si vede chiaramente che lavora al limite delle sue potenzialità

I2C a 1 MHz (fast speed +)

Se nel vostro progetto è richiesta una velocità di clock superiore a 100 kHz, probabilmente avete fatto scelte sbagliate in merito alla tipologia del bus, meglio sarebbe stato puntare su altro, come SPI. Tra l'altro lo standard SMBus, molto simile a I2C e per molti versi più "moderno", richiede obbligatoriamente un clock tra 10 e 100 kHz.

Simili esempi sono presenti nelle pagine PIC18 come master I2C  e Collegare una periferica I2C al bus USB

Il codice

Ultima modifica di questa pagina: 7 marzo 2016


Raspberry Pi - Note di hardware

Vedi anche: Il taccuino tecnico


Licenza "Creative Commons" - Attribuzione-Condividi allo stesso modo 3.0 Unported


EN - Pagina principale - Sommario - Accessibilità - Note legali e privacy policy - Posta elettronica

XHTML 1.0 Strict - CSS 3