Master SPI - Software

Collegamento SPI su breadboard

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.

L'hardware usato

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.

Schema elettrico 

Lo schema mostra i collegamenti tra il PIC e le due periferiche:

SPI software, solo scrittura

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:

SPI write: 0x785A

Dall'alto:

SPI software, scrittura e lettura

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:

SPI full duplex: TX: CF 00 00 - RX: 00 36 D0

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.

Codice sorgente

Esercizi

  1. Scrivere la funzione void Set_DAC(int mVoltage, int channel) il cui scopo è impostare la tensione in uscita ad uno dei canali del DAC MCP4288, con tensione in millivolt. In alternativa, la tensione potrebbe essere espressa come double... con qualche effetto negativo (quali?)
  2. Scrivere la funzione int Get_mV_ADC (int channel) il cui scopo è leggere la tensione applicata ad uno dei canali del MAX146


Data di creazione di questa pagina: febbraio 2015
Ultima modifica: 27 dicembre 2015


Licenza Creative Commons Attribuzione 4.0 Internazionale


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima