SPI Protocol

Introduction to the SPI function

The serial peripheral interface (SPI) protocol initially was proposed by Freescale to simplify the interfacing of peripheral devices (ICs) to the microcontroller. Since its introduction, the SPI has been adopted by almost all 8-bit, 16-bit and 32-bit microcontrollers. SPI protocol is used mainly in interfacing with peripheral  devices such as shift registers, LED/LCD display drivers, real-time clock (RTC) chips, phase-locked loop (PLL) chips, serial memory components, digital temperature sensors, CAN controllers, Ethernet controllers, digital potentiometers, and A/D or D/A converter chips.

The SPI module can operate as a master or as a slave in both three-wire or four-wire modes and supports multiple masters and slaves on a single SPI bus. When a SPI module is configured as a master, it is responsible for generating the clock signal (SCK) required for synchronizing data transfer. An SPI slave can respond only to the data-transfer request. The SPI data transfer is full duplex, because the master and the slave exchange a byte with each other.

SPI Signal Pins

The SPI modules use the following signals to communicate:

1] Serial clock (SCK). The SCK signal is an output from the master device and an input to the slave device. It is used to synchronize the transfer of data between the master and the slave on the MOSI and MISO pins. This signal is generated by the master and is ignored by a slave device if it is not selected.

2] Master in, slave out (MISO). This signal is an output from the slave device (SDO slave’s pin) and an input to the master device. It is used to transfer data serially from the slave to the master. The MISO pin is placed in a high-impedance state when the SPI module is disabled and when the SPI is not selected when in slave mode. Data can be transferred most significant bit first or least significant bit first in the AVR MCUs

3] Master out, slave in (MOSI). This signal is an output from the master and an input to slave devices (SDI slave’s pin). It is used to transfer data serially from the master to the slave. Data can be transferred most significant bit first or least significant bit first in the AVR MCU

4] Slave select (SS). When you are in master mode, you can choose to make this pin either input or output. If you make it output, the SPI circuit of AVR will not control the SS pin and you can make it one or zero by software. When you make the SS pin as input, it will control the function of SPI. In this case, you should externally make SS pin high to ensure master operation. If an external device makes the SS pin low, the SPI module stops working in master mode and switches to slave mode by clearing the MSTR bit in SPCR and then sets the SPIF bit in SPSR. It is highly recommended to make the SS pin output if you do not want to be interrupted when you working in master mode.
When you are in slave mode, the SS pin is always input. The SPI module is disabled when this pin is pulled high. The SPI slave is enabled to perform data transfer when this signal is pulled low.

Clock polarity and phase in SPI device

In SPI communication, the master and slave(s) must agree on the clock polarity and phase with respect to the data. Freescale names these two options as CPOL (clock polarity) and CPHA (clock phase), respectively, and most companies like Atmel have adopted that convention. At CPOL=0 the base value of the clock is zero, while CPOL=1 the base value of the clock is one. CPHA=0 means sample on the leading (first) clock edge, while CPHA=1 means sample on the trailing (second) clock. Notice that if the base value of the clock is zero, the leading (first) clock edge, is the rising edge but if the base value of the clock is one, the leading (first) clock edge is falling edge.

AVR SPI registers

Most AVRs, including ATmega family members, support SPI protocols. In AVR three registers are associated with SPI. They are SPSR (SPI Status Register), SPCR (SPI Control Register) and SPDR (SPI Data Register). In this section we will focus on these registers.

SPSR (SPI Status Register)

Bit7-SPIF (SPI Interrupt Flag): In master mode, this bit is set in two situations: when a serial transfer is completed, or when SS pin is an input and is driven low by an external device. Setting the SPIF flag to one will cause an interrupt if  SPIE in SPCR is set and global interrupts are enabled.
Bit 6 – WCOL (Write COLlision Flag): The WCOL bit is set if you write on SPDR during a data transfer.
Bit 0 – SPI2X (Double SPI Speed): When the SPI is in master mode, setting this bit to one doubles the SPI speed

Notice that both the WCOL bit and the SPIF bit are cleared when you read the SPI Status Register and then access the SPI Data Register. Alternatively, the SPIF bit is cleared by hardware when executing the corresponding interrupt handler.

SPCR (SPI Control Register)

Bit 7 – SPIE: SPI Interrupt Enable: Setting this bit to one enables the SPI interrupt.
Bit 6 – SPE: SPI Enable: Setting this bit to one enables the SPI
Bit 5 – DORD: Data Order: This bit lets you choose to either transmit MSB and then LSB or vice versa. The LSB is transmitted first if DORD is one; otherwise, the MSB is transmitted first.
Bit 4 – MSTR: Master / Slave select: If you want to work in master mode then set this bit to one; otherwise, slave mode is selected. Notice that if the SS pin is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF will become set.
Bit 3 – CPOL: Clock polarity: This bit set the base value of clock when it is idle. At CPOL=0 the base value of the clock is zero while at CPOL=1 the base value of the clock is one.
Bit 2 – CPHA: Clock Phase: CPHA=0 means sample on the leading (first) clock edge, while CPHA=1 means sample on the trailing (second) clock.
Bits 1, 0 – SPR1, SPR0: SPI Clock Rate Select 1 and 0: These two bits control the SCK rate of the device in master mode.

In table below you see how SPI2X, SPR1 and SPR0 are combined to make different clock frequencies for master. Aw you see, by setting SPI2X to one, the SCK frequency is doubled.

SPDR (The SPI Data Register)

The SPI Data Register is a read/write register. To write into SPI shift register, data must be written to SPDR. To read from the SPI shift register, you should read from SPDR. Writing to the SPDR register initiates data transmission. Notice that you cannot write to SPDR before the last byte is transmitted completely, otherwise, a collision will happen. You should also read the received data before another byte of data is received completely. Otherwise, the first byte will be lost.

The SPI Operation

In the SPI format, data transfer is initiated by an SPI master. The SPI master starts a data transfer by writing to the data register SPDR. Data are transmitted and received simultaneously. The serial clock, SCK, synchronizes shifting and sampling of the information on the serial data lines MOSI and MISO, The serial select signal (SS) enables the SPI slave to perform data transfer. Because the AVR SPI module can be configured as a slave or master, we have to be specific about how data is sent and received in these two modes.

To send data to an SPI slave, the SPI master writes the byte to be transferred into SPDR register. This action triggers eight clock pulse (from the SCK pin) to shift out a byte to the slave. To receive a byte from the SPI slave, the SPI master also writes a byte into the SPDR register. However, the contents of the byte are unimportant. The write action triggers eight clock pulses from the SCK pin to shift in a byte from the slave.

To send a byte to the SPI master, the SPI slave writes the byte to be transferred into the SPDR register and waits for the master SPI to generate SCK clock pulses to shift out data. The SPI can find out whether the data byte has been shifted out by checking the SPIF flag or waiting for the interrupt caused by the setting of the SPIF flag. The setting of the SPIF flag indicates that the byte has been sent out. To receive a byte from the SPI master, the SPI slave can keep checking the SPIF flag until it is set or wait for the interrupt caused by the setting of the SPIF flag. The setting of the SPIF flag indicates that a new byte has been shifted in.

SPI Circuit Connection

If there is only one SPI master to connect to an SPI slave, the user needs only to wire the same signals together, as shown in Figure below

When there are multiple slaves to be driven by a single master SPI, then there are two connection methods. The user can choose the connection method shown in Figure below if he or she has the need to select one SPI slave to exchange data without affecting other SPI slaves.

However, there are many applications in which the user needs to exchange data with all the SPI slave at a time. The connection method shown in Figure below can satisfy this requirement, and it saves quite a few I/O pins.

The above two circuits differ  in the following ways:  1] The MISO pin of each slave is wired to the MOSI pin of the slave device to its right. The MOSI pins of the master and slave 0 are wired together. 2] The MISO pin of the SPI master is wired to the same pin as the last slave device. 3] The SS inputs of slaves are tied to ground to enable all slaves.

Thus the shift registers of the SPI master and slaves became a ring. The data of the slave k is shifted to the master SPI, the data of the master is shifted to slave 0, the data of slave 0 is shifted to slave 1, and so on. In this configuration, a minimal number of pins control a large number of peripheral devices. However, the master does not have the freedom to select an arbitrary slave device for data transfer without going through other slave devices.

This type of configuration is often used to extend the capability of the SPI slave. For example, suppose there is an SPI-compatible, 7-segment display driver/decoder that can drive only four digits. By using the circuit connection shown in the last Figure, up 4xk can be displayed when k driver/decoders are cascaded.

Example: Configure the SPI module of the Mexa device to operate with the following setting, assuming that fosc=16Mz a) 4-Mhz data shift rate b) Interrupt disabled c) SPI enabled d) SCK idle low and data sampled on the rising edge e) Data shift most significant bit first.

Solution: Because 16Mhz/4Mhz=4, we need to set SPI2X:SPR1:SPR0 to 000. We have to load 0x50 and 0x00 into the SPCR and SPSR register, respectively.

The C language version of the function is as follows:

void initSPI(void)
{
   uint8_t tmp;
   SPCR    =    0x50;  //enable master mode, set baud rate to 4 Mhz
                       //select data mode 0, disable interrupt
   tmp     =    SPSR;  //clear all the SPI flags
   tmp     =    SPDR;  //clear all the SPI flags
   SPSR    =    0;     // clear the SPI2X bit
}  

Writing Common SPI Data Transfer Functions

Sending and reading data using the SPI interface is very straighforward. The following two functions are used most often:
1] putcSPI. This function sends out a character through the SPI module.
2] getcSPI. This function reads a character from the SPI module

Multiple-byte transfer can be achieved by calling one of these two functions repeatedly.

The following function sends a character from the master SPI to the slave SPI.

void putcSPI_master(uint8_t cx)
{
     uint8_t tmp;
     SPDR  =  cx;
     while(!(SPSR & (1<<SPIF)));  // wait for data to be shift out
     tmp  =  SPDR;     // clear SPIF flag
}

The following function lets the master SPI read a byte from the slave SPI

uint8_t getcSPI_master(void)
{
     SPDR  =   0;    // trigger clocks from SCK pin
     while(!(SPSR & (1<<SPIF)));  //wait for a byte to shift in
     return SPDR;
}

The following function sends a character from the slave SPI to the master SPI.

void putcSPI_slave(uint8_t cx)
{
     uint8_t   tmp;
     SPDR  =   cx;
     while(!(SPSR & (1<<SPIF)));  // wait for data byte to be shifted out
     tmp  =  SPDR;
}

The following function lets the SPI slave read a byte from the master SPI

uint8_t getcSPI_slave(void)
{
     while(!(SPSR & (1<<SPIF)));  // wait for data byte to be shifted in
     return  SPDR;
}

Example: Write an AVR program to initialize the SPI for master, mode 0, with CLCK frequency = Fosc/16, and then trnsmit ‘G’ via SPI repeatedly. The received data should be displayed on Port D.

Solution:

#include  <avr/io.h>
#define  MOSI 3
#define SCK 5
#define SS 2
int main(void)
{
   DDRB = (1<<MOSI)|(1<<SCK)|(1<<SS); //MOSI and SCK are output
   DDRD = 0xFF                        //Port D is output
   SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); //enable SPI as master
   while(1){                            //do for ever
         PORTB &= ~(1<<SS);             //enable slave device
         SPDR  =  'G';                  //start transmission
         while(!(SPSR & (1<<SPIF)));    //wait transfer finish
         PORTD  =  SPDR;           //move received data to PORTD
         PORTB |= (1<<SS);         //disable slave device
    }
    return 0;
}

Example: Write an AVR program to initialize the SPI for slave, mode 0, with CLCK frequency=fck/16 and then transmit ‘G’ via SPI repeatelly. The received data should be displayed on Port D.

Solution

#include <avr/io.h>
#define MISO 4
int main(void)
{
   DDRD  =  0xFF;                    //Port D is output
   DDRB  =  (1<<MISO);               //MISO is output
   SPCR  =  (1<<SPE);               //enable SPI as slave
   while(1){
       SPDR  =  'G';
       while(!(SPSR & (1<<SPIF)));  //wait for transfer finish
       PORTD  =  SPDR;              //move received data to PORTD
    }
    return 0;
}