Home / Tutorials / PIC Tutorial / PIC16 SPI Tutorial

PIC16 SPI Tutorial

Simply put, synchronous serial protocols like SPI and I2C have a separate line for timing (the CLK line) while asynchronous protocols like UART (RS232) doesn’t. The addition of a clock line makes reception timing much more easier and thus reduces transmission errors.

It is also possible to interconnect more than two devices with synchronous protocols because there is no need for two devices to have the same data rates: one device (master) sets the data rate for everyone (slaves).

Why Use SPI

Synchronous protocols are also faster because of the type of drivers used. While asynchronous serial is sufficient for most projects, there are instances that you will need more speed like accessing memory cards or liquid crystal displays. The UART baud rate of the PIC16F877A in high speed mode is calculated according to the formula:

Where X is the contents of the SPBRG register which is up to 255 only. So let’s say that we set SPBRG = 0 and use the recommended maximum frequency of 20 MHz, then the highest baud rate possible for the said microcontroller would be

In UART, the baud rate is the same as the bit rate so we can also say that the maximum transfer rate is 1.25 Mbps. If a higher oscillator frequency is selected through overclocking (which is possible at the expense of stability), this rate would be higher.

Now let’s look at SPI, whose maximum bit rate is Fosc/4. Using the same 20 MHz oscillator, the maximum data rate would be 5 Mbps! That’s 4 times the speed of UART on the same device using the same oscillator.

Speed and multiple connections are primary reasons for using SPI over UART. A separate SPI tutorial is provided for more information. For the rest of this article, I will cover how to implement the SPI protocol in PIC microcontrollers.

Read the SPI Protocol Guide to know more about SPI

The MSSP Module

The synchronous protocols SPI and I2C are managed by the Master Synchronous Serial Port (MSSP) module. This module is not part of the PIC16F84A features so for the rest of this tutorial, we'll be using the PIC16F877A.

There are three registers associated with this module: SSPSTAT, SSPCON and SSPCON2. The use of each registers is different in SPI and I2C modes. SPI mode only uses the SSPSTAT and SSPCON registers. The I2C mode is covered in part two of this tutorial.

Here are the above mentioned registers:

SSPSTAT Register

Configuring SPI

During initialization, we’ll need to configure the SMP (if the device is the master) and CKE bits of SSPSTAT.  Then, the SSPEN bit must be set to enable the pins RC3 (SCK),  RC4 (SDI) and RC5 (SD0). We may also need to configure CKP bit of SSPCON1.

The SMP, CKE and CKP bit settings must be the same for the master and the slave. Here, we will take a sample on the middle of data output time, use idle to active transmission and make low level as idle state. This means the three bits will be cleared.

The SPI data rate is configured using the last four bits of SSPCON1:

SPI bit rate

Basically, any data to be sent via SPI must be placed inside the SSPBUF register. Conversely, any data received is inside the SSPBUF register. The BF bit of SSPSTAT will set if data is available.


Below are two ASM codes, one for a master and another for a slave device, where the master sends data to the slave:

Master Code:

Slave Code:

SPI uses a Master-Out-Slave-In (MOSI) line and a Master-In-Slave-Out (MISO) line. In PICs, they are named as SDI and SDO respectively. Of course, you also have the clock line or SCK/SCL. The slave select (SS) pin is used for multiple slave connections. When a slave's SS pin is pulled low, this means the master is "talking" to him. In our example which uses only one slave device, we connected the SS pin to ground.

This is the schematic for the codes above (note that oscillator and pullup resistors have been omitted):

SPI PIC16F877A schematic

The master device increments a variable and sends the value of each increment via the SPI bus. The slave device receives the value and displays it as PORTB values.

SPI in XC8

Here’s how the above code can be implemented in XC8:

Master Code:

Slave Code:

Custom SPI Library

We can simplify the use of SPI in XC8 using a custom library. I created "spi.h" with three functions, spiBegin(), spiWrite() and spiRead(). The spiBegin() accepts three parameters: mode, sample bit, clock edge select and clock polarity. Here are all the possible values for each parameter:



sample bit:


clock edge select:


clock polarity:


The spiWrite() and spiRead() functions are used to write and read from SSPBUF, respectively.

Here is "spi.h" which should be added to your project's Header Files folder:

Here are example codes that uses the library:

Master with SPI Library

Slave with SPI Library

Once you get a grip on how SPI works, you can now use it to communicate with SD cards or liquid crystal displays.

On the next page, we will look at how to use the I2C mode of the MSSP module.

Check Also

water level sensor

Using a Water Level Sensor with Microcontrollers

A water level sensor is a simple device that is used to measure the level …