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 Raspberry Pi OS 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 (3 Interface Options → I2C

vv@raspberrypi:~ $ sudo raspi-config

Dopo il riavvio:

vv@raspberrypi:~ $ ls -l /dev/i2c-*
crw-rw---- 1 root i2c 89, 1 Apr 24 20:31 /dev/i2c-1
crw-rw---- 1 root i2c 89, 2 Apr 24 20:31 /dev/i2c-2 (nota 2)

Serviranno inoltre alcuni pacchetti:

vv@raspberrypi:~ $ sudo apt install libi2c-dev i2c-tools

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

vv@raspberrypi:~ $ sudo usermod -a -G i2c USERNAME

L'hardware

Per verificare il funzionamento del bus i2C è necessario collegare almeno un dispositivo Slave. È possibile condurre i primi "esperimenti" con qualunque circuito I2C, purché alimentato a 3.3 V.

Alcune considerazioni preliminari:

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

Gli esempi seguenti utilizzano i due comandi i2cget e i2cset, per leggere e scrive nei registri di una periferica.

Attività 1

Il primo circuito usato negli esempi è costituito da RPi demo che, tra gli altri componenti, ospita un LM75A, un semplice sensore di temperatura. La ricerca trova la presenza di un solo dispositivo I2C il cui indirizzo dipende dalla presenza o meno dei jumper JP2, JP3 3 JP4. In seguito supporremo l'assenza di tali jumper (indirizzo 0x4F).

Passiamo quindi ad interrogare il dispositivo, leggendo una word (w, due byte) dal registro 0x00 dal dispositivo 0x4F sul bus 1 (si rimanda ai fogli tecnici per la spiegazione dettagliata dei registri di LM75):

vv@raspberrypi:~ $ i2cget -y 1 0x4f 0x00 w
0x0015

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

La risposta ovviamente deve essere interpretata... Per chi ha fretta, 0x0015 contiene la temperatura in gradi centigradi, in complemento a due (in tutto 11 bit utili più il segno, con risoluzione di un ottavo di grado). Per conoscere la temperatura occorre riordinare i due byte e togliere i bin non significativi. Qui sotto un codice esemplificativo, devo dire di non immediata comprensione: bash non si presta molto a manipolare numeri.

Nell'esempio mostrato 0x15 (21 in decimale) è la parte intera della temperatura e 0x00 è la parte frazionaria (nota 3).

Visualizzando con un oscilloscopio i due segnali SDA e SCL possiamo riconoscere due frame:

i2cget -y 1 0x4f 0x00 w

Attività 2

[ Approfondimento ] Un secondo 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

 

Attività 2.1: LM77

Questo esempio utilizza LM77, un sensore di temperatura piuttosto diffuso. 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@raspberrypi:~ $ i2cget -y 1 0x4b 0x00 w
0x6001

Qui sotto un codice esemplificativo.

Esempio 2.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@raspberrypi:~ $ i2cset -y 1 0x20 0x00 0xFF
vv@raspberrypi:~ $ i2cset -y 1 0x20 0x0C 0xFF
vv@raspberrypi:~ $ 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@raspberrypi:~ $ i2cset -y 1 0x20 0x01 0x00
vv@raspberrypi:~ $ 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

Aumentare la velocità

[ Approfondimento ] La velocità di clock di I2C può essere modificata. Questa procedura in realtà non è consigliata, in quanto I2C viene usato oggi quasi esclusivamente con clock a 100 KHz.

E' necessario, come root, prima rimuovere il modulo (se già caricato al boot) e poi ricaricarlo con la nuova frequenza. Per aumentare la frequenza a 400 kHz (Fast Speed, secondo lo standard) le operazioni da fare dipendono dalle versioni di RPi e del sistema operativo. Per esempio potrebbero essere le seguenti:

vv@raspberrypi:~ $ sudo modprobe -r i2c_bcm2708
vv@raspberrypi:~ $ 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.

Il codice

Se viene mostrato l'errore bash: ./XXX.sh: cannot execute: required file not found Potrebbe essere necessario convertire il file da DOS a UNIX ( dos2unix XXX.sh )

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)
  2. Il bus i2c-2 è riservato ad usi interni, nello specifico al connettore HDMI. Lo stesso vale per altri eventuali bus diversi da i2c-1
  3. La temperatura rilevata è notevolmente superiore a quella ambiente in quanto il sensore è riscaldato dal processore

Prima di creazione di questa pagina: marzo 2016
Ultima modifica: 24 aprile 2024


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


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima