⚡ Communication SPI — PIC16F877A

Bus SPI 4 fils, modes 0 à 3, module MSSP et interfaçage de périphériques haute vitesse

Qu'est-ce que le SPI ?

Le SPI (Serial Peripheral Interface) est un bus de communication série synchrone et full-duplex développé par Motorola. Contrairement à l'I2C, le SPI utilise 4 fils et n'a pas d'adressage : chaque esclave a sa propre ligne de sélection (CS/SS).

Le SPI est nettement plus rapide que l'I2C (jusqu'à plusieurs MHz), ce qui le rend idéal pour les transferts de données volumineux (cartes SD, écrans TFT, ADC externes rapides).

Les 4 fils du SPI

SignalDirectionPIC16F877ARôle
SCKMaître → EsclaveRC3 (Pin 18)Horloge série
SDO (MOSI)Maître → EsclaveRC5 (Pin 24)Données sortantes du maître
SDI (MISO)Esclave → MaîtreRC4 (Pin 23)Données entrantes vers le maître
CS / SSMaître → EsclaveN'importe quel GPIOChip Select : LOW = esclave sélectionné

🔄 Les 4 modes SPI (CPOL / CPHA)

Le SPI a 4 modes définis par deux paramètres : la polarité de l'horloge (CPOL) et la phase des données (CPHA). Le mode doit correspondre entre le maître et l'esclave.

ModeCPOLCPHADescriptionPIC (CKP, CKE)
000Repos bas, échantillonnage front montantCKP=0, CKE=1
101Repos bas, échantillonnage front descendantCKP=0, CKE=0
210Repos haut, échantillonnage front descendantCKP=1, CKE=1
311Repos haut, échantillonnage front montantCKP=1, CKE=0

Le mode 0 (CPOL=0, CPHA=0) est le plus courant et est utilisé par la majorité des périphériques SPI (cartes SD, MCP3008, écrans TFT).

💻 Code : bibliothèque SPI Master

#include <xc.h>
#define _XTAL_FREQ 4000000

#define CS_PIN   PORTAbits.RA5  // Chip Select sur RA5
#define CS_TRIS  TRISAbits.TRISA5

/* ---- Initialisation SPI Master, Mode 0, Fosc/4 ---- */
void spi_init(void) {
    // Broches SPI
    TRISCbits.TRISC3 = 0;   // SCK en sortie
    TRISCbits.TRISC5 = 0;   // SDO (MOSI) en sortie
    TRISCbits.TRISC4 = 1;   // SDI (MISO) en entrée
    CS_TRIS = 0;             // CS en sortie
    CS_PIN = 1;              // CS inactif (HIGH)

    // Mode 0 : CKP=0 (repos bas), CKE=1 (donnée sur front montant)
    SSPSTAT = 0x40;  // SMP=0, CKE=1
    SSPCON = 0x20;   // SSPEN=1, CKP=0, Fosc/4 (SSPM=0000)
}

/* ---- Envoyer/Recevoir un octet (full-duplex) ---- */
unsigned char spi_transfer(unsigned char data) {
    SSPBUF = data;                          // Écrire dans le buffer = lance le transfert
    while (!SSPSTATbits.BF);                 // Attendre fin de transfert
    return SSPBUF;                          // Lire la donnée reçue en même temps
}

/* ---- Écrire un octet (ignorer la réponse) ---- */
void spi_write(unsigned char data) {
    spi_transfer(data);
}

/* ---- Lire un octet (envoyer un dummy 0xFF) ---- */
unsigned char spi_read(void) {
    return spi_transfer(0xFF);
}

📊 Exemple : Lire un ADC externe MCP3008 (10 bits, 8 canaux)

Le MCP3008 est un convertisseur ADC 10 bits SPI très populaire. Il offre 8 canaux analogiques et coûte moins de 2 €. Idéal pour étendre les capacités ADC du PIC.

uint16_t mcp3008_read(uint8_t channel) {
    uint8_t b0, b1, b2;

    CS_PIN = 0;                               // Sélectionner le MCP3008

    spi_transfer(0x01);                       // Start bit
    b1 = spi_transfer((0x80 | (channel << 4))); // Single-ended + channel
    b2 = spi_transfer(0x00);                   // Dummy pour lire les bits restants

    CS_PIN = 1;                               // Désélectionner

    return ((b1 & 0x03) << 8) | b2;          // Résultat 10 bits
}

📌 I2C vs SPI — Quand utiliser lequel ?

CritèreI2CSPI
Nombre de fils2 (SDA, SCL)4 (SCK, MOSI, MISO, CS)
Vitesse max400 kHz (Fast)Plusieurs MHz
Multi-périphériquesOui (adresses)Oui (1 CS par esclave)
Full-duplexNonOui
Idéal pourCapteurs lents, EEPROM, RTCCarte SD, écran TFT, ADC rapide
Pull-up nécessairesOui (4.7 kΩ)Non

En résumé : utilisez I2C quand vous avez beaucoup de périphériques lents et peu de broches disponibles, et SPI quand la vitesse est critique ou que vous transférez beaucoup de données.