What is SPI? How do I set it up on the Raspberry Pi?

Introduction

Serial Peripheral Interface (SPI) is a synchronous communication protocol used to enable communication between the raspberry pi and slave peripherals. SPI uses a synchronous serial bus, which means that data is sent synchronously with a common clock between master and slaves.

The SPI bus consists of four main lines:

  • MOSI (Master Out Slave In): This is the line on which the master sends data to the slaves.
  • MISO (Master In Slave Out): This is the line through which the slaves send data back to the master.
  • SCLK (Serial Clock): This is the clock line that synchronizes data transmission.
  • SS/CS (Slave Select/Chip Select): This is an individual selection line for each slave, allowing the master to choose which slave will communicate.

The image above shows two squares: blue and green. The blue square corresponds to the wires you’ll need to connect to talk to your peripheral. The green square corresponds to the two chip select pins that allow you to connect two different components to the SPI. You must connect a component to only one of these two pins. As a reminder, CEO corresponds to SPI spidev0.0 and CE1 to spidev0.1.

You can also use other GPIOs like chip select if you want to use more components on the SPI. In this case, you’ll need to define the pin yourself, following the timeline indicated in the datasheet for the component you wish to control.

Installation

The first step is to install the spi-tools library:

sudo apt-get install spi-tools

We will now activate the spi in the raspi-config menu:

sudo raspi-config

This is the raspi-config menu. Next, click on Interface Options :

In Interface Options, we can see the SPI line, which we will click on:

We are asked to activate the SPI. We click on yes:

We are then presented with a page indicating that the SPI has been activated:

By typing the line ls -l /dev/spi* we can see that both SPIs have been activated:

Test

We’ll see how to send information and read through the SPI.

SPI device reading

Here’s an example of SPI reading for the RFID-RC522 component connected to the CE0 select chip. This program is easily adaptable to other components:

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define SPI_DEVICE "/dev/spidev0.0" // The SPI device to use

void transfer(int fd, uint8_t *tx, uint8_t *rx, size_t len) {
    struct spi_ioc_transfer transfer = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = len,
        .delay_usecs = 0,
        .speed_hz = 1000000, // SPI communication speed in Hz
        .bits_per_word = 8,
    };

    ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
}

int main() {
    int spi_fd;

    // Ouvrir le périphérique SPI
    spi_fd = open(SPI_DEVICE, O_RDWR);
    if (spi_fd < 0) {
        perror("Error opening SPI device");
        return 1;
    }

    
    uint8_t tx_buffer[2] = {0x80 | 0x09, 0x00}; // Adresse du registre à lire (0x80 pour lecture)
    uint8_t rx_buffer[4] = {0};

    transfer(spi_fd, tx_buffer, rx_buffer, sizeof(tx_buffer));

    // Displaying read data
    printf("Data read from the RC522 FIFO register: ");
    for (int i = 0; i < sizeof(rx_buffer); i++) {
        printf("%02X ", rx_buffer[i]);
    }
    printf("\n");

    // Close SPI device
    close(spi_fd);

    return 0;
}

Write to a peripheral via SPI

Here’s a program to send two numbers 125 and 34 over the SPI to chip select CE0 :

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define SPI_DEVICE "/dev/spidev0.0" // The SPI device to use

void transfer(int fd, uint8_t *tx, uint8_t *rx, size_t len) {
    struct spi_ioc_transfer transfer = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = len,
        .delay_usecs = 0,
        .speed_hz = 1000000, // SPI communication speed in Hz
        .bits_per_word = 8,
    };

    ioctl(fd, SPI_IOC_MESSAGE(1), &transfer);
}

int main() {
    int spi_fd;

    // Open SPI device
    spi_fd = open(SPI_DEVICE, O_RDWR);
    if (spi_fd < 0) {
        perror("Error opening SPI device");
        return 1;
    }

    // Example of reading an RC522 register
    uint8_t tx_buffer[2] = {0x80 | 0x09, 0x00}; // Address of register to be read (0x80 for reading)
    uint8_t rx_buffer[4] = {0};

    transfer(spi_fd, tx_buffer, rx_buffer, sizeof(tx_buffer));

    // Display of read data
    printf("Data read from the RC522 FIFO register: ");
    for (int i = 0; i < sizeof(rx_buffer); i++) {
        printf("%02X ", rx_buffer[i]);
    }
    printf("\n");

    // Close SPI device
    close(spi_fd);

    return 0;
}

Here’s the program test showing the MOSI and clock on the oscilloscope: