Qu'est ce que le SPI ? Comment le mettre en place sur la Raspberry Pi ?

Introduction

Le Serial Peripheral Interface (SPI) est un protocole de communication synchrone utilisé pour permettre la communication entre la Raspberry pi et des périphériques esclaves. Le SPI utilise un bus série synchrone, ce qui signifie que les données sont envoyées de manière synchrone avec une horloge commune entre le maître et les esclaves.

Le bus SPI est constitué de quatre lignes principales :

  • MOSI (Master Out Slave In) : C’est la ligne par laquelle le maître envoie des données aux esclaves.
  • MISO (Master In Slave Out) : C’est la ligne par laquelle les esclaves renvoient des données au maître.
  • SCLK (Serial Clock) : C’est la ligne d’horloge qui synchronise la transmission des données.
  • SS/CS (Slave Select/Chip Select) : C’est une ligne de sélection individuelle pour chaque esclave, permettant au maître de choisir quel esclave communiquera.

Sur l’image ci-dessus on peut voir deux carrés : bleu et vert. Le carré bleu correspond aux fils que vous aurez besoin de connecter pour discuter avec votre périphérique. Le carré vert correspond au deux chip select qui permettent de connecter deux composants différents au SPI. Vous devez connecter un composant qu’à un seul de ces deux pins. Pour rappel CEO corresponds au SPI spidev0.0 et CE1 à spidev0.1.

Vous pouvez aussi utiliser d’autre GPIO comme chip select si vous souhaitez utiliser plus de composant sur le SPI. Il faudra alors définir la pin par vous même en suivant le chronogramme indiqué dans la fiche technique du composant que vous souhaitez contrôler.

Installation

La première étape est d’installer la librairie spi-tools :

sudo apt-get install spi-tools

On va maintenant activer le spi dans le menu raspi-config :

sudo raspi-config

Voici le menu de raspi-config.  On va ensuite cliquer sur Interface Options : 

Dans Interface Options, on peut voir la ligne SPI.On va cliquer dessus :

On nous propose d’activer le SPI. On clique sur yes :

On ensuite une page nous indiquant que le SPI a bien été activé :

En tapant la ligne ls -l /dev/spi* on voit que les deux SPI on bien été activé :

Test

On va voir comment envoyer des informations et lire à travers le SPI.

Lecture du périphérique par SPI

Voici un exemple de lecture du SPI pour le composant RFID-RC522 qui est branché sur le chip select CE0. Ce programme est facilement adaptable à un autre composant :

#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" // Le périphérique SPI à utiliser

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, // Vitesse de communication SPI en 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("Erreur lors de l'ouverture du périphérique SPI");
        return 1;
    }

    // Exemple de lecture d'un registre du RC522
    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));

    // Affichage des données lues
    printf("Données lues depuis le registre FIFO du RC522 : ");
    for (int i = 0; i < sizeof(rx_buffer); i++) {
        printf("%02X ", rx_buffer[i]);
    }
    printf("\n");

    // Fermer le périphérique SPI
    close(spi_fd);

    return 0;
}

Ecriture vers un périphérique par SPI

Voic un programme qui permet d’envoyer deux nombres 125 et 34 sur le SPI sur le 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" // Le périphérique SPI à utiliser

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, // Vitesse de communication SPI en 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("Erreur lors de l'ouverture du périphérique SPI");
        return 1;
    }

    // Exemple de lecture d'un registre du RC522
    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));

    // Affichage des données lues
    printf("Données lues depuis le registre FIFO du RC522 : ");
    for (int i = 0; i < sizeof(rx_buffer); i++) {
        printf("%02X ", rx_buffer[i]);
    }
    printf("\n");

    // Fermer le périphérique SPI
    close(spi_fd);

    return 0;
}

Voici le test du programme où l’on peut voir le MOSI et la clock sur l’oscilloscope :