🔗 Communication I2C — PIC16F877A

Protocole I2C, module MSSP, registres SSPCON/SSPSTAT et interfaçage de capteurs et EEPROM

Qu'est-ce que l'I2C ?

L'I2C (Inter-Integrated Circuit, prononcé « I-deux-C ») est un bus de communication série synchrone inventé par Philips (aujourd'hui NXP) dans les années 1980. C'est l'un des protocoles les plus utilisés en électronique embarquée pour connecter des capteurs, des mémoires EEPROM, des écrans OLED, des horloges RTC et bien d'autres périphériques à un microcontrôleur.

Le bus I2C n'utilise que 2 fils (plus la masse) :

Plusieurs périphériques (jusqu'à 128 avec adressage 7 bits) peuvent partager le même bus, chacun identifié par une adresse unique. Le microcontrôleur agit comme maître (Master) et les capteurs/mémoires comme esclaves (Slave).

Caractéristiques du bus I2C

ParamètreValeur
Nombre de fils2 (SDA + SCL) + GND commun
Vitesse standard100 kHz (Standard Mode)
Vitesse rapide400 kHz (Fast Mode)
Adressage7 bits (128 adresses) ou 10 bits (1024)
TopologieMulti-maître, multi-esclave sur le même bus
Résistances pull-up4.7 kΩ sur SDA et SCL (obligatoires)

⚠️ Résistances pull-up obligatoires ! Les lignes SDA et SCL sont en drain ouvert (open-drain). Sans résistances pull-up de 4.7 kΩ vers VDD, le bus ne fonctionne pas. C'est l'erreur n°1 des débutants avec l'I2C. Certains modules (écrans OLED, capteurs) intègrent déjà ces résistances.

📡 Le protocole I2C en détail

Une transaction I2C typique (le maître lit un octet depuis un esclave) se déroule ainsi :

  1. Start Condition — Le maître tire SDA vers le bas pendant que SCL est haut. Signale le début de la communication.
  2. Adresse + R/W — Le maître envoie l'adresse de l'esclave (7 bits) suivie du bit de direction : 0 = écriture (W), 1 = lecture (R).
  3. ACK de l'esclave — L'esclave reconnaît sa propre adresse en tirant SDA vers le bas (ACK). S'il ne répond pas, c'est un NACK (SDA reste haut).
  4. Données — Le maître ou l'esclave envoie un ou plusieurs octets, chaque octet suivi d'un ACK/NACK.
  5. Stop Condition — Le maître relâche SDA pendant que SCL est haut. Signale la fin de la communication et libère le bus.

🔌 Brochage I2C sur le PIC16F877A

Le PIC16F877A intègre un module MSSP (Master Synchronous Serial Port) qui supporte nativement I2C et SPI. Les broches I2C sont :

Ces broches doivent être configurées en entrée (TRISC bits 3 et 4 à 1) car le module MSSP contrôle leur direction automatiquement.

📋 Registres MSSP pour l'I2C

RegistreRôle
SSPCONContrôle principal : active le module MSSP, sélectionne le mode (I2C Master/Slave)
SSPCON2Contrôle I2C Master : génère Start, Stop, Repeated Start, ACK/NACK
SSPSTATStatut : buffer plein (BF), transmission en cours, détection Start/Stop
SSPBUFBuffer de données : écriture pour envoyer, lecture pour recevoir
SSPADDEn mode Master : définit la vitesse I2C. Valeur = (Fosc / (4 × Fscl)) − 1

💻 Code complet : bibliothèque I2C Master

#include <xc.h>
#define _XTAL_FREQ 4000000

/* ---- Initialisation I2C Master à 100 kHz ---- */
void i2c_init(void) {
    TRISC3 = 1;    // SCL en entrée (contrôlé par MSSP)
    TRISC4 = 1;    // SDA en entrée (contrôlé par MSSP)
    SSPCON = 0x28;  // SSPEN=1, I2C Master mode (SSPM=1000)
    SSPCON2 = 0x00;
    SSPSTAT = 0x00;
    SSPADD = 9;    // Fscl = 4MHz / (4 × (9+1)) = 100 kHz
}

/* ---- Attendre la fin de l'opération ---- */
void i2c_wait(void) {
    while ((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));
}

/* ---- Générer condition Start ---- */
void i2c_start(void) {
    i2c_wait();
    SSPCON2bits.SEN = 1;  // Envoyer Start
}

/* ---- Générer condition Stop ---- */
void i2c_stop(void) {
    i2c_wait();
    SSPCON2bits.PEN = 1;  // Envoyer Stop
}

/* ---- Générer Repeated Start ---- */
void i2c_restart(void) {
    i2c_wait();
    SSPCON2bits.RSEN = 1;
}

/* ---- Envoyer un octet, retourne ACK (0) ou NACK (1) ---- */
unsigned char i2c_write(unsigned char data) {
    i2c_wait();
    SSPBUF = data;
    i2c_wait();
    return SSPCON2bits.ACKSTAT;  // 0=ACK, 1=NACK
}

/* ---- Recevoir un octet avec ACK ---- */
unsigned char i2c_read_ack(void) {
    i2c_wait();
    SSPCON2bits.RCEN = 1;         // Activer réception
    i2c_wait();
    unsigned char data = SSPBUF;
    SSPCON2bits.ACKDT = 0;         // ACK (on veut plus de données)
    SSPCON2bits.ACKEN = 1;         // Envoyer ACK
    return data;
}

/* ---- Recevoir un octet avec NACK (dernier octet) ---- */
unsigned char i2c_read_nack(void) {
    i2c_wait();
    SSPCON2bits.RCEN = 1;
    i2c_wait();
    unsigned char data = SSPBUF;
    SSPCON2bits.ACKDT = 1;         // NACK (c'est le dernier octet)
    SSPCON2bits.ACKEN = 1;
    return data;
}

🌡️ Exemple : Lire un capteur I2C (température)

Voici comment lire un registre d'un capteur I2C générique (ex : LM75 à l'adresse 0x48) :

#define LM75_ADDR  0x48   // Adresse 7 bits du capteur
#define TEMP_REG   0x00   // Registre de température

int lire_temperature(void) {
    unsigned char msb, lsb;

    i2c_start();
    i2c_write((LM75_ADDR << 1) | 0);  // Adresse + Write
    i2c_write(TEMP_REG);                  // Pointeur vers registre temp

    i2c_restart();
    i2c_write((LM75_ADDR << 1) | 1);  // Adresse + Read
    msb = i2c_read_ack();                // Octet poids fort
    lsb = i2c_read_nack();               // Octet poids faible (dernier)
    i2c_stop();

    int temp = (msb << 8) | lsb;
    temp >>= 5;   // Alignement selon datasheet LM75
    return temp;   // Résultat en 0.125°C par LSB
}

📋 Périphériques I2C populaires

ComposantAdresseFonctionPrix
SSD13060x3CÉcran OLED 128×64~3 €
BMP280 / BME2800x76/0x77Température, pression, (humidité)~2 €
DS32310x68Horloge temps réel (RTC) de précision~2 €
24C02 / 24C2560x50–0x57EEPROM externe (256 octets à 32 Ko)~0.50 €
MPU60500x68/0x69Accéléromètre + gyroscope 6 axes~2 €
PCF85740x20–0x27Expandeur GPIO 8 bits (écran LCD I2C)~1 €

🔧 Dépannage I2C