In this entry, we will see the SPI bus, one of the main communication methods available in Arduino. In previous entries, we already saw the serial port, and in the next entry, we will look at the I2C bus.
The SPI bus is interesting as a communication medium because a wide variety of sensors and commercial devices have an SPI interface as a means of 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 or exchange data directly with each other.
In the SPI bus, data communication between master and slave is done over two independent lines, one from the master to the slaves, and another from the slaves to the master. Therefore, the communication is Full Duplex, meaning the master can send and receive data simultaneously.
Another feature of SPI is that it is a synchronous bus. The master device provides a clock signal that 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 the master to the slave
- MISO (Master-in, slave-out) for communication from the slave to the master
- SCK (Clock) clock signal sent by the master
Additionally, an extra SS (Slave Select) line is required for each connected slave device to select the device with which communication will take place.
However, this has the disadvantage of requiring one line for each slave device. If there are many slave devices, this may not be practical, so it is possible to adopt a daisy chain connection, where each slave transmits data to the next.
On the other hand, in this configuration, the information must reach all slaves for communication to be completed, so in general, the bus’s response speed is lower.
Operation of the SPI Bus
The operation of the SPI bus is straightforward.
By default, the master keeps all SS lines in HIGH state. 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 pulse of the clock signal, typically on the rising edge, the master device sends a bit to the selected slave while receiving a bit from it.
The frame (the data sent) does not follow any specific rule, meaning we can send any arbitrary sequence of bits. This means that the connected devices need to have pre-agreed on the length and meaning of what they will send and receive.
The electronics required to implement the SPI bus are simple and cheap; even a single shift register may be sufficient. Additionally, since the clock signal is provided by the master, slaves do not even need to have their own clock.
Advantages and Disadvantages of SPI
Advantages
- High transmission speed (up to 8 MHz on Arduino) and Full Duplex
- The required devices are simple and cheap, making it integrated into many devices.
- Can send bit sequences of any size, without splitting and without interruptions.
Disadvantages
- Requires 3 wires (SCK, MOSI, and MISO) + 1 additional wire (SS) for each slave device.
- Only suitable for short distances (about 30 cm).
- There is no control mechanism, meaning we cannot know if the message has been received, let alone if it has been received correctly.
- The length of the messages sent and received must be known by both devices.
The SPI Bus in Arduino
Hardware
Arduino has hardware SPI support physically linked to certain pins. It is also possible to use any other set of pins as an SPI bus through software, but in that case, the speed will be much lower.
The pins associated with SPI vary from model to model. The following table shows the layout in some of the main models. For other models, refer to the corresponding pinout diagram.
MODEL | SS | MOSI | MISO | SCK |
---|---|---|---|---|
Uno | 10 | 11 | 12 | 13 |
Nano | 10 | 11 | 12 | 13 |
Mini Pro | 10 | 11 | 12 | 13 |
Mega | 53 | 51 | 50 | 52 |
The hardware SS pin is used when using Arduino as a slave. If using Arduino as a master, we can use any pin as SS, or several if we have multiple slaves.
Connecting the SPI bus is straightforward. The main challenge will be finding the function of each pin in the device we want to connect since not all manufacturers use the same designation for the pins that participate in the SPI bus.
To make it easier for you, the following table shows some of the common pins we will find in SPI devices with designations you might encounter depending on the manufacturer. The ones marked in red are power, in yellow are the SPI-specific pins, and in blue are other pins that frequently appear in SPI devices, although they are not part of the SPI bus.
Name | Alias | Pin (on Arduino Uno or Nano) | Description |
---|---|---|---|
VCC | +3.3 … 5 Volt | ||
GND | Ground | Ground | |
SCLK | CLK/SCK/SCLK | D13 (hardware) | Clock (SPI) |
MISO | MISO/SDO/DOUT | D12 (hardware) | Master In Slave Out (SPI) |
MOSI | MOSI/SDI/DIN | D11 (hardware) | Master Out Slave In (SPI) |
SS | SS/CS/SDA/ | D10 (hardware, only in slave) | Slave/Chip Select (SPI) |
RES | RST/RES/REST | D9 (variable, set by software) | Controller Reset |
RS | RS/DC | D8 (variable, set by software) | Mode: Command/Data |
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.
Additionally, 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 share programs between different board models.
The basic functions to operate the SPI bus are as follows:
SPI.begin(); // Starts the SPI bus
SPI.transfer(c); // Sends a byte
SPI.attachInterrupt(); // Activates the interrupt to receive data
There are also other functions to configure the SPI bus options. To change the order of the bits sent, we have the setBitOrder function:
setBitOrder (LSBFIRST); // least significant bit first
setBitOrder (MSBFIRST); // most significant bit first
To change the polarity and phase of the clock, we have the SPI.setDataMode function:
setDataMode (SPI_MODE0); // clock normally LOW, sampling on rising edge
setDataMode (SPI_MODE1); // clock normally LOW, sampling on falling edge
setDataMode (SPI_MODE2); // clock normally HIGH, sampling on rising edge
setDataMode (SPI_MODE3); // clock normally HIGH, sampling on falling edge
Finally, we can change the speed of the bus with the SPI.setClockDivider() function ranging from 2 to 128. The bus frequency will be the clock speed divided by the chosen divider.
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 have been deprecated since Arduino version 1.6.0, with the preferred function being beginTransaction, as shown in the following example.
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); // 2 MHz clock, MSB first, mode 0
However, since the data frame is specific to each device, it is most common that we do not use these functions directly and that our use of the SPI bus is done indirectly through the component library.
In upcoming entries, we will cover how to connect two Arduinos via the I2C port and introduce the I2C bus.
Download the Code
All the code from this entry is available for download on Github.