In this post we are going to look at the SPI bus, one of the main communication methods available in Arduino. In previous posts we have already seen the serial port, and in the next post we will see the I2C bus.
The SPI bus is of interest as a means of communication because a wide variety of commercial sensors and devices have an SPI interface for communication.
The SPI bus
The SPI bus (Serial Peripheral Interface) was developed by Motorola in 1980. Its advantages over other systems have made it a de facto standard in the world of electronics and automation.
The SPI bus has a master-slave architecture. The master device can initiate communication with one or more slave devices, and send or receive data from them. Slave devices cannot initiate communication, nor can they exchange data with each other directly.
In the SPI bus, data communication between masters and slaves is done on two independent lines, one from the master to the slaves, and the other from the slaves to the master. Therefore, communication is Full Duplex, meaning the master can send and receive data simultaneously.
Another characteristic of SPI is that it is a synchronous bus. The master device provides a clock signal, which keeps all devices synchronized. This reduces system complexity compared to asynchronous systems.
Therefore, the SPI bus requires a minimum of 3 lines.
- MOSI (Master-out, slave-in) for communication from master to slave.
- MISO (Master-in, slave-out) for communication from slave to master.
- SCK (Clock) clock signal sent by the master.
In addition, an additional SS (Slave Select) line is required for each slave device connected, to select the device with which to communicate.
However, this has the disadvantage of requiring a line for each slave device. If there are many slave devices, this may not be practical, so it is possible to adopt a cascading connection, where each slave transmits data to the next.
On the other hand, in this configuration, the information must reach all the slaves for the communication to be completed so, in general, the bus response speed is lower.
Operation of the SPI bus
The operation of the SPI bus is simple.
By default, the master keeps all SS lines HIGH. When the master wants to establish communication with a slave, it sets the corresponding SS line to LOW, indicating to the slave that it should initiate communication.
On each clock signal pulse, usually on the rising edge, the master device sends a bit to the slave and at the same time receives a bit from the selected slave.
The frame (the data sent) does not follow any rule, meaning we can send any arbitrary sequence of bits. This means that the connected devices need to have agreed on the length and meaning of the data they are going to send and receive.
The electronics required to implement the SPI bus are simple and inexpensive, and even a single shift register may be sufficient. Also, since the clock signal is provided by the master, the slaves don’t even need to have their own clock.
Advantages and disadvantages of SPI
- High transmission speed (up to 8 Mhz on Arduino) and Full Duplex
- The necessary devices are simple and inexpensive, making it integrated in many devices.
- Can send bit sequences of any size, without dividing and without interruptions.
- 3 cables required (SCK, MOSI, and MISO) + 1 additional cable (SS) for each slave device.
- Only suitable for short distances (about 30cm)
- No control mechanism is available, meaning we cannot know if the message has been received, and even less if it has been received correctly.
- The length of the sent and received messages has to be known by both devices.
The SPI bus in Arduino
Arduino has hardware SPI support physically linked to certain pins. It is also possible to use any other group of pins as an SPI bus through software, but in that case the speed will be much slower.
The pins associated with SPI vary from one model to another. The following table shows the layout in some of the main models. For other models, refer to the corresponding pinout diagram.
The hardware SS pin is used when using Arduino as a slave. If using Arduino as a master, any pin can be used as SS, or several pins if there are multiple slaves available.
Connecting the SPI bus is simple. The biggest difficulty will be finding the function of each pin on the device we want to connect, as not all manufacturers use the same designation for the pins involved in the SPI bus.
To make it easier for you, the following table shows some of the usual pins that we will find in SPI devices with designations you may find according to the manufacturer. Those marked in red are power, in yellow the SPI bus pins, and in blue other pins that frequently appear in SPI devices, although they are not part of the SPI bus.
|Pin (on Arduino Uno or Nano)
|+3.3 … 5 Volt
|Master In Slave Out (SPI)
|Master Out Slave In (SPI)
|D10 (hardware, only in slave)
|Slave/Chip Select (SPI)
|D9 (variable, set by software)
|D8 (variable, set by software)
To use the SPI port in Arduino, the Standard IDE provides the “SPI.h” library which contains the necessary functions to control the integrated SPI hardware.
Likewise, the Arduino programming environment defines the constants SCK, MOSI, MISO, and SS for the SPI pins. Using these “aliases” in our code makes it easier to interchange programs between board models.
The basic functions to make the SPI bus work are as follows:
SPI.begin(); // Start the SPI bus
SPI.transfer(c); // Send a byte
SPI.attachInterrupt(); // Activate the interrupt to receive data
There are also other functions to configure the options of the SPI bus. To change the order of the sent bits, we have the setBitOrder function:
setBitOrder (LSBFIRST); // least significant bit first
setBitOrder (MSBFIRST); // more significant bit first
To change the clock polarity and phase, we have the SPI.setDataMode function:
setDataMode (SPI_MODE0); // clock normally LOW, sample on rising edge
setDataMode (SPI_MODE1); // clock normally LOW, sample on falling edge
setDataMode (SPI_MODE2); // clock normally HIGH, sample on rising edge
setDataMode (SPI_MODE3); // clock normally HIGH, sample on falling edge
Finally, we can change the bus speed with the SPI.setClockDivider() function, divisors from 2 to 128. The bus frequency will be the clock speed divided by the chosen divisor.
setClockDivider(SPI_CLOCK_DIV2); //8 MHz (considering a 16 Mhz model)
setClockDivider(SPI_CLOCK_DIV4); //4 MHz
setClockDivider(SPI_CLOCK_DIV8); //2 MHz
setClockDivider(SPI_CLOCK_DIV16); //1 MHz
setClockDivider(SPI_CLOCK_DIV32); //500 KHz
setClockDivider(SPI_CLOCK_DIV64); //250 KHz
setClockDivider(SPI_CLOCK_DIV128); //125 KHz
However, these functions are obsolete from Arduino version 1.6.0, preferring the beginTransaction function, as shown in the following example.
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); // 2 MHz clock, MSB first, mode 0
However, as the data frame is specific to each device, it is more common that we do not use these functions directly, and that our use of the SPI bus is done indirectly through the library of the component.
In upcoming posts we will cover how to connect two Arduinos using the I2C port, and we will introduce the I2C bus.