I2C con Bash 

Raspberry Pi collegato a MCP23017

Raspberry possiede un bus I2C accessibile all'utente (nota 1). 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. In questa pagina qualche informazione.

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

Per verificare il funzionamento del bus i2C è necessario collegare almeno un dispositivo slave.

Un primo circuito utilizzato come esempio è quello mostrato nella fotografia di apertura e realizzato interamente su breadboard; è costituito da due dispositivi, tra di loro assolutamente indipendenti e scelti solo perché "erano nel cassetto":

MCP23017

Un secondo circuito usato negli esempi è costituito da RPi demo che, tra gli altri componenti, ospita un LM75A.

Ovviamente è possibile condurre i primi "esperimenti" con qualunque circuito I2C, purché dotato di interfaccia a 3.3 V.

Alcune osservazione comuni:

Ovviamente è possibile scegliere altri indirizzi.

Cercare i dispositivi presenti

Innanzitutto verifichiamo se i circuiti integrati connessi al bus vengono visti correttamente, attraverso il comando i2cdetect -y 1

L'output fornito dal programma è una tabella simile alla seguente:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- 4b -- -- -- 4f
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Di seguito alcuni esempi che utilizzano i due comandi i2cget e i2cset, per leggere e scrive nei registri di una periferica.

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: LM75A

Questo esempio con LM75A è molto simile al precedente: in pratica si tratta solo di interpretare diversamente i due byte ricevuti.

pi@raspberrypi:~ $ i2cget -y 1 0x4f 0x00 w
0x8011

In questo caso 0x11 (17 in decimale) è la parte intera della temperatura e 0x80 è la parte frazionaria, formata dai soli tre bit più significativi (1000 0000 → 0,5 in decimale). A fondo pagina uno script esemplificativo.

Esempio 3: 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 accendere 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 rimuovere il modulo (se già caricato al boot) e poi ricaricarlo con la nuova frequenza. Questa procedura in realtà non è consigliata, in quanto I2C viene usato oggi quasi esclusivamente con clock a 100 KHz.

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 possibilità.

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

Note

  1. Nelle vecchie versioni di RPi era disponibile un secondo bus I2C su un connettore separato. A partire dal B+ il secondo bus è dedicato allo standard HATs (Hardware Attached on Top)

 

Prima di creazione di questa pagina: marzo 2016
Ultima modifica: 30 marzo 2018


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


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima