Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

12
voti

Impariamo con il Pierin - Il bus I2C

Indice

Premesse

Lo scopo di questo articolo è quello di riuscire a configurare e utilizzare correttamente la periferica MSSP del PIERIN PIC18 alfine di stabilire una comunicazione con il sensore di temperatura TCN75A. Il programma scritto per questa applicazione è stato sviluppato partendo dal codice presentato nell’articolo precedente (quello sulla USART): infatti il codice che vedrete tra poco, legge la temperatura dal sensore comunicando via I2C, esegue una conversione del dato e poi invia il risultato via UART al PC.

Cos’è il MSSP

Il MSSP (Master Synchronous Serial Port) è una periferica di comunicazione seriale con altri dispositivi quali EEPROMs, sensori, convertitori A/D o D/A , ecc. Questa periferica può operare in due modi:

-SPI(Serial Peripheral Interface)

-I2C(Inter-Integrated Circuit)

Quest’ultima modalità utilizza due cavi per la comunicazione: SDA (per I dati) e SCK (per il clock).Il clock viene generato esclusivamente dal master che controlla l’intera rete a cui si possono collegare uno o più slave. Ogni slave è dotato di un proprio indirizzo univoco che li contraddistingue nella rete e possono comunicare solo quando vengono “interrogati” dal master, inviando un byte alla volta. Una particolarità di questo bus è che sono necessarie due resistenze di pull-up per entrambi i cavi: questo perché le uscite dei vari dispositivi collegati sono open-drain, ovvero possono assumere due valori: 0V o impedenza infinita. Il perché di questa caratteristica lo si può dedurre dal protocollo di comunicazione.

Protocollo di comunicazione

protocollo

protocollo


La comunicazione inizia sempre con un segnale di start: il master porta la linea SDA al livello logico basso. Segue l’indirizzo del dispositivo con cui si vuole comunicare. Esso è composto da 7bit più un bit che indica il tipo di trasmissione (scrittura o lettura). Inviato l’indirizzo il dispositivo interrogato risponde con un segnale di acknowledge, ovvero portando a livello basso la linea dati. Ecco il perché delle uscite open-drain: in questo modo entrambi i dispositivi possono inviare dati sulla stessa linea senza causare problemi. In seguito, in base al tipo di trasmissione, il master invia o riceve byte: ogni volta che invia un byte, lo slave deve rispondere con un acknowledge e lo stesso deve fare il master quando riceve dati dagli slave.

Registri di configurazione

SPPxSTAT

SPPxSTAT


Bit Funzione
SMP Attiva lo slew rate. Va configurato solo quando la velocità non è standard (100kHz o 1MHz)
CKE Attiva gli input peril SMBus
D/A Bit di sola lettura che indica se il dato trasmesso è un indirizzo o un dato (solo modalità slave)
P Bit di sola lettura che indica se è stato inviato un segnale di stop
S Bit di sola lettura che indica se è stato inviato un segnale di start
R/W Bit di sola lettura che indica se la trasmissione è in progresso (modalità master)
UA Serve nella modalità slave con indirizzo a 10bit
BF Bit di sola lettura che indica se il registro SSPxBUF è pieno o vuoto


SPPxCON1

SPPxCON1


Bit Funzione
WCOL Serve per le operazioni di rilevamento delle collisioni tra le trasmissioni
SSPOV Indica se è stato ricevuto un byte mentre il registro SPPxBUF era pieno
SSPEN Attiva le porte SDA e SCK
CKP In modalità master non serve
SSPM<3:0> Sono 4bit che configurano la modalità del modulo I2C


SPPxCON2

SPPxCON2


Bit Funzione
GCEN Utilizzato solo nella modalità slave
ACKSTAT Indica se è stato ricevuto o meno l’acknolege dallo slave
ACKDT Imposta il tipo di acknolege da inviare
ACKEN Invia l’acknolege(in base a quanto impostato nel bit ACKDT)
RCEN Attiva la modalità ricezione del master
PEN Invia un segnale di stop
SEN Invia un segnale di start



Oltre a questi registri di configurazione, c’è anche il SPPxBUF che contiene il dato da inviare o ricevuto e il registro SPPxADD che contiene il valore che configura la frequenza di trasmissione secondo questa formula: clock = FOSC/(4 *(SSPxADD + 1))

Il Programma

Il programma sviluppato si compone di 6 file: header.h, main.c, configuration_bits.c, funzioni.c, i2c.h e i2c.c. Gli ultimi due file sono quelli che ci interessano maggiormente perché in essi è contenuto il codice che permette la comunicazione con il sensore.

void I2C_init(intfreq);

void I2C_start(void);

void I2C_write(unsigned char dato);

unsigned charI2C_read(unsigned char ack);

void I2C_rep_start(void);

void I2C_stop(void);

Queste sono le varie funzioni che permettono di far funzionare il modulo I2C. Analizziamo la prima:


//Inizializza il modulo MSSP in modalità I2C master. La frequenza deve essere espressa in KHz

void I2C_init(intfreq)

{

    unsigned long tmp;

    //Pin SDA e SCKdel modulo I2C

    TRISB4=1;

    TRISB5=1;

 
    //Controlla se viene utilizzata una frequenza standard o meno, per attivare lo slew rate control

    if (freq==100 || freq==1000) SSP1STAT=0b10000000;

    else SSP1STAT=0;

    SSP1CON1=0b00101000;

    SSP1CON2=0;

    //Fout=Fosc/(4*(SPP1ADD+1)) ==>SPP1ADD=(Fosc/(4*Fout))-1

    tmp=_XTAL_FREQ/1000;

    tmp/=4;

    tmp/=freq;

    SSP1ADD=tmp-1;

}


Questa funzione inizializza il modulo MSSP in modalità master utilizzando le configurazioni standard. Passando il valore dellafrequenza in KHz, il codice calcola il valore da porre nel registro SPP1ADD secondo la formula indicata dal datasheet.


//Invio segnale di start

void I2C_start(void)

{

   SSP1CON2bits.SEN=1; //Invia il segnale di start

   while(!SSP1IF);     //Attende cheil segnale sia stato inviato

    SSP1IF=0;

}

//Ripetizione del segnale di start

void I2C_rep_start(void)

{

   SSP1CON2bits.RSEN=1;    //Ripetoil segnale di start

   while(!SSP1IF);     //Attende che il segnale sia stato inviato

    SSP1IF=0;

}

//Invio segnale di stop

void I2C_stop(void)

{

   SSP1CON2bits.PEN=1; //Segnale di stop

   while(!SSP1IF);     //Attende che il dato sia stato inviato

    SSP1IF=0;

}

Con queste due funzioni è possibile mandare i segnali distart, re-start e stop . Come è possibile vedere a pagina 336-337 del datasheet, alla fine di ogni operazione (invio del segnale di stop/start, invio o ricezione di un byte) viene settato il pin SSP1IF del registro PIR1 che se abilitato genera un interrupt. Questo bit deve essere ogni volta resettato.


//Invio un byte

void I2C_write(unsigned char dato)

{

   SSP1BUF=dato;       //Invio deldato via I2C

   while(!SSP1IF);     //Attende che il dato sia stato inviato

    SSP1IF=0;

}

Per inviare un dato basta semplicemente scrivere nel registro SSP1BUF e attende l’invio

Più complesso è ricevere un byte:

//Lettura di un byte ricevuto

unsigned char I2C_read(unsigned char ack)

{

    unsigned dato;

   SSP1CON2bits.RCEN=1;            //Modalità ricezione

   while(!SSP1IF);                 //Attende che il dato sia stato ricevuto

    SSP1IF=0;

    dato=SSP1BUF;

    if (ack)SSP1CON2bits.ACKDT=1;   //Setto che deve inviare un segnale di acknowledge

    elseSSP1CON2bits.ACKDT=0;       //Setto che deve inviare un segnale di not-acknowledge

   SSP1CON2bits.ACKEN=1;           //Invio il segnale

   while(!SSP1IF);                //Attende che il segnale sia stato inviato

    SSP1IF=0;

    return dato;

}

Innanzitutto bisogna abilitare la ricezione agendo sul bit RCEN. Arrivato il byte, lo si legge e si invia il segnale di acknoelege: questo segnale può essere di due tipi: acknowledge (stato logico 0) o not-acknowledge(stato logico 1).

Inviando il primo, si indica allo slave di inviare un altro byte, con il secondo invece indica che la ricezione è terminata.

Leggiamo la temperatura

Ora analizziamo le modalità di comunicazione per poter leggere la temperatura dal sensore in questione. Innanzitutto dobbiamo scoprire l’indirizzo del nostro slave. Queste sensore, come molti altri dispositivi I2C,hanno dei pin che in base allo stato logico in cui si trovano, modificano l’indirizzo dello slave. Questo risulta importante nel caso in cui si vogliano collegare più sensori uguali nella stessa rete. Il TCN75 ha 3 pin A0, A1 e A2 che permettono di modificare gli ultimi 3 bit dell’indirizzo a 7 bit dello slave:questo significa che possiamo collegare fino a 8 sensori sulla stessa linea.Nel nostro caso, mettiamo tutti i pin a massa in modo da avere il seguente indirizzo: 0b1001000.

Ci creiamo nel file header.h due bei define dove salviamo gli indirizzi di lettura e scrittura.

Registri TCN75

Registri TCN75

Il sensore è molto semplice da usare ed è infatti dotato di solo 4 registri: noi ne useremo solo i primi due.

Il primo registro è quello di configurazione. Noi configureremo solo la risoluzione, portandola da 9bit di default a 12bit.

Il secondo registro invece è a 16bit e serve per la lettura della temperatura. Nel primo byte è contenuta la parte intera, mentre nel secondo la parte decimale espressa come da schema.


Ora dobbiamo capire come inviare e ricevere le informazioni che ci servono.


Schema scrittura

Schema scrittura


Da questo schema possiamo dedurre le modalità di comunicazione: come descritto prima, bisogna inviare prima l’indirizzo di scrittura, e poi il dato che ci interessa. Nel nostro caso, inviamo l’indirizzo del registro di configurazione che si trova alla posizione 0x01. Poi inviamo le configurazioni da porre in quel registro. Questa configurazione deve essere fatto una volta sola all'inizio del programma. Ecco il codice:


//Configurazione iniziale: Risoluzione a 12 bit (massima), il resto è di default

    I2C_start();

    I2C_write(tcn_W);

    I2C_write(1);

    I2C_write(0b01100000);

    I2C_stop();


Come vedete utilizzando le varie funzioni, il codice è molto semplice ed è immediata la comprensione. Più complesso è leggere il dato della temperatura.


Schema lettura

Schema lettura


Infatti bisogna come per la scrittura inviare l’indirizzo di scrittura seguito dal registro che vogliamo leggere, nel nostro caso è 0x00. Poi però bisogna ripetere il segnale di start: ecco che entra in scena il comando rep_start(); che pone a 1 il bit RSEN del registro SSP1CON2. Fatto ciò,si rinvia l’indirizzo dello slave, questa volta in modalità lettura. Si prepara il master alla ricezione e si attende che arrivi il primo byte. Per confermarel’arrivo del primo byte si deve dare un segnale di acknoledge, che indica allo slave che il byte è stato ricevuto e che può inviare il secondo. Arrivato anche il secondo, il master deve inviare un segnale di not-acknoledge che indica la fine della ricezione. Il tutto si conclude con il segnale di stop. Ecco il codice:

I2C_start();

        I2C_write(tcn_W);

        I2C_write(0);

        I2C_rep_start();

        I2C_write(tcn_R);

       delay_ms(300);          //Attende che la lettura venga effettuata (il datasheet indica 240mS pag.22)

        temp1=I2C_read(0);

        temp2=I2C_read(1);

        I2C_stop();


Ora però non possiamo trasmettere i dati ricevuto così come sono, ma dobbiamo convertirli.


Registro temperatura

Registro temperatura


Per poter inviare il dato della temperatura via seriale,dobbiamo ottenere prima il numero decimale della temperatura e poi convertirlo in ASCII. Quindi dobbiamo passare da una variabile numerica a una stringa.Creiamo quindi una stringa di 5 caratteri (2 cifre delle unità, la virgola, la cifra decimale e il carattere terminatore) e poi otteniamo le decine e le unità della variabile che contiene la parte intera della temperatura. A questi due valori (che assumono valori da 0 a 9) sommiamo 48 che in ASCII corrisponde allo zero, e avendo poi tutti gli altri numeri in serie (49=”1”, 50=”2” ecc),otteniamo le cifre da 0 a 9. Nella terza posizione mettiamo il numero 44 che corrisponde alla virgola. Per i decimali la questione è diversa. Innanzitutto shiftiamo di 4 posizioni verso destra il nostro dato, in modo da avere un numero che va da 0 a 15. Quindi possiamo dire che i decimali sono espressi in sedicesimi. Quindi per ottenere il nostro decimale ci basta dividere per 16 e otterremmo un numero che va da 0 a 0,9375 (la risoluzione è di 0,125). Ma per non complicarci la vita con le variabili a virgola mobile, moltiplichiamo per 10 prima della divisione per 16, in modo da avere sempre numeri interi (da 0 a 9). Il valore ottenuto lo poniamo nella quarta posizione (sommando sempre 48) e concludiamo l’ultimo carattere con il valore zero (il terminatore). Dopo di che invio la stringa ottenuta al modulo USART.

Ecco il codice:


//Converto e invio della temperatura letta

voidstampaTemp(unsigned char t1, unsigned char t2)

{

    unsigned chars_temp[5];

   s_temp[0]=(t1/10)+48;      //Ottengo la cifra delle decine

   s_temp[1]=(t1%10)+48;      //Ottengo la cifra delle unità

   s_temp[2]=44;              //Corrisponde alla virgola

    

    t2=t2>>4;                   //Shifto di 4 posizioni il dato dei decimali

    t2*=10;                     //Moltiplico per dieci per non dover usare i decimali

   t2=t2>>4;                  //E divido per 16 (i decimali della temp sono espressi in 16simi digrado)

   s_temp[3]=t2+48;           //Sommo 48 che corrisponde allo zero in ascii

 

 

   s_temp[4]=0;               //Carattere di terminazione

    //Invio la tempertura letta

   stampaTesto("Temperatura misurata: ");

   stampaTesto(s_temp);

    stampaTesto("\n");

}

Collegamenti

Schema

Schema


Collegare il PicKit2, il PIERIN e il sensore come in figura. Per stabilire la comunicazione, dovete avviare il pickit2, nel menù Tool selezionare UART tool. In seguito settate il baud rate a 19200 espuntate la casella VDD. Premete su connect e collegate al PIERIN PIC18 il cavo USB per dargli alimentazione. Dovrebbe comparire una scritta iniziale e poi ad intervalli di 1sec la rilevazione della temperatura come in figura.


UART Tool

UART Tool

Conclusioni

Qui potete scaricare il programma completo.


Se avete domande o ho commesso qualche errore, non esitate a commentare l’articolo. Buona sperimentazione!

4

Commenti e note

Inserisci un commento

di ,

Grazie mille mi è stato molto utile con poche parole mi ha dato una visione molto più chiara di altri tutorial al riguardo. Per esempio l'ack e il notAck non ne capivo la differenza e l'utilità. Se ho qualche problema spero che posso disturbati per chiederti qualche consiglio. Ciao a presto.

Rispondi

di ,

Articolo ben scritto! Complimenti :)

Rispondi

di ,

Ottimo articolo! Speravo proprio in un articolo che parlasse del bus I2C. E' una periferica utilissima che permette di fare grandi cose. Chapeau! :)

Rispondi

di ,

Complimenti! Bello anche quest'articolo! :)

Rispondi

Inserisci un commento

Per inserire commenti è necessario iscriversi ad ElectroYou. Se sei già iscritto, effettua il login.