Periferica I2C

Attenzione! Questa pagina contiene materiale obsoleto Attenzione!

Questo breve tutorial mostra come realizzare con un PIC16F690 una periferica slave I2C "intelligente". Il codice è interamente in C e pronto per la compilazione così come è. Si tratta di un codice intenzionalmente minimale che ha il solo scopo di introdurre il neofita alla programmazione del modulo SSP del PIC16F690.

Non verrà spiegato cosa è e come si usa il bus I2C. Per una breve spiegazione potete far riferimento alla nota applicativa AN734 disponibile sul sito http://www.microchip.com, nella quale viene illustrato lo standard in stretta relazione al modulo SSP. Se ne consiglia vivamente la lettura!

E' ovviamente necessario disporre di un master I2C: la scelta è stata quella di utilizzare un PC attraverso una scheda di conversione USB-I2C basata su FT2232H; qui trovate il codice. La spiegazione di un codice con le stesse funzionalità la trovate anche su collegare un bus I2C ad un bus USB, a cui si rimanda. Ovviamente se disponete di un altro dispositivo capace di funzionare come master I2C sentitevi liberi di utilizzarlo...

La demo board del PICkit2 collegata ad una scheda i2c-usb

L'hardware

L'hardware esterno necessario si riduce alle due resistenze di pull-up da 10 kΩ, come richiesto dallo standard IIC.

Il collegamento tra master (in questo caso la scheda a destra nella fotografia, a sua volta collegata ad un PC attraverso il connettore USB) e slave (il PIC sulla scheda demo a sinistra) è realizzato attraverso quattro fili:

Due osservazioni:

Una nota importante: alcuni PIC16 hanno un bug al modulo hardware SSP: verificate che quello che state utilizzando sia almeno pari alla revisione 6, leggendo questa informazione direttamente nella finestra di output del MPLAB. Se precedente occorre modificare leggermente il codice per il corretto funzionamento.

Revisione 6 del PIC16F690

Il software

Come già scritto, potete qui trovare il codice completo. In breve:

Scorrendo il codice si possono fare alcune osservazioni (altre note sono presenti nel codice sorgente):

#define I2C_ADDR 0x51

L'indirizzo IIC del PIC è, in questo esempio, 0x51 e deve essere noto al master. Nell'esempio qui riportato verrà utilizzato solo l'indirizzamento a 7 bit, come per la maggior parte delle periferiche I2C.

int I2C_counter;

Questa variabile inizialmente vale zero. Successivamente, ad ogni lettura da parte del master di un blocco di dati, verrà incrementata di 1.

TRISC = 0x00; // Set PORTC as Output
ANSEL = 0;    // Set as Digital I/O
ANSELH = 0;   // Set as Digital I/O
PORTC = 0x00; // All LEDs off
TRISB = 0xFF; // Set PORTB as Inputs

La PORTC è configurata come uscita: sono collegati i quattro LED.

La PORTB è configurata come ingresso. In realtà è necessario configurare in questo modo solo i due pin connessi a SDA e SLC del bus IIC. In realtà il segnale SDA è bidirezionale ma questo aspetto è gestito direttamente dal''hardware dopo la prima configurazione.

// SSPCON = 0;           // Work around for PIC16F690 pre-A6 revision
  //Configure SSP as I2C slave
  //xxxx 0110 = I2C Slave mode, 7 bit address, no interrupts on start and stop
  //xxx1 xxxx = CKP = 1, Enable clock
  //xx1x xxxx = SSPEN, Enable SSP
SSPCON = 0b00110110;
SSPADD = I2C_ADDR << 1;  // Set I2C address
SSPSTAT = 0x00;          // All bits must be cleared
SSPIE = 1;               // Enable the interrupt
SSPIF = 0;
PEIE = 1;
GIE = 1;

Il codice precedente configura il modulo SSP (si consiglia di leggere la parte introduttiva della già citata nota applicativa AN734) ed abilita gli interrupt. Si noti la prima riga, necessaria solo con i PIC più vecchi (riga non verificata per mancanza di vecchi PIC16...).

A questo punto il programma entra in un loop infinito, in attesa di una richiesta del master, integralmente gestita dalla seguente funzione isr(), invocata direttamente dall'hardware attraverso un interrupt.

void interrupt isr(void) // Interrupt Service Routine
{if (SSPIF)              // I2C interrupt

Viene come prima cosa verificato se l'interrupt è stato generato dal modulo SSP. Nell'esempio è un'operazione "inutile" in quanto questo è l'unico modulo attivo ma nel mondo reale molti potrebbero essere i dispositivi attivi.

Da notare che l'interrupt viene generato solo quando il master indirizza il dispositivo con indirizzo pari (in questo esempio) a 0x51: non è quindi necessario verificare se i messaggi sono effettivamente diretti al PIC, ha già provveduto l'hardware a questo controllo.

A questo punto deve essere riconosciuta la richiesta del master e occorre provvedere ad eseguire il comando richiesto. Come descritto nella AN734 sono possibili cinque situazioni, da gestire individualmente:

if(!DA && !RW && BF && START) // An address from master: write to slave
{SSPBUF = SSPBUF;             // Empty buffer and clear BF bit
 if(SSPOV)                    // Check overflow
 {//...                       // Overflow handling, if required
  SSPOV = 0;
 }
}

Se il master invia l'indirizzo del PIC con l'intenzione di inviare successivamente un byte di dati l'unica cosa che occorre fare è segnalare la disponibilità a ricevere il successivo byte, svuotando il buffer di ricezione.

Il codice mostrato non riporta nessuna azione nel caso di overflow, cioè un errore nella ricezione causato per esempio dalla mancata elaborazione del comando precedente.

if(DA && !RW && BF) // Data from master to slave
{PORTC = SSPBUF;    // Output data to PORTC LEDs and clear BF bit
}

Se il PIC riceve un dato, questo è semplicemente utilizzato per accendere i quattro LED.

if(!DA && RW && !BF)     // An address from master: read from slave
{SSPBUF = ++I2C_counter; // Data will be write to master
 CKP = 1;                // Release clock (data ready to be send to master)
}

Se il master richiede un primo byte al PIC, questo risponde con un numero, incrementato ogni volta di una unità: la prima volta risponde 1, la seconda 2, le terza 3... Da notare che dopo che il PIC ha predisposto la risposta è necessario rilasciare il clock affinché il master possa effettivamente iniziare la lettura di un byte.

if(DA && RW && !BF && START) // Read a byte from slave (next byte)
{SSPBUF = 255-I2C_counter;   // Data will be write to master
 CKP = 1;                    // Release clock (data ready to be send to master)
}

Se il master richiede in una solo operazione di lettura un altro byte, il PIC risponde con il complemento del byte precedentemente mandato.

if(DA && !RW && !BF) // NAK from master (complete transmission has occurred)
{ }

Se il master decide di interrompere le comunicazioni il PIC non fa, in questo esempio, nulla.

SSPIF = 0; // Re-enable interrupt

Prima di terminare la funzione isr() occorre abilitare nuovamente le interrupt.

Collaudo

Per verificare il funzionamento del codice occorre disporre di un dispositivo capace di funzionare come master I2C: esso dovrà inviare un byte che il PIC visualizzerà sui LED e dovrà richiedere al PIC una coppia di byte. Un esempio capace di funzionare con un modulo di conversione USB-I2C è riportato più avanti.

La finestra mostra l'output generato: il numero 5 (in binario 0101) passato sulla linea di comando è visualizzato sui LED, come nella fotografia di apertura.

Il PIC risponde con un numero (10 in questo caso: si tratta quindi della decima volta che il programma interroga il PIC) e con un secondo numero che, sommato al precedente, dà 255.

Verifica del funzionamento (lato PC)

Scarica il codice C per il microcontrollere PIC16 (slave)

Scarica il codice VisualC per il PC (master)

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


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima