Tutorial → PIC18 in C →
SPI → Slave SPI (MSSP)

In questa pagina viene mostrato come utilizzare un PIC18 come slave SPI
sfruttando il modulo MSSP Master Synchronous Serial Port.
In questa pagina non verrà spiegato cosa è e come si usa il bus SPI. Se
serve,
qui
trovate qualche informazione.
Il modulo MSSP
La struttura interna del modulo MSSP configurato come Slave SPI è piuttosto
semplice:

- il registro a scorrimento, in giallo. Il processore ha accesso
diretto solo al registro SSPBUF, mentre
SSPSR è il registro a
scorrimento vero e proprio che permette lo scorrimento dei bit da e
verso SDI e SDO
- Il clock, in verde. Sempre proveniente dall'esterno. L'unica
opzione è la scelta è il fronte attivo
- SS, in rosso, che permette di abilitare o meno il funzionamento del
registro a scorrimento. Da notare che lo Slave Select, a differenza di quelli del Master, è
un pin predefinito ed è
gestito direttamente dall'hardware del modulo MSSP.
L'hardware
L'hardware utilizzato negli esempi, piuttosto semplice, è costituito da due PIC18 collegati tra
di loro, uno funzionante come Master (a sinistra nello schema), l'altro come
Slave (a destra)

I tre segnali MOSI (SDO del Master), Clock e Slave Select (RBO) sono pilotati dal
Master; MISO (SDO dello Slave) è pilotato dallo Slave, ma solo quando è attivo lo
Slave Select.; altrimenti è configurato ad alta impedenza (3 state) ed
eventualmente pilotato da altre periferiche SPI. Si noti che SDI e SCO sono
"incrociati" tra Master e Slave. Da tenere inoltre presente che
l'alimentazione (o perlomeno la massa) devono essere comuni tra i due
circuiti.
Nella fotografia di apertura, in primo piano è visibile lo Slave. Sullo
sfondo il Master ed un secondo Slave.
Il collegamento è "a 4 fili": SS, Clock, MISO e MOSI, rispettivamente
i fili viola,
verde, blu e giallo. Da tenere presente che l'alimentazione (o perlomeno la
massa) deve essere comune ai due circuiti: nella fotografia ovviamente sono
i due fili rosso e nero (nello schema non sono disegnati).
Il programma eseguito dal PIC Master nei primi due esempi è lo stesso non fa altro che incrementare
il valore di un
byte e trasmetterlo; inoltre verifica se il byte ricevuto è di una unità
minore di quello trasmesso e, in caso negativo, accende un LED per segnalare
un errore nel protocollo. Il codice è oggetto del terzo esercizio della pagina
PIC18 come master SPI.
Se si dispone di due PICKIT/ICD e di due PC non ci sono problemi particolari
nel realizzare il circuito; è comunque possibile prima programmare il Master, renderlo
autonomo e collegare quindi l'ICD al PIC18 che eseguirà il codice delle
Slave. In alternativa è possibile utilizzare come Master SPI un
qualunque altro dispositivo programmabile, per esempio un
Rapsberry Pi.
SPI slave da programma
Il primo programma di esempio utilizza SPI
in configurazione Slave senza far uso di interrupt. Questo programma non fa altro che ricevere un
singolo byte e ritrasmetterlo al Master, ovviamente durante lo scambio di
dati successivo. Il codice è suddiviso in due parti:
- La configurazione del modulo MSSP, in particolare nella scelta della
modalità Slave con l'uso in hardware dello Slave Select
SSPCON1bits.SSPEN = 1; //
Synchronous Serial Port Enable bit
SSPCON1bits.SSPM = 0b0100; // 0100 = SPI Slave mode, clock = SCK pin, SS
pin control enabled
SSPCON1bits.CKP = 0; // Idle state
for clock is a low level
SSPSTATbits.CKE = 1; // Output data
changes on clock transition from active to idle
SSPSTATbits.SMP = 0; // SMP must be
cleared when SPI is used in Slave mode.
- L'attesa della trasmissione (decisa dal Master), la lettura del byte
ricevuto e la preparazione del nuovo dato da inviare al Master:
while (!SSPSTATbits.BF); // Data Recived?
DataRecived = SSPBUF; // Read date recived
and clear SSPSTATbits.BF
SSPBUF = DataRecived; // Write back data
recived (next time...)
Due osservazioni:
- il ciclo while() che attende la ricezione
del byte è bloccante e potrebbe non terminare mai nel caso in cui il
Master non trasmette nulla
- La lettura di SSPBUF è sempre necessaria, anche nel caso in cui non
si è interessati al suo valore: infatti questa operazione permette di
azzerare il flag che segnala la ricezione di un byte

Il diagramma temporale non presenta sorprese; dall'alto:
- Lo Slave Select come sempre attivo basso,
rosso. E' comandato dal Master
- Il clock, in verde. é comandato dal Master e, se rimane nelle
specifiche del PIC18 può assumere qualunque frequenza. In questo caso è
stato impostato a 1 MHz
- Il dato trasmesso dal Master (MOSI), in blu, iIn questo caso vale
0xC4
- Il dato trasmesso dalla Slave (MISO), in ocra, in questo caso vale
0xC3. Tale valore è semplicemente l'echo del precedente valore trasmesso
dal Master.
Da notare che il segnale MISO, quando lo Slave Select non è
attivo, si trova ad un valore intermedio tra 0 e 1 logico (tri-state)
SPI slave con interrupt
Si è già accennato alla presenza nel programma appena descritto di un
codice "bloccante", cioè di un loop di attesa del termine
di una operazione di una periferica, nel caso specifico la ricezione di
un byte da parte del modulo MSSP:
while (!SSPSTATbits.BF);
Il problema è critico in quanto, semplicemente, questa operazione
potrebbe non avvenire mai. Una soluzione parziale è l'inserimento di un
contatore di "time-out". Una soluzione migliore è l'uso delle interrupt,
generate alla ricezione di un nuovo byte.
Il codice è simile al precedente e si comporta allo
stesso modo:
- La configurazione di MSSP è identica a quanto già descritto, se non
nella necessità di attivare le interruzioni alla ricezione di un byte:
PIE1bits.SSPIE = 1; // Enable
interrupt from MSSP
IPR1bits.SSPIP = 1; // MSSP Interrupt Priority set to
high
- La ISR contiene lo stesso codice prima presente nel
main()
if (PIR1bits.SSPIF) { // Interrupt from SPI ?
data = SSPBUF; // Get data from
SPI and store it
SSPBUF = data; // Send back data
(next time)
PIR1bits.SSPIF = 0; // Clear interrupt flag
}

Il diagramma temporale mostrato è relativo allo scambio di due byte; dall'alto:
- Lo Slave Select come sempre attivo basso,
rosso. E' comandato dal Master
- Il clock, in verde. é comandato dal Master ed in questo caso ha
frequenza 4 MHz. La trasmissione di un byte dura quindi, rigorosamente, 2 µs.
- (il MOSI non è mostrato)
- Il pin 2 di PORTB, in blu; il suo valore logico è
continuamente modificato durante l'esecuzione del main(); permette, quando
smette di commutare, di identificare i periodi di esecuzione
della ISR e del codice ad essa associato. La durata della ISR è, circa,
4 µs, simile al tempo per cui viene eseguito il main().
- Il dato trasmesso dalla Slave (MISO), in ocra, in questo caso vale
0xB9 e 0xBA. Tali valori sono semplicemente la ripetizione dei valori
precedentemente trasmessi dal Master.
Osservazioni
- L'esempio mostrato ha un clock piuttosto elevato, ma, essendo
gestito in hardware, non si presentano problemi particolari.
- Nel codice dello
Slave non è presente alcun riferimento alla frequenza di clock, gestita
interamente dal Master
- Circa il 50% del tempo CPU è usato per la trasmissione SPI. Nel caso
di trasmissione interrupt-driven, quella mostrata può essere considerata
la massima velocità operativa (1 byte ogni 9 µs, poco meno di 900 k/s in
full-duplex)
Un altro esempio
Questo esempio utilizza le interruzioni per scambiare con un dispositivo
Master un frame di lunghezza predefinita, 10 byte. Il corrispondente
programma Master è disponibile in
questa pagina, oggetto
del primo esercizio.
Il codice utilizza due array della stessa lunghezza "utile" (e della
stessa lunghezza prevista per il Master). Nell'esempio l'array atteso
proveniente dal Master è "MASTER 5 #" dove 5 è un
numero progressivo di una cifra. Il frame trasmesso dallo Slave contiene la
stringa "ACK 4 ####", dove 4 è il numero
progressivo inviato dal Master con il frame precedente.
Nel codice di esempio è stata usata la tecnica, ampiamente utilizzata,
di:
- predisporre un array contenente il frame da trasmettere.
- eseguire codice generico, indipendente dal funzionamento dei
SPI. La trasmissione del frame avverrà "in background" mano a mano che
il Master trasmetterà il frame.
- il codice deve, ad intervalli sufficientemente piccoli, verificare
la ricezione del frame e, nel caso, predisporre il nuovo frame da
trasmettere
Osservazioni
- l'array di ricezione è un byte più grande di quanto necessario:
questo serve per ospitare l'ultimo byte ricevuto nel caso in cui tale
array non sia stato "svuotato" prima della ricezione di un nuovo frame.
Ovviamente ciò comporta la perdita di dati (ed anche la perdita di
sincronismo con il master, aspetto non gestito nell'esempio)
- analogamente non è gestita la perdita di byte nel caso in cui un
nuovo byte arriva prima della lettura del registro
SSPBUF. Questo aspetto è monitorato dal flag
SSPOV del registro SSPCON1
- il primo byte trasmesso dallo Slave deve essere predisposto alla
trasmissione in
SSPBUF prima dell'attivazione dello Slave Select
da parte del Master
Il diagramma temporale:

- In rosso, lo Slave Select, generato dal Master
- In verde, il clock, generato dal Master. Nel caso specifico è stata
utilizzata la frequenza di 1 MHz e quindi la trasmissione di un byte è
rigorosamente pari a 8 µs
- In blu, i 10 byte del MOSI, trasmessi dal Master, con relativa
decodifica ASCII
- In blu, i 10 byte del MISO, trasmessi dallo Slave, con relativa
decodifica ASCII
Data di creazione di questa pagina: luglio 2015
Ultima modifica: 27 dicembre 2015