🔌
Protocoles de Communication
I2C - SPI - UART - CAN - RS-485
Communications Serie pour Systemes Embarques
I²C
SPI
UART
CAN Bus
Chapitre 1 : Introduction aux Communications Serie
Concepts fondamentaux des protocoles de communication
🎯 Objectifs du chapitre
- Differencier communication serie et parallele
- Comprendre les modes synchrone et asynchrone
- Connaitre les topologies de communication
- Identifier les protocoles adaptes a chaque usage
1.1 Communication Serie vs Parallele
COMMUNICATION PARALLELE COMMUNICATION SERIE
Emetteur Recepteur Emetteur Recepteur
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ D0 │─────│ D0 │ │ │ │ │
│ D1 │─────│ D1 │ │ TX │─────│ RX │
│ D2 │─────│ D2 │ │ │ │ │
│ D3 │─────│ D3 │ └─────┘ └─────┘
│ D4 │─────│ D4 │
│ D5 │─────│ D5 │ 1 bit a la fois
│ D6 │─────│ D6 │ Moins de fils
│ D7 │─────│ D7 │ Longues distances
└─────┘ └─────┘
8 bits simultanes
Beaucoup de fils
Courtes distances
| Critere | Parallele | Serie |
|---|---|---|
| Vitesse | Rapide (theorique) | Plus lente par bit |
| Nombre de fils | 8+ fils donnees | 1-4 fils |
| Distance | Courte (<1m) | Longue (km possible) |
| Cout | Eleve (cablage) | Faible |
| Interferences | Probleme de diaphonie | Meilleure immunite |
| Exemple | Port parallele, bus memoire | UART, I2C, SPI, USB |
1.2 Synchrone vs Asynchrone
Communication Asynchrone
- Pas de signal d'horloge partage
- Utilise des bits de START et STOP
- Emetteur et recepteur doivent avoir la meme vitesse (baud rate)
- Exemple : UART
Communication Synchrone
- Signal d'horloge (CLK) partage entre emetteur et recepteur
- Les donnees sont echantillonnees sur front montant ou descendant
- Plus rapide et plus fiable
- Exemples : I2C, SPI
ASYNCHRONE (UART) SYNCHRONE (SPI/I2C)
TX ──┬─┬─┬─┬─┬─┬─┬─┬─┬── CLK ─┬─┬─┬─┬─┬─┬─┬─┬─
│S│0│1│0│1│1│0│1│P│ │ │ │ │ │ │ │ │ │
S = Start bit DATA ─┬───┬───┬───┬───
P = Stop bit │ 0 │ 1 │ 0 │ 1 │
Pas d'horloge commune
Donnees synchronisees
avec l'horloge
1.3 Topologies de Communication
↔️
Point a Point
2 dispositifs seulement
UART classique
🌐
Multi-maitre
Plusieurs maitres
I2C, CAN
⭐
Maitre-Esclave
1 maitre, N esclaves
SPI
Modes de transmission
- Simplex : Communication dans un seul sens
- Half-duplex : Communication bidirectionnelle, mais pas simultanee
- Full-duplex : Communication bidirectionnelle simultanee (TX et RX)
1.4 Vue d'ensemble des Protocoles
| Protocole | Type | Fils | Vitesse | Distance | Usage |
|---|---|---|---|---|---|
| UART | Asynchrone | 2 (TX/RX) | ~1 Mbps | ~15 m | Debug, GPS, Bluetooth |
| I2C | Synchrone | 2 (SDA/SCL) | 100-400 kbps | ~1 m | Capteurs, EEPROM, RTC |
| SPI | Synchrone | 4 (MOSI/MISO/SCK/SS) | ~50 Mbps | ~1 m | Flash, ecrans, ADC |
| CAN | Asynchrone | 2 (CAN_H/CAN_L) | 1 Mbps | ~1 km | Automobile, industrie |
| RS-485 | Asynchrone | 2 (A/B) | 10 Mbps | ~1.2 km | Industrie, Modbus |
| USB | Synchrone | 4 | 480 Mbps+ | 5 m | Peripheriques PC |
Chapitre 2 : UART - Universal Asynchronous Receiver Transmitter
Communication serie asynchrone
🎯 Objectifs du chapitre
- Comprendre le fonctionnement de l'UART
- Configurer les parametres (baud rate, parite)
- Analyser une trame UART
- Programmer la communication UART
2.1 Principe de l'UART
UART (Universal Asynchronous Receiver Transmitter)
Protocole de communication serie asynchrone point-a-point. Pas d'horloge partagee : emetteur et recepteur doivent etre configures avec le meme baud rate.
Connexion UART
CONNEXION UART CROISEE
Dispositif A Dispositif B
┌──────────┐ ┌──────────┐
│ │ TX ───────│ RX │
│ MCU │ │ MCU │
│ │ RX ───────│ TX │
│ │ │ │
│ │ GND ───────│ GND │
└──────────┘ └──────────┘
TX (Transmit) de A → RX (Receive) de B
RX (Receive) de A ← TX (Transmit) de B
GND commun obligatoire
2.2 Format de la Trame UART
TRAME UART - Format 8N1 (le plus courant)
Ligne au repos (IDLE) = niveau HAUT (1)
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
1 ───┤ │ │ │ │ │ │ │ │ │ │ │─── 1
│ │ D │ D │ D │ D │ D │ D │ D │ D │ │ │
0 ───┤ S │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ P │ T │
│ T │ │ │ │ │ │ │ │ │ │ P │
│ A │ │ │ DONNEES (LSB first) │ │ │
│ R │ │ │ │ A │ S │
│ T │ │ │ │ R │ T │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
START bit : Toujours 0 (passage de 1 a 0)
DATA bits : 5 a 9 bits (generalement 8)
PARITY : Optionnel (Even, Odd, None)
STOP bit : 1 ou 2 bits a 1
Notation courante
- 8N1 : 8 bits de donnees, No parity, 1 stop bit
- 8E1 : 8 bits de donnees, Even parity, 1 stop bit
- 8O1 : 8 bits de donnees, Odd parity, 1 stop bit
2.3 Baud Rate
Baud Rate
Nombre de symboles par seconde. En UART (2 niveaux), 1 baud = 1 bit/s. Les deux dispositifs DOIVENT avoir le meme baud rate (tolerance ±2-3%).
Baud rates standards
| Baud Rate | Duree bit | Usage |
|---|---|---|
| 9600 | 104.17 µs | Capteurs, GPS (defaut courant) |
| 19200 | 52.08 µs | Modems |
| 38400 | 26.04 µs | Communication rapide |
| 57600 | 17.36 µs | Communication rapide |
| 115200 | 8.68 µs | Debug, Bluetooth, WiFi modules |
| 921600 | 1.09 µs | Haute vitesse |
Duree d'un bit = 1 / Baud Rate
2.4 Programmation UART (Arduino)
// UART sur Arduino void setup() { // Initialiser UART a 115200 baud Serial.begin(115200); // Attendre l'ouverture du port while (!Serial) { ; // Necessaire pour certaines cartes } Serial.println("UART initialise a 115200 baud"); } void loop() { // Verifier si donnees disponibles if (Serial.available() > 0) { // Lire un octet char received = Serial.read(); // Echo : renvoyer le caractere recu Serial.print("Recu: "); Serial.println(received); } } // Envoyer des donnees Serial.print("Hello"); // Texte sans retour ligne Serial.println("World"); // Texte avec retour ligne Serial.write(0x41); // Envoyer un octet (ASCII 'A') Serial.write(buffer, len); // Envoyer un tableau
2.5 UART sur STM32
// UART sur STM32 (HAL) // Configuration dans CubeMX : USART2, 115200, 8N1 UART_HandleTypeDef huart2; uint8_t rxBuffer[100]; uint8_t txData[] = "Hello STM32!\r\n"; // Envoi bloquant HAL_UART_Transmit(&huart2, txData, strlen((char*)txData), 100); // Reception bloquante (attend 10 octets) HAL_UART_Receive(&huart2, rxBuffer, 10, 1000); // Envoi non-bloquant (interruption) HAL_UART_Transmit_IT(&huart2, txData, strlen((char*)txData)); // Reception par interruption HAL_UART_Receive_IT(&huart2, rxBuffer, 1); // Callback de reception void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { // Traiter rxBuffer[0] HAL_UART_Receive_IT(&huart2, rxBuffer, 1); // Relancer } }
Chapitre 3 : I2C - Principes
Inter-Integrated Circuit - Fondamentaux
🎯 Objectifs du chapitre
- Comprendre l'architecture I2C
- Connaitre le format des trames I2C
- Maitriser l'adressage 7 bits
- Analyser les conditions START et STOP
3.1 Presentation de l'I2C
I2C (Inter-Integrated Circuit)
Bus serie synchrone developpe par Philips (maintenant NXP). Utilise seulement 2 fils (SDA et SCL) pour connecter plusieurs peripheriques. Architecture maitre-esclave avec possibilite multi-maitre.
Caracteristiques
2️⃣
2 fils
SDA (Data)
SCL (Clock)
📍
Adressage
7 ou 10 bits
128 adresses max
⚡
Vitesses
100 kHz (Standard)
400 kHz (Fast)
3.2 Architecture du Bus I2C
BUS I2C - ARCHITECTURE
VCC (3.3V ou 5V)
│ │
┌┴┐ ┌┴┐ Resistances de pull-up
│R│ 4.7kΩ │R│ 4.7kΩ (obligatoires car open-drain)
└┬┘ └┬┘
│ │
├───────────┼─────────────────────────── SDA (Data)
│ │
│ ├─────────────────────────── SCL (Clock)
│ │
┌┴──────────┴┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ MAÎTRE │ │ ESCLAVE │ │ ESCLAVE │ │ ESCLAVE │
│ (MCU) │ │ 0x48 │ │ 0x68 │ │ 0x27 │
│ │ │ (Temp) │ │ (RTC) │ │ (LCD) │
└────────────┘ └──────────┘ └──────────┘ └──────────┘
- Maitre genere l'horloge SCL
- Chaque esclave a une adresse unique
- Jusqu'a 128 dispositifs (adressage 7 bits)
Resistances de Pull-up
Les lignes SDA et SCL sont en open-drain : elles ne peuvent que tirer vers le bas. Les resistances de pull-up (typiquement 4.7kΩ a 10kΩ) ramenent les lignes a VCC quand personne ne tire.
3.3 Conditions START et STOP
CONDITIONS START ET STOP
SCL ────────┐ ┌─────────────┐ ┌──────────────
│ │ │ │
└───┘ └───┘
SDA ──────┐ ┌─────────────┐ ┌────────
│ │ │ │
└───────┘ └───────┘
│START│ │ STOP │
└─────┘ └──────┘
START: SDA passe de HIGH a LOW pendant que SCL est HIGH
STOP: SDA passe de LOW a HIGH pendant que SCL est HIGH
(Normalement SDA ne change que quand SCL est LOW)
3.4 Format de la Trame I2C
TRAME I2C - ECRITURE
┌───────┬─────────────────────┬─────┬─────────────────┬─────┬──────┐
│ START │ ADRESSE (7 bits) │ R/W │ ACK │DATA │ STOP │
│ │ A6 A5 A4 A3 A2 A1 A0│ 0 │ (esclave) │ 8b │ │
└───────┴─────────────────────┴─────┴─────────────────┴─────┴──────┘
R/W = 0 : Ecriture (Maitre → Esclave)
R/W = 1 : Lecture (Esclave → Maitre)
ACK (Acknowledge) : L'esclave tire SDA a LOW pour confirmer
NACK : SDA reste HIGH (pas de reponse)
TRAME I2C - LECTURE
┌───────┬──────────────┬─────┬─────┬──────────────┬─────┬─────┬──────┐
│ START │ ADRESSE 7b │ R/W │ ACK │ REGISTRE │ ACK │ │ │
│ │ │ 0 │ │ │ │ │ │
├───────┼──────────────┼─────┼─────┼──────────────┼─────┼─────┼──────┤
│ START │ ADRESSE 7b │ R/W │ ACK │ DATA (escl.) │NACK │STOP │ │
│ REP. │ │ 1 │ │ │ │ │ │
└───────┴──────────────┴─────┴─────┴──────────────┴─────┴─────┴──────┘
3.5 Adresses I2C Courantes
| Peripherique | Adresse (7 bits) | Usage |
|---|---|---|
| EEPROM 24C02 | 0x50 - 0x57 | Memoire non volatile |
| DS1307 / DS3231 | 0x68 | Horloge temps reel (RTC) |
| BMP280 / BME280 | 0x76 ou 0x77 | Capteur pression/temperature |
| SHT31 | 0x44 ou 0x45 | Capteur humidite/temperature |
| MPU6050 | 0x68 ou 0x69 | Accelerometre/Gyroscope |
| OLED SSD1306 | 0x3C ou 0x3D | Ecran OLED |
| PCF8574 | 0x20 - 0x27 | Expanseur GPIO |
| LCD I2C (PCF8574) | 0x27 ou 0x3F | Ecran LCD 16x2 |
Chapitre 4 : I2C - Pratique
Programmation I2C sur Arduino et STM32
🎯 Objectifs du chapitre
- Scanner le bus I2C
- Lire et ecrire des registres
- Interfacer des capteurs I2C courants
- Deboguer les problemes I2C
4.1 I2C sur Arduino
// I2C sur Arduino avec Wire.h #include <Wire.h> void setup() { Wire.begin(); // Initialiser I2C (maitre) Serial.begin(115200); } // Scanner I2C - Trouver tous les peripheriques void scanI2C() { Serial.println("Scan I2C..."); for (byte addr = 1; addr < 127; addr++) { Wire.beginTransmission(addr); byte error = Wire.endTransmission(); if (error == 0) { Serial.print("Trouve a 0x"); Serial.println(addr, HEX); } } } // Ecriture d'un registre void writeRegister(uint8_t deviceAddr, uint8_t reg, uint8_t value) { Wire.beginTransmission(deviceAddr); Wire.write(reg); Wire.write(value); Wire.endTransmission(); } // Lecture d'un registre uint8_t readRegister(uint8_t deviceAddr, uint8_t reg) { Wire.beginTransmission(deviceAddr); Wire.write(reg); Wire.endTransmission(); Wire.requestFrom(deviceAddr, (uint8_t)1); return Wire.read(); }
4.2 Exemple : Capteur BME280
// Lecture temperature avec BME280 #include <Wire.h> #include <Adafruit_BME280.h> Adafruit_BME280 bme; #define BME280_ADDR 0x76 void setup() { Serial.begin(115200); Wire.begin(); if (!bme.begin(BME280_ADDR)) { Serial.println("BME280 non trouve!"); while(1); } Serial.println("BME280 OK"); } void loop() { float temp = bme.readTemperature(); float hum = bme.readHumidity(); float pres = bme.readPressure() / 100.0; // hPa Serial.print("Temp: "); Serial.print(temp); Serial.println(" °C"); Serial.print("Hum: "); Serial.print(hum); Serial.println(" %"); Serial.print("Pres: "); Serial.print(pres); Serial.println(" hPa"); delay(2000); }
4.3 I2C sur STM32 (HAL)
// I2C sur STM32 avec HAL I2C_HandleTypeDef hi2c1; #define DEVICE_ADDR (0x76 << 1) // Adresse 8 bits (decalee) // Ecriture d'un registre HAL_StatusTypeDef I2C_WriteReg(uint8_t reg, uint8_t value) { uint8_t data[2] = {reg, value}; return HAL_I2C_Master_Transmit(&hi2c1, DEVICE_ADDR, data, 2, 100); } // Lecture d'un registre uint8_t I2C_ReadReg(uint8_t reg) { uint8_t value; HAL_I2C_Master_Transmit(&hi2c1, DEVICE_ADDR, ®, 1, 100); HAL_I2C_Master_Receive(&hi2c1, DEVICE_ADDR, &value, 1, 100); return value; } // Avec HAL_I2C_Mem_Read (plus simple) uint8_t I2C_ReadReg_Mem(uint8_t reg) { uint8_t value; HAL_I2C_Mem_Read(&hi2c1, DEVICE_ADDR, reg, 1, &value, 1, 100); return value; }
Chapitre 5 : SPI - Principes
Serial Peripheral Interface - Fondamentaux
🎯 Objectifs du chapitre
- Comprendre l'architecture SPI
- Connaitre les 4 modes SPI (CPOL/CPHA)
- Maitriser la selection des esclaves
- Comparer SPI et I2C
5.1 Presentation du SPI
SPI (Serial Peripheral Interface)
Bus serie synchrone full-duplex developpe par Motorola. Utilise 4 fils et une architecture maitre-esclave strict. Plus rapide que I2C mais necessite plus de fils.
Signaux SPI
| Signal | Autres noms | Direction | Fonction |
|---|---|---|---|
| SCK | SCLK, CLK | Maitre → Esclave | Horloge |
| MOSI | SDO, DO, DOUT, SI | Maitre → Esclave | Donnees maitre vers esclave |
| MISO | SDI, DI, DIN, SO | Esclave → Maitre | Donnees esclave vers maitre |
| SS | CS, NSS, CE | Maitre → Esclave | Chip Select (actif bas) |
5.2 Architecture SPI
ARCHITECTURE SPI
┌──────────────┐
│ ESCLAVE 1 │
┌────────│ (SD Card) │
│ └──────────────┘
│ SS1
┌──────────────┐ │ ┌──────────────┐
│ MAÎTRE │───────────┼────────│ ESCLAVE 2 │
│ (MCU) │ SCK │ SS2 │ (Display) │
│ │───────────┼────────└──────────────┘
│ │ │
│ │───────────┼────────┌──────────────┐
│ │ MOSI │ SS3 │ ESCLAVE 3 │
│ │───────────┼────────│ (Flash) │
│ │ │ └──────────────┘
│ │ MISO │
│ │───────────┘
└──────────────┘
- SCK, MOSI, MISO : partages par tous les esclaves
- SS : un fil dedie par esclave (actif LOW)
- Un seul esclave actif a la fois
5.3 Modes SPI (CPOL / CPHA)
Le SPI a 4 modes definis par CPOL (polarite horloge) et CPHA (phase donnees).
| Mode | CPOL | CPHA | Horloge repos | Echantillonnage |
|---|---|---|---|---|
| Mode 0 | 0 | 0 | LOW | Front montant |
| Mode 1 | 0 | 1 | LOW | Front descendant |
| Mode 2 | 1 | 0 | HIGH | Front descendant |
| Mode 3 | 1 | 1 | HIGH | Front montant |
MODE 0 (CPOL=0, CPHA=0) - Le plus courant
SS ─────┐ ┌─────
└───────────────────────────────┘
SCK ─────────┬───┬───┬───┬───┬───┬───┬───┬─────
│ │ │ │ │ │ │ │
└───┘ └───┘ └───┘ └───┘
MOSI ─────────┬───────┬───────┬───────┬───────
│ D7 │ D6 │ D5 │ ...
Echantillonnage sur front MONTANT (↑)
5.4 Comparaison SPI vs I2C
| Critere | SPI | I2C |
|---|---|---|
| Nombre de fils | 4 + (N-1) pour N esclaves | 2 (bus partage) |
| Vitesse max | ~50 Mbps | 3.4 Mbps (High Speed) |
| Mode | Full-duplex | Half-duplex |
| Adressage | Par ligne SS dediee | Par adresse 7/10 bits |
| Accuse reception | Non (aucun ACK) | Oui (ACK/NACK) |
| Nombre max esclaves | Limite par pins SS | 128 (7 bits) |
| Complexite | Simple | Plus complexe |
| Usage typique | Flash, SD, ecrans, ADC rapides | Capteurs, EEPROM, RTC |
Chapitre 6 : SPI - Pratique
Programmation SPI sur Arduino et STM32
🎯 Objectifs du chapitre
- Configurer le SPI sur Arduino et STM32
- Interfacer une memoire Flash SPI
- Communiquer avec un ecran SPI
- Lire une carte SD en SPI
6.1 SPI sur Arduino
// SPI sur Arduino #include <SPI.h> #define CS_PIN 10 // Chip Select void setup() { pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); // Desactiver esclave SPI.begin(); // Initialiser SPI // Configuration (optionnel, valeurs par defaut = Mode 0) SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // 1 MHz, MSB first, Mode 0 } // Transfert d'un octet (full-duplex) uint8_t spiTransfer(uint8_t data) { digitalWrite(CS_PIN, LOW); // Activer esclave uint8_t received = SPI.transfer(data); digitalWrite(CS_PIN, HIGH); // Desactiver esclave return received; } // Lire un registre uint8_t readRegister(uint8_t reg) { digitalWrite(CS_PIN, LOW); SPI.transfer(reg | 0x80); // Bit 7 = Read uint8_t value = SPI.transfer(0x00); // Dummy byte digitalWrite(CS_PIN, HIGH); return value; } // Ecrire un registre void writeRegister(uint8_t reg, uint8_t value) { digitalWrite(CS_PIN, LOW); SPI.transfer(reg & 0x7F); // Bit 7 = 0 = Write SPI.transfer(value); digitalWrite(CS_PIN, HIGH); }
6.2 SPI sur STM32 (HAL)
// SPI sur STM32 avec HAL SPI_HandleTypeDef hspi1; // Chip Select (GPIO) #define CS_GPIO_Port GPIOA #define CS_Pin GPIO_PIN_4 #define CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) // Transfert full-duplex uint8_t SPI_Transfer(uint8_t data) { uint8_t received; HAL_SPI_TransmitReceive(&hspi1, &data, &received, 1, 100); return received; } // Lire un registre uint8_t SPI_ReadReg(uint8_t reg) { uint8_t txData[2] = {reg | 0x80, 0x00}; uint8_t rxData[2]; CS_LOW(); HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, 100); CS_HIGH(); return rxData[1]; } // Ecrire un registre void SPI_WriteReg(uint8_t reg, uint8_t value) { uint8_t txData[2] = {reg & 0x7F, value}; CS_LOW(); HAL_SPI_Transmit(&hspi1, txData, 2, 100); CS_HIGH(); }