Watch the Video Tutorial: Introduction to SPI

SPI stands for Serial Peripheral Interface. The SPI protocol was initially developed and proposed by Motorola for use in microprocessor and microcontroller based interface applications. 
In a system that uses the SPI bus, one device acts as the master and other devices act as slaves. The master initiates a communication and also provides clock pulses to the slave devices. 
SPI is implemented in the PIC microcontrollers by a hardware module called the Synchronous Serial Port or the Master Synchronous Serial Port (MSSP). This module is built into many different PICmicro devices. It allows serial communication between two or more devices at a high speed and is reasonably easy to implement The MSSP can operate either in I²C or SPI mode.
SPI is usually called a 4-wire full duplex synchronous serial communication, the functions of the 4 wires are as follow:
SCLK (Serial Clock): This is the serial clock signal. It is generated by the master device and controls when data is sent and when it is read. The SCLK pin of master is connected to SCLK pin of slave.
MOSI (Master Out Slave In) or SDO (Serial Data Output) This pin carries data out of a device (from master to slave).
MISO (Master In slave Out) or SDI (Serial Data In) This pin carries data into a device (from slave to master).
SS (Slave Select) or CS (Chip Select): This pin allows a master device to indicates to a slave that the master  wishes to start an SPI data exchange between that slave device and itself. The signal is most often active low, so a low on this line will indicate the SPI is active and communication can take place, while a high will signal inactivity.
This signal must be used when more than one slave exists in a system, but can be optional when only one slave exists in the circuit but it always a good practice to use it. Depending on the number of devices in the bus, we can have more than one chip Select lines, one per device in the bus. Figure 1 hows the microcontroller connected to 3 SPI slaves (3 chip select lines).

Figure 1 : SPI Bus

SPI is used for connecting peripherals to each other and to microcontrollers.
There are many devices that support the SPI protocol and can easily communicate with a microcontroller via SPI: A/D converters, D/A converters, SD Cards, Liquide Crystal Displays (LCD), Real Time Clocks like the popular DS1306, serial display drivers like MAX7219 , Serial EEPROM like 25LC256 , etc.  
Depending on the number of slave devices connected, the master will have one dedicated pin for SS line for each slave. Figure 1 below shows an illustration of one master (microcontroller) controlling three slave devices.

Advantages of SPI:

  • Full duplex communication.
  • It’s faster than asynchronous serial and I2C allowing large quantities of data to be transferred quickly. SPI can operate at extremely high speeds but generally dictated by the slowest device on the bus. Common bus speeds are in the 1-100Mbits per second range
  • The hardware protocol is simple to implement, the receiver hardware can be a simple shift register. Transceivers are not needed, slaves use the master’s clock and don’t need precision oscillators
  • It supports multiple slaves
  • Complete protocol flexibility for the bits transferred, this is not limited to 8-bit words and arbitrary choice of message size, content, and purpose
  • Lower power requirements than I²C due to less circuitry and no need for pull-ups resistors

Disadvantages of SPI:

  • It requires more signal lines due to the fact that each slave must have its own CS line, this will require more wires than other serial communications especially when many slaves are involved.
  • The master must control all communications (slaves can’t talk directly to each other)
  • SPI is designed for fairly short range i.e. on the same PCB as the Master or via a relatively short cable compared to RS-232, RS-485, or CAN-bus

SPI Configuration with MPLAB Code Configurator

The MPLAB® Code Configurator (MCC) is a user friendly Graphical User Interface (GUI) plug-in tool for MPLAB® X IDE which generates easy to understand C code that is inserted into an MPLAB® X project, based on the settings peripherals configurations and selections made in the Graphical User Interface (GUI).  The advantage of  MCC , it can generate codes not only for PIC18F but for a wide range of PICs including PIC16F and PIC24 series.

To learn more, read the article: MPLAB® Code Configurator

Let us create a simple project as shown on figure 2 below, it consists of a PIC18F26K20 Master connected with SPI bus to two PIC18F26K20 Slaves. Two switches SW1 and SW2 are connected to PORTB0 and PORTB1 of the master. If SW1 is closed, the master sends a ‘1’ to slave 1 and a ‘0’ to slave 2. The same if SW2 is closed, the master sends a ‘1’ to slave 2 and a ‘0’ to slave 1. If both switches are closed a ‘1’ is sent to both slaves and if both switches are open a ‘0’ is sent to both slaves.  If a slave receives a ‘1’ it switches ON the three LEDs 1 after the other at an interval of 1 second and whenever a slave receives a ‘0’ it switches OFF all LEDs.

Figure 2: PIC18F26K20 Master connected with SPI bus to two PIC18F26K20 Slaves

Below is the configuration of SPI Master. The An SPI clock of 125KHz is selected. The Clock Edge: Active to Idle. PORTC0 is used for Chip Select of Slave 1(CS1)  and PORTC1 for Chip Select of Slave 2(CS2).

Watch the Video Tutorial: Configuration with MPLAB Code Configurator

Figure 3: Master SPI Configuration

Below is the configuration of SPI Slaves. Clock Edge: Active to Idle. The Red LED is connected to PORTB0, the Yellow to PORTB1 and the Green to PORTB2. Slave Select pin is enabled.

Figure 4: Slaves SPI Configuration

MPLAB Code Configurator generates the functions that you can use to access the SPI Peripheral.

  • SPI_Initialize(): This routine initializes the SPI. This routine must be called before any other MSSP routine is called.
  • SPI_Exchange8bit(): This routine exchanges a data byte over SPI bus. This is a blocking routine. SPI is a data exchange mechanism. There’s no difference between reading and writing bytes between the devices. So the same SPI_Exchange8bit() function can be used to send or receive data. As one device sends a byte of data, so the other transmits one. After sending a single byte of data from an SPI buffer another (possibly different) byte may appear in it’s place, this is the byte that has been received. If the incoming data is not read, then the data will be lost and the SPI module may become disabled as a result. Always read the data after a transfer has taken place, even if the data has no use in your application. So a Slave to read a byte let’s say from the Master, we have to send it a byte too. It’s normal to send either all zeros (0x00) or all ones (0xFF)  for “don’t care” bytes – so if you’re just reading data from the other device, and it’s not important what data you send, it’s common to either send 0x00 or 0xFF. Data is always “exchanged” between devices. No device can just be a “transmitter” or just a “receiver” in SPI. However, each device has two data lines, one for input and one for output.
  • SPI_Exchange8bitBuffer(): This routine exchanges buffer of data (of size one byte) over SPI bus.
  • SPI_IsBufferFull(): This routine gets the SPI buffer full status
  • SPI_HasWriteCollisionOccured(): This routine gets the status of write collision.
  • SPI_ClearWriteCollisionStatus(): This routine clears the status of write collision.

Below is the Master Main.c code:

Below is the Slaves Main.c code:

SPI Functions with PIC18F Peripheral Library 

MPLAB is phasing out the PIC18F Peripheral Library which is no longer included in XC8 compiler from XC8 v1.35. You have to download and install them separately into your compiler and they are now called Legacy Peripheral Libraries. It’s now advisable to use MPLAB® Code Configurator 

Programming an SPI Bus communication is not difficult, most of the compilers provide some library routines. 
From the PIC18F Peripheral Library Help Document found inside your compiler installation directory in: ..Program Files (x86) Microchip xc8 v1.34 docs MPLAB_XC8_Peripheral_Libraries.pdf (assuming you installed your compiler in the Program Files (x86) directory. v1.34 is the version of your compiler, it might be different if you are using a different compiler).
Search for the PIC you are going to use, click on: “CLICK HERE for the Peripheral Library Support Details for this Device” and For a PIC 18F2620 family for example, the SPI functions are from page 1196 in the pdf )

Notes: 
1. For devices with multiple SPI peripherals, you will have to specify which SPI module you are going to use:
Example: 

2. For PIC Microcontrollers which don’t have an SPI module or if any input/output either than the default hardware pins are required, the Software SPI functions can be used. Below is a quick descriptions of functions and macros. 

OpenSPI
: This function initializes the SPI module for SPI bus communications. It takes the following parameters for the PIC18F2620 family: sync_mode, bus_mode and smp_phase.
synch_mode (parameters to configure the SSPxCON1 register): This first argument configure the SCK clock 
rate:     SPI_FOSC_4:          Master clock rate is Fosc/4 

SPI_FOSC_16:        Master clock rate is Fosc/16
SPI_FOSC_64:        Master clock rate is Fosc/64
SPI_FOSC_TMR2:   Master clock rate is TMR2 output/2
SLV_SSON (used if device is a slave): SPI Slave mode, /SS pin control enabled
SLV_SSOFF (used in slave mode only): SPI Slave mode, /SS pin control disabled
Example: If the required data rate of SPI bus communication has to be at least 1 Mbps, assume that
the microcontroller oscillator clock rate is 16 MHz, set the synch-mode: SPI_FOSC_16 (16/16) = 1 Mbps.
bus_mode: This second parameter specifies the clock edge:
MODE_00:    Clock is idle low, transmit on rising edge
MODE_01:    Clock is idle low, transmit on falling edge
MODE_10:    Clock is idle high, transmit on falling edge
MODE_11:    Clock is idle high, transmit on rising edge
smp_phase: This is the 
input data sampling time: 

SMPEND:    Input data sample at the end of data out
SMPMID:    Input data sample at middle of data out
Example Code for master: 
OpenSPI(SPI_FOSC_16, MODE_00, SMPEND); 
Example Code for slave:    
OpenSPI(SLV_SSON, MODE_00, SMPEND); 

WriteSPI: This function writes a single byte to the SPI bus and it is the same as the putcSPI.
Example code: 
WriteSPI(‘x’);   //Send letter “c” to SPI bus.
ReadSPI: This function reads a single byte from the SPI bus and it is the same as getcSPI.
Example code: 
char data;
data = ReadSPI();

putsSPI: This function writes a string to the SPI bus.
Example code: 
unsigned char data[] = “Hello World”;
putsSPI(test);

getsSPI: This function reads a string from the SPI bus. The number of characters to read must be specified in the function argument.
Example code: 
getsSPI(data, 10); 
DataRdySPI: This function determines whether the SPI buffer contains data. 0 is returned if there is no data in the SSPBUF register, and 1 is returned if new data is available in the SSPBUF register.
Example code: 
While (DataRdySPI()); 
This function disables the SPI module.
Example code: 
closeSPI(); 

You can download the full project files (MPLAB XC8 source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).

Download Mplab Project with Code Configurator: SPI MPLAB Project

Download Proteus Schematic: SPI_Proteus