Maîtriser la communication I2C avec Arduino : Tutoriel Pratique
Introduction
Dans ce tutoriel, nous allons apprendre comment fonctionne le protocole de communication I2C et nous ferons aussi un exemple pratique avec la carte Arduino et un capteur qui utilise ce protocole. Vous pouvez regarder la vidéo suivante ou lire le tutoriel écrit ci-dessous.
Vue d'ensemble
Le bus de communication I2C est très populaire et largement utilisé par de nombreux dispositifs électroniques car il peut être facilement implémenté dans de nombreux designs électroniques qui nécessitent une communication entre un maître et plusieurs dispositifs esclaves, ou même plusieurs dispositifs maîtres. La facilité d'implémentation vient du fait que seulement deux fils sont nécessaires pour la communication entre jusqu'à presque 128 (112) dispositifs lors de l'utilisation d'un adressage 7 bits et jusqu'à presque 1024 (1008) dispositifs lors de l'utilisation d'un adressage 10 bits.

Comment fonctionne I2C
Comment est-ce possible, une communication entre tant de dispositifs avec seulement deux fils ? Eh bien, chaque dispositif a un ID prédéfini ou une adresse de périphérique unique, donc le maître peut choisir avec quels dispositifs il communiquera.
Les deux fils, ou lignes, sont appelés Serial Clock (ou SCL) et Serial Data (ou SDA). La ligne SCL est le signal d'horloge qui synchronise le transfert de données entre les dispositifs sur le bus I2C et elle est générée par le dispositif maître. L'autre ligne est la ligne SDA qui transporte les données.
Les deux lignes sont en "open-drain" (collecteur ouvert), ce qui signifie que des résistances de pull-up doivent être attachées à elles pour que les lignes soient à l'état haut car les dispositifs sur le bus I2C sont actifs à l'état bas. Les valeurs couramment utilisées pour les résistances vont de 2K pour des vitesses plus élevées à environ 400 kbps, à 10K pour des vitesses plus basses à environ 100 kbps.

Protocole I2C

Le signal de données est transféré par séquences de 8 bits. Donc après qu'une condition de démarrage spéciale se produit, vient la première séquence de 8 bits qui indique l'adresse de l'esclave auquel les données sont envoyées. Après chaque séquence de 8 bits suit un bit appelé Acknowledge (Acquittement). Après le premier bit d'acquittement, dans la plupart des cas, vient une autre séquence d'adressage mais cette fois pour les registres internes du dispositif esclave. Juste après les séquences d'adressage suivent les séquences de données autant que nécessaire jusqu'à ce que les données soient complètement envoyées et cela se termine par une condition d'arrêt spéciale.
Examinons de plus près ces événements. La condition de démarrage se produit lorsque la ligne de données passe à l'état bas alors que la ligne d'horloge est encore à l'état haut. Après cela, l'horloge démarre et chaque bit de données est transféré pendant chaque impulsion d'horloge.
La séquence d'adressage du dispositif commence avec le bit de poids fort (MSB) en premier et se termine avec le bit de poids faible (LSB) et elle est en fait composée de 7 bits car le 8ème bit est utilisé pour indiquer si le maître va écrire sur l'esclave (logique bas) ou lire depuis lui (logique haut).

Le bit suivant AKC/NACK est utilisé par le dispositif esclave pour indiquer s'il a reçu avec succès la séquence précédente de bits. Donc à ce moment, le dispositif maître cède le contrôle de la ligne SDA au dispositif esclave et si le dispositif esclave a reçu avec succès la séquence précédente, il tirera la ligne SDA vers le bas vers la condition appelée Acknowledge (Acquittement). Si l'esclave ne tire pas la ligne SDA vers le bas, la condition est appelée Not Acknowledge (Non-acquittement), et signifie qu'il n'a pas reçu avec succès la séquence précédente, ce qui peut être causé par plusieurs raisons. Par exemple, l'esclave pourrait être occupé, pourrait ne pas comprendre les données ou la commande reçue, ne peut plus recevoir de données, etc. Dans un tel cas, le dispositif maître décide comment il procédera.

Vient ensuite l'adressage des registres internes. Les registres internes sont des emplacements dans la mémoire de l'esclave contenant diverses informations ou données. Par exemple, l'accéléromètre ADX345 a une adresse de périphérique unique et des adresses de registres internes additionnelles pour les axes X, Y et Z. Donc si nous voulons lire les données de l'axe X, nous devons d'abord envoyer l'adresse du périphérique et ensuite l'adresse de registre interne particulière pour l'axe X. Ces adresses peuvent être trouvées dans la fiche technique du capteur.
Après l'adressage, les séquences de transfert de données commencent soit depuis le maître, soit depuis l'esclave selon le mode sélectionné au bit R/W. Après que les données sont complètement envoyées, le transfert se terminera par une condition d'arrêt qui se produit lorsque la ligne SDA passe de bas à haut alors que la ligne SCL est haute.
Exemple
Comme exemple, j'utiliserai la carte de dérivation GY-80 qui comprend 5 capteurs différents et la carte de dérivation GY-521 qui comprend 3 capteurs différents. Ainsi, nous pouvons obtenir des données de 8 capteurs différents avec seulement deux fils grâce au bus I2C.

Vous pouvez obtenir ces composants sur les sites ci-dessous :
- Accéléromètre 3 axes ADXL345
- 2 en 1 : MPU6050 Gyroscope 6 axes & Accéléromètre
- 3 en 1 : GY-80 Champ magnétique 9 axes Accélération Gyroscope
- 3 en 1 : GY-86 10DOF MS5611 HMC5883L MPU6050 Module
Voici comment nous allons connecter les cartes. La broche Serial Clock de la carte Arduino sera connectée aux broches Serial Clock des deux cartes de dérivation, la même chose pour les broches Serial Data et nous alimenterons les cartes avec les broches GND et 5V de la carte Arduino. Notez ici que nous n'utilisons pas de résistances de pull-up car les cartes de dérivation en ont déjà.

Maintenant, afin de communiquer avec ces puces ou capteurs, nous devons connaître leurs adresses uniques. Nous pouvons les trouver dans les fiches techniques des capteurs. Pour la carte de dérivation GY-80, nous avons les 4 adresses suivantes : un hexadécimal 0x53 pour le capteur accéléromètre 3 axes, un hexadécimal 0x69 pour le gyroscope 3 axes, un hexadécimal 0x1E pour le magnétomètre 3 axes et un hexadécimal 0x77 pour le capteur baromètre et thermomètre.
Pour la carte de dérivation GY-521, nous n'avons qu'une seule adresse et c'est un hexadécimal 0x68. Nous pouvons également obtenir ou vérifier les adresses en utilisant le sketch Arduino I2C Scanner qui peut être trouvé sur le site officiel Arduino. Donc ici, si nous téléversons et exécutons ce sketch, nous obtiendrons les adresses des dispositifs connectés sur le bus I2C.
| Numéro de pièce du capteur | Adresse I2C |
|---|---|
| Accéléromètre 3 axes Analog Devices ADXL345 | 0x53 |
| Gyroscope 3 axes ST Microelectronics L3G4200D | 0x69 |
| Magnétomètre 3 axes Honeywell MC5883L | 0x1E |
| Baromètre + Thermomètre Bosch BMP085 | 0x77 |
Après avoir trouvé les adresses des dispositifs, nous devons également trouver les adresses de leurs registres internes afin de lire les données depuis eux. Par exemple, si nous voulons lire les données pour l'axe X depuis le capteur accéléromètre 3 axes de la carte de dérivation GY-80, nous devons trouver l'adresse de registre interne où les données de l'axe X sont stockées. D'après la fiche technique du capteur, nous pouvons voir que les données pour l'axe X sont en fait stockées dans deux registres, DATAX0 avec une adresse hexadécimale 0x32 et DATAX1 avec une adresse hexadécimale 0x33.
Code Arduino I2C
Maintenant, faisons le code qui récupérera les données pour l'axe X. Nous utiliserons la bibliothèque Arduino Wire qui doit être incluse dans le sketch. Ici, nous devons d'abord définir l'adresse du capteur et les adresses des deux registres internes que nous avons trouvées précédemment. La fonction Wire.begin() initialisera la bibliothèque Wire et nous devrons aussi initialiser la communication série car nous utiliserons le moniteur série pour afficher les données du capteur.
Dans le loop(), nous commencerons avec la fonction Wire.beginTransmission() qui débutera la transmission vers le capteur particulier, l'accéléromètre 3 axes dans notre cas. Ensuite, avec la fonction Wire.write(), nous demanderons les données particulières des deux registres de l'axe X. La fonction Wire.endTransmission() terminera la transmission et transmettra les données depuis les registres. Maintenant, avec la fonction Wire.requestFrom(), nous demanderons les données transmises ou les deux octets des deux registres.
La fonction Wire.available() retournera le nombre d'octets disponibles pour la récupération et si ce nombre correspond à nos octets demandés, dans notre cas 2 octets, en utilisant la fonction Wire.read(), nous liront les octets des deux registres de l'axe X. À la fin, nous afficherons les données dans le moniteur série. Voici ces données mais gardez à l'esprit que ce sont des données brutes et qu'un peu de maths est nécessaire pour obtenir les bonnes valeurs de l'axe X. Vous pouvez trouver plus de détails à ce sujet dans mon prochain tutoriel sur l'utilisation d'accéléromètres avec la carte Arduino car je ne veux pas surcharger ce tutoriel car son objectif principal était d'expliquer comment fonctionne la communication I2C Arduino.
/*
* Comment fonctionne le protocole de communication I2C - Tutoriel Arduino I2C
*
* by Dejan, www.HowToMechatronics.com
*/
#include <Wire.h>
int ADXLAddress = 0x53; // Adresse du périphérique incluant le 8ème bit pour sélectionner le mode, lecture dans ce cas
#define X_Axis_Register_DATAX0 0x32 // Adresse hexadécimale pour le registre interne DATAX0
#define X_Axis_Register_DATAX1 0x33 // Adresse hexadécimale pour le registre interne DATAX1
#define Power_Register 0x2D // Registre de contrôle d'alimentation
int X0,X1,X_out;
void setup() {
Wire.begin(); // Initialise la bibliothèque Wire
Serial.begin(9600);
delay(100);
// Activer la mesure
Wire.beginTransmission(ADXLAddress);
Wire.write(Power_Register);
// Bit D3 High pour activer la mesure (0000 1000)
Wire.write(8);
Wire.endTransmission();
}
void loop() {
Wire.beginTransmission(ADXLAddress); // Début de transmission au capteur
// Demande les registres particuliers pour les données
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission(); // Termine la transmission et transmet les données des deux registres
Wire.requestFrom(ADXLAddress,2); // Demande les deux octets transmis des deux registres
if(Wire.available()<=2) {
// X0 = Wire.read(); // Lit les données du registre
X1 = Wire.read();
}
Serial.print("X0= ");
Serial.print(X0);
Serial.print(" X1= ");
Serial.println(X1);
}
Processus de fabrication
- Intégration I2C fluide entre Arduino et Raspberry Pi 2 pour projets IoT de capteurs
- Moniteur d'énergie Arduino & Data Logger fiable – Guide complet (Dr. Wattson + carte SD)
- Enregistreur de Température et Humidité Arduino avec DHT11 et Shield Ethernet
- Contrôle Arduino UNO depuis Python 3 – Communication Série et Démo LED
- Connecter Arduino à NMEA‑0183 : Guide étape par étape
- Construire JARVIS v1 : Automatisation domestique avec Arduino Nano – Tutoriel pas à pas
- Maîtriser le multitâche Arduino avec les Protothreads : Tutoriel pas à pas
- Maîtriser le nRF24L01 : Communication sans fil, Intégration Arduino, Circuits et Guide de Code
- Maîtriser le MPU6050 : Tutoriel Accéléromètre et Gyroscope pour Arduino