iot

In questo post realizzeremo un programma di utilità che restituisce l'indirizzo dei dispositivi collegati al BUS I2C.

La comunicazione I2C è una seriale in cui vengono utilizzate la linea dati (SDA) e la linea clock (SCL). La comunicazione I2C è di tipo master-slave, il master (quello che instaura il dialogo) nell'esempio che segue è il microntrollore PIC, Il sensore laser che faremo dialogare con il micro è lo slave; su una singola linea I2C possono venire collegati fino a 112 dispositivi slave. Nel protocollo I2C ogni slave ha un indirizzo che può essere espresso da una cifra a 7 bit oppure da una cifra a 10 bit, è necessario conoscere l'indirizzo dello slave per stabilire la connessione.

Il bus I2C prevede l'utilizzo di due soli componendi discreti, due resitenze di pull-up collegate su SDA e su SCL. Nel nostro caso la scheda per la sperimentazione che monta il sensore laser è già equipaggiata con le due resistenze, di conseguenza il collegamento al micro non richiederebbe altri componenti. E' anche vero che per il nostro scopo, dobbiamo considerare l'ipotesi che anche nessuno slave sia collegato; in questo caso il bus I2C fluttuerebbe non avendo le resistenze di pull up collegate e il programma restituirebbe valori non attendibili. Quindi montiamo sulla breadboard due resistenze di pull up da 10Kohm che andranno in parallelo a quelle del sensore laser.

Dal datasheet del sensore (cod. scheda CJV L53L0X V2) si ricava che l'indirizzo I2C è 0x52 e che il bus può lavorare in modalità "fast" ovvero a una frequenza di 400kHz. Utilizzeremo la conoscenza dell'indirizzo per verificare il corretto funzionamento del programma.

schtut1 

Avviamo l'IDE e il tool MCC. Per prima cosa è necessario abilitare la Master Synchronous Serial Port (MSSP) clicchiamo due volte sulla voce indicata in figura:

Schermata 2017 12 06 alle 16.45.10

C'è solo un valore da modificare rispetto a quelli proposti dal programma: la velocità di trasmissione. Modificare il valore del "Baud Rate Generator" a 0x27 e premere invio, il campo sottostante si aggiornerà di conseguenza mostrando 400kHz (si suppone di avere il clock di sistema a 64MHz -> tutorial_1_step1). Si prema poi il bottone "Generate".

Schermata 2017 12 06 alle 17.14.35

Questa operazione crea due nuovi file: i2c1.h e i2c1.c inoltre vengono modificati i file relativi alla configurazione dei pin e dell'interrupt.

Il file interrupt viene modificato in modo da lanciare le nuove funzioni definite nei file i2c.* quando viene generato l'irq sui registri dell' I2C. I file pin_manager.* vengono aggiornati in modo da inizializzare opportunamente le porte alla nuova funzionalità, inoltre vengono definiti alias e macro utili allo scopo. Infine abbiamo i due nuovi file i2c1.* con le funzioni necessarie a gestire la seriale I2C, inviare e ricevere dati.

Il lavoro più grosso e ripetitivo (come la corretta configurazione di tutti i registri del micro) è stato eseguito da MCC lasciandoci liberi di concentrarci sull'obiettivo del programma.

La logica del programma segue la seguente flow chart:

Immagine del 08 12 17 alle 11.17

In questo caso il dato che inviamo al dispositivo è irrilevante in quanto noi ci concentriamo sulla risposta "ACK" che il dispositivo rimanda quando viene correttamente indirizzato.

In realtà il codice creato dal tool MCC non ci fa entrare nel dettaglio del segnale "ACK" in quanto separa le varie fasi di invio con delle etichette più comprensibili tipo: I2C1_MESSAGE_COMPLETE oppure I2C1_MESSAGE_PENDING. Nel dettaglio la flow chart relativa alla verifica della risposta "ACK" è la seguente:

Immagine del 12 12 17 alle 16.45

Questo è invece il codice della funzione i2c_detect:

Schermata 2017 12 12 alle 16.24.17

Rispetto alle flow chart esposte sopra c'è una differenza: il loop while .. MESSAGE_PENDING ha una variabile "toPending" che se raggiunge il valore 1000 interrompe il loop con un messaggio che segnala un problema sul bus. Qui si affronta una circostanza rara ovvero quando il bus è collegato ma il dispositivo non è alimentato; in questo caso il programma rimarrebbe bloccato all'infinito nello stato PENDING.

Un'altro elemento da segnalare è che la funzione I2C1_MasterWrite(..) creata dal tool MCC è proprio quella che nella flow chart ho indicato con "read status" perché è quello che fa una volta che ha scritto il dato sul bus.

Adesso si può vedere tramite il terminale RS232 il valore restituito da i2c_detect, che se la memoria non inganna è decisamente una sorpresa (nel datasheet del sensore è riportato l'indirizzo 0x52).

Schermata 2017 12 12 alle 17.01.33

La spiegazione è semplice: si tratta di convenzioni. Bisogna ricordare che nel protocollo I2C il bit meno significativo, quello di destra, indica se il comando è scrittura (bit = 0) oppure lettura (bit = 1) quindi l'indirizzo del sensore è 0x52 se scrivo, 0x53 se leggo. La funzione creata da Microchip considera l'indirizzo del dispositivo al netto del bit di scrittura e lettura eseguendo uno slittamento a sinistra di un bit in fase di esecuzione ed inserendo opportunamente il bit a destra. Dalla tabella che segue si può vedere che se sposto a sinistra di un bit il valore 0x29 ottengo proprio 0x52.

Schermata 2017 12 12 alle 17.26.36

Il programma completo è al solito disponibile su github.

download project 

NOTA IMPORTANTE:

Con l'ultimo aggiornamento la versione del tool MCC 3.45.1 presenta una correzione del codice generato in automatico nel file i2c1.c che rende il presente tutorial non funzionante. Riporto uno spezzone del codice del file funzionante in cui si può vedere l'insolita presenza del ';' al termine dell'istruzione while:

Schermata 2017 12 19 alle 15.49.11

Sembrerebbe logico rimuovere il carattere ';' e nella nuova versione è stato fatto; però mettere a true il flag dell'IRQ nella condizione specificata dall'istruzione while, significa dichiarare che la trasmissione/ricezione è terminata, affermazione che anche non conoscendo tutto il resto del codice sembra essere un errore, ed effettivamente il programma non funziona. Occorre quindi ripristinare a mano il carattere ';'.