Protocole I2C, module MSSP, registres SSPCON/SSPSTAT et interfaçage de capteurs et EEPROM
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).
| Paramètre | Valeur |
|---|---|
| Nombre de fils | 2 (SDA + SCL) + GND commun |
| Vitesse standard | 100 kHz (Standard Mode) |
| Vitesse rapide | 400 kHz (Fast Mode) |
| Adressage | 7 bits (128 adresses) ou 10 bits (1024) |
| Topologie | Multi-maître, multi-esclave sur le même bus |
| Résistances pull-up | 4.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.
Une transaction I2C typique (le maître lit un octet depuis un esclave) se déroule ainsi :
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.
| Registre | Rôle |
|---|---|
SSPCON | Contrôle principal : active le module MSSP, sélectionne le mode (I2C Master/Slave) |
SSPCON2 | Contrôle I2C Master : génère Start, Stop, Repeated Start, ACK/NACK |
SSPSTAT | Statut : buffer plein (BF), transmission en cours, détection Start/Stop |
SSPBUF | Buffer de données : écriture pour envoyer, lecture pour recevoir |
SSPADD | En mode Master : définit la vitesse I2C. Valeur = (Fosc / (4 × Fscl)) − 1 |
#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;
}
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
}
| Composant | Adresse | Fonction | Prix |
|---|---|---|---|
| SSD1306 | 0x3C | Écran OLED 128×64 | ~3 € |
| BMP280 / BME280 | 0x76/0x77 | Température, pression, (humidité) | ~2 € |
| DS3231 | 0x68 | Horloge temps réel (RTC) de précision | ~2 € |
| 24C02 / 24C256 | 0x50–0x57 | EEPROM externe (256 octets à 32 Ko) | ~0.50 € |
| MPU6050 | 0x68/0x69 | Accéléromètre + gyroscope 6 axes | ~2 € |
| PCF8574 | 0x20–0x27 | Expandeur GPIO 8 bits (écran LCD I2C) | ~1 € |