In questa pagina viene mostrato come collegare una periferica SPI ad un PIC18, senza utilizzare hardware dedicato. L'uso del modulo hardware per la gestione del bus SPI (MSSP) è descritto in questa pagina.
In questa pagina non è spiegato cosa è e come si usa il bus SPI. Se serve, qui trovate qualche informazione.
In genere tutti i PIC18 posseggono almeno un modulo hardware adatto a trasmettere e ricevere segnali SPI. In questi esempi si utilizza invece esclusivamente software. I vantaggi di questa scelta:
Lo svantaggio di una soluzione solo software è evidente:
Questa tecnica è spesso indicata col nome di bit banging.
Il circuito utilizzato per le prove è stato realizzato su breadboard utilizzando un PIC18F25K20, un ADC a 12 bit MAX 146 ed un DAC a 12 bit MCP4822. E' il circuito della fotografia di apertura.
Il circuito non presenta particolari criticità. Le uniche avvertenze, utili soprattutto con le frequenza del bus SPI più elevate, sono quelle di inserire alcuni condensatori per disaccoppiare l'alimentazione e, ovviamente, realizzare collegamenti ordinati e non troppo lunghi.
Lo schema mostra i collegamenti tra il PIC e le due periferiche:
Il primo esempio mostra come utilizzare pin di I/O generici per scrivere una coppia di byte verso un Convertitore Digitale Analogico MCP4822; si tratta di un doppio DAC a 12 bit, capace di generare tensioni tra 0 e 2.048 V. Per i dettagli occorre consultare il foglio tecnico. A fondo pagina il codice.
Innanzitutto occorre configurare i pin di I/O utilizzati dal bus, in questo caso tutti in uscita:
TRISCbits.RC5 = 0; // Set MOSI pin as output
TRISCbits.RC3 = 0; // Set clock pin as output
TRISBbits.RB0 = 0; // Set SS pin as output
È comodo usare una serie di #define per rendere il codice più leggibile e favorire la manutenzione :
#define MOSI LATCbits.LATC5 // Pin 16 MOSI RC5/SDO
#define SS0 LATBbits.LATB0 // Pin 21 DAC_SS RB0
#define CLK LATCbits.LATC3 // Pin 14 SCK RC3/SCK
SPI prevede quattro diverse modalità (mode 0, mode 1, mode 2 e mode 3, spesso indicati con nomi diversi), in funzione del valore iniziale e del fronte attivo del clock. Per evitare problemi è consigliabile non fidarsi troppo dei nomi, passare alll'esame diretto dei diagrammi temporali pubblicati dai produttori delle periferiche e scegliere di conseguenza la modalità da utilizzare.
Prima di iniziale la trasmissione, occorre impostare lo stato iniziale dei segnali:
SS0 = 1; // Disable Slave Select
CLK = 0; // Clear clock
Per scrivere un frame SPI occorre:
Il codice corrispondente:
SS0 = 0;
// Enable Slave Select
SPI_Master_write_byte(DATA1); // Write a byte to slave
SPI_Master_write_byte(DATA2); // Write a byte to slave
SS0 = 1;
// Disable Slave Select
I due byte trasmessi continuamente dal codice di esempio sono 0x78 e 0x5A (in binario: 0111 1000 e 0101 1010 ed impostano la tensione di uscita del DAC a 1,069 V. Per i dettagli potete consultare il foglio tecnico. Il primo esercizio è proposto per rendere meno "rigido" il comportamento del codice.
Tra la scrittura di un byte e del successivo oppure dopo l'attivazione di SS o prima della disattivazione, può essere inserito un ritardo, anche se in genere non richiesto. Nell'esempio è presente al solo scopo di rendere più semplice la lettura del diagramma temporale.
Per trasmettere un singolo byte occorre, nel caso del "modo" richiesto da MCP4822 occorre:
Il codice corrispondente:
for (mask = 0x80; mask != 0; mask >>= 1) { // Write 8
bit, starting form MSB
if (dato & mask) // If masked bit is 1
MOSI = 1; // Write 1
else
MOSI = 0; // Write 0
CLK = 1; // Clock active on rising edge
CLK = 0; // Clock to idle
}
In genere tra scrittura del bit e attivazione del clock e tra attivazione del clock e sua disattivazione vengono inseriti ritardi che determinano la frequenza del clock di trasmissione.
La figura seguente mostra i segnali trasmessi e la relativa decodifica:
Dall'alto:
Uno dei punti di forza di SPI è la possibilità di trasmettere e ricevere contemporaneamente (full-duplex), utilizzando i due pin MOSI e MISO. Il codice presentato in questo paragrafo permette di leggere la tensione in ingresso ad uno dei canale dell'ADC MAX 146. A fondo pagina il codice di esempio completo, sempre in versione esclusivamente software.
L'inizializzazione è simile a quanto già mostrato, semplicemente si aggiunge il pin MISO, configurato come ingresso:
TRISCbits.RC5 = 0; // Set MOSI pin as output
TRISCbits.RC3 = 0; // Set clock pin as output
TRISBbits.RB1 = 0; // Set SS1 pin as output
TRISCbits.RC4 = 1; // Set MISO pin as input
Occorre ricordare che, nel caso in cui il pin MISO sia anche un ingresso analogica (ANx), è necessario configurare il corrispondente registro ANSELx per attivare il buffer di ingresso digitale.
La lettura di un byte è contemporanea alla scrittura (full duplex). Nell'esempio, specifico per il MAX 146, viene mostrato la lettura della tensione presente sul canale 0:
Il codice:
SS1 = 0;
// Enable Slave Select
dummy = SPI_Master_write_read_byte(MAX146_CONTROL_BYTE); // Write a byte,
Read back first byte
Byte1 = SPI_Master_write_read_byte(0);
// Write 0 to slave - Read a byte
Byte2 = SPI_Master_write_read_byte(0);
// Write 0 to slave - Read a byte
SS1 = 1;
// Disable Slave Select
A volte, ma non è in genere richiesto, viene inserito un ritardo tra la lettura/scrittura di un byte e del successivo.
Il primo byte trasmesso (Control Byte) nell'esempio è fisso e vale 0xCF; il suo significato è specifico per il MAX 146 ed è descritto nella Table 1 dei fogli tecnici. Esso permette di:
La lettura/scrittura di un singolo byte è simile all'esempio precedente, con l'aggiunta della lettura di un singolo bit alla volta, sfruttando la stessa "maschera" usata per la scrittura ed un OR bitwise:
for (mask = 0x80; mask != 0; mask >>= 1) { // Write
and read 8 bit, starting form MSB
if (dato & mask)
MOSI = 1; // Write single 1
else
MOSI = 0; // Write single 0
__delay_us(10); // Delay half periode
CLK = 1; // Clock active on rising edge
if (MISO)
data = data | mask;
__delay_us(10); // Delay half periode
CLK = 0; // Clock to idle
}
Per rendere il codice "meno rigido", è proposto il secondo esercizio.
Il diagramma temporale:
Dall'alto:
Si noti che la frequenza di clock SPI utilizzata è troppo bassa per il corretto funzionamento del MAX146 che quindi effettua una conversione non accurata: dovrebbe essere almeno di 100 kHz mentre nell'esempio è di poco più di 15 kHz.
Data di creazione di questa pagina: febbraio 2015
Ultima modifica: 27 dicembre 2015
PIC18 in C - Versione 0.991 - luglio 2019
Copyright 2014-2019, Vincenzo Villa (https://www.vincenzov.net)
PIC18 in C di Vincenzo Villa è distribuito con Licenza Creative Commons Attribuzione 4.0 Internazionale