Arduino et le bus I2C

Exemple d'application : le capteur de température DS1621

f-leb

Comment échanger des données entre une carte Arduino et un périphérique communiquant avec une liaison série I^2C (Inter-Integrated Circuit).

Commentez

- Titre : Arduino et le bus I2C

- Auteur : f-leb

- Parution : 11 octobre 2015

- Licence : Le contenu de cet article est rédigé par f-leb et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Voir également :

- L'univers Arduino - Partie I : Aperçu de la plateforme Arduino et ses cartes d'interface (shields)

- L'univers Arduino - Partie II : Réalisation d'un prototype à base d'Arduino

Sommaire

- I. Introduction

- II. Le bus I2C

- III. Pourquoi utiliser des périphériques I2C ?

- IV. I2C et Arduino

- V. Exemple d'application : le capteur de température DS1621

- V-A. Présentation du composant

- V-B. Branchements

- V-C. Comment envoyer des données

- V-D. Comment recevoir des données

- V-E. Le code de démonstration

- VI. Conclusion

- VII. Sitographie

- VIII. Remerciements

I - Introduction

I^2C est le sigle d'Inter-Integrated Circuit. À la fin des années 1970, la division des semi-conducteurs de chez Philips (maintenant devenue NXP) avait vu la nécessité de simplifier et standardiser les échanges de données entre les différents circuits intégrés dans leurs produits. Leur solution fut le bus I^2C, elle réduisait le nombre de lignes nécessaires à seulement deux lignes, SDA - Serial DAta, et SCL - Serial CLock.

Dans ce tutoriel, nous allons décrire l'architecture physique du bus I^2C, le protocole de communication série et comment communiquer en I^2C entre une carte Arduino et un capteur de température DS1621.

Le capteur de température DS1621

II - Le bus I2C

C'est donc maintenant chez le fabricant de semi-conducteurs NXP que vous trouverez les spécifications de la norme I^2C.

À l'origine, la communication était limitée à la vitesse de transfert de 100 kbit/s, et cela suffisait dans la majorité des cas. Pour des débits plus rapides, de nouvelles spécifications sont nées. D'abord un Fast Mode à 400 kbit/s, puis un Fast Mode plus (FM+) à 1 Mbit/s. Depuis 1998, il y a une version High Speed à 3,4 Mbit/s. Le débit maximal possible via un bus I^2C est spécifié dans l'Ultra Fast mode à 5 Mbit/s, mais avec un fonctionnement un peu particulier.

Caractéristiques principales du bus I^2C :

Source : nxp.com

- seulement deux lignes (bidirectionnelles) sont nécessaires, une pour transporter les données - SDA -, une autre pour l'horloge de synchronisation - SCK - (1 bit échangé à chaque « coup » d'horloge) ;

- transmission synchrone. Pas besoin de spécifier une vitesse de transfert comme pour la liaison RS232. Ici, le périphérique maître (master) génère le signal d'horloge qui synchronise et cadence les échanges ;

- la relation entre les périphériques du bus est de type maître-esclave (master/slave). Le maître est à l'initiative de la transmission et s'adresse à un esclave (ou tous les esclaves) ;

- chaque périphérique sur le bus I^2C est adressable, avec une adresse unique pour chaque périphérique du bus ;

- l'I^2C gère le fonctionnement multimaître (multi-master), plusieurs périphériques maîtres peuvent prendre simultanément le contrôle du bus (système d'arbitrage des maîtres et gestion des collisions).

III - Pourquoi utiliser des périphériques I2C ?

Il y a des milliers de composants qui utilisent une interface I^2C, et les cartes de la famille Arduino peuvent toutes les contrôler. Les applications sont multiples : horloges temps réel, potentiomètres numériques, capteurs de température, boussoles numériques, mémoires, circuits radio FM, cartes d'extension d'entrées-sorties, contrôleurs d'afficheur LCD, accéléromètres, amplificateurs et bien d'autres encore. Vous pouvez connecter plusieurs composants adressables sur un même bus à tout moment (jusqu'à 112 périphériques I^2C adressables sur un même bus en théorie).

Quelques composants et modules bien utiles avec une interface I^2C :

Composants I^2C compatibles Arduino

Description

Le capteur de température et thermostat DS1621 (Maxim Integrated), objet de ce tutoriel.

- Mesure de température (-55 °C à +125 °C par incrément de 0,5 °C), conversion de la température en mot de 16 bits en moins d'une seconde.

- Configuration possible en thermostat (configuration des seuils de température haut et bas non volatile).

- Version en boîtier DIP 8 broches.

Mémoire morte EEPROM - 256 Kbit (Réf. 24LC256 Microchip)

- Cette mémoire morte effaçable et programmable d'une capacité de 256 kbit peut rendre bien des services pour enregistrer les données de vos capteurs (data logging).

- Version en boîtier DIP 8 broches.

Module Horloge temps réel (Real Time Clock) à base de DS1307 (Maxim Integrated).

Real Time Clock Module (Sparkfun)

Ce petit circuit imprimé comporte la puce RTC DS1307 (qui existe aussi en boîtier DIP 8 broches), mais aussi les composants nécessaires à son fonctionnement : quartz, condensateur, résistances... ainsi qu'une alimentation par pile bouton au Lithium (au verso du circuit). Il n'y a plus qu'à relier les broches SDA et SCL.
Dans les registres du DS1302 : année, date, heures, minutes et secondes en temps réel.

Module de conversion Numérique-Analogique (Digital-to-Analog Converter) à base de MCP4725 (Microchip).

I2C DAC Breakout - MCP4725 (Sparkfun)

Ce circuit imprimé comporte une puce MCP4725 pour produire un signal analogique à partir d'une source numérique. Idéal pour produire des ondes sonores ou autre création musicale.

Module d'interface I^2C pour écran LCD

Ce genre de module va vous permettre d'économiser les broches de votre Arduino en interfaçant les signaux d'un écran LCD en I^2C.

Circuit d'extension Entrée-Sortie à base de PCF8575 (NXP)

I2C Expander Breakout - PCF8575 (Sparkfun)

Votre Arduino manque d'entrées-sorties ? Ce circuit permet d'obtenir 16 entrées-sorties supplémentaires adressables individuellement grâce au bus I^2C.

IV - I2C et Arduino

En ce qui concerne l'architecture matérielle, le câblage est très simple. Ceux qui disposent de l'Arduino Uno ou d'une carte compatible utiliseront les connecteurs A4 pour SDA (les données) et A5 pour SCL (l'horloge) :

Si vous utilisez une carte Arduino Mega, SDA est sur le connecteur 20 et SCL sur le connecteur 21. Si vous voulez connecter un shield Arduino avec une interface I^2C, vérifiez bien sa compatibilité avec l'Arduino Mega. Pour les autres types de cartes, il faudra consulter la fiche technique (data sheet) ou se renseigner sur le site de la communauté Arduino. Finalement, si vous interfacez directement le microcontrôleur ATmega328-PU dans son boîtier DIP, vous utiliserez les pattes 27 pour SDA et 28 pour SCL.

L'architecture du bus est simple, ici avec deux périphériques sur le bus I^2C :

Source : nxp.com

Avec un seul équipement I^2C connecté à l'Arduino, les résistances de tirage au plus (pull-up resistors) ne sont pas (normalement) requises, car le microcontrôleur ATmega328 de l'Arduino les intègre déjà. Si plusieurs équipements sont connectés au bus, utilisez deux résistances de 10 kΩ chacune. Comme souvent, ce sont les tests que vous effectuerez en prototypant un circuit sur une plaquette de câblage rapide que vous jugerez de leur nécessité. Parfois, dans les fiches techniques (datasheets), vous trouverez d'autres valeurs de résistance, du 4,7 kΩ par exemple. Dans ce cas, suivez les recommandations du constructeur.

La distance maximale maître-esclave pour un bus I^2C est d'environ un mètre et dépend de plusieurs facteurs comme la capacité électrique du bus ou le débit de transmission. Cette distance peut être sensiblement augmentée en passant par des interfaces spécifiques (un i2c-bus extender amplifie le courant débité par les broches SDA et SCL, ce qui augmente la portée du signal).

Chaque équipement peut être connecté au bus dans n'importe quel ordre, et certains équipements peuvent même passer du statut de maître à esclave et inversement. Dans ce tutoriel, la carte Arduino sera dans la situation du maître et le composant connecté au bus I^2C sera en situation d'esclave. De l'Arduino, on peut « écrire » sur la ligne pour envoyer des données vers un composant, ou « lire » la ligne pour récupérer les données retournées par celui-ci.

Se pose maintenant la question de savoir comment différencier les composants connectés sur le bus. En fait, chacun d'entre eux doit posséder une adresse unique, fixée par le constructeur et parfois configurable. Cette adresse sera utilisée pour écrire ou lire les données vers le composant souhaité.

Pour communiquer en suivant le protocole I^2C, on inclura une bibliothèque Arduino dans les croquis, ici nommée Wire, avec un #include <Wire.h>. Puis on l'initialisera avec Wire.begin() à l'intérieur du void setup().

V - Exemple d'application : le capteur de température DS1621

V-A - Présentation du composant

Le DS1621 de Maxim Integrated apporte à la fois des fonctionnalités de thermomètre et de thermostat dans une puce au format DIP 8 broches, bien pratique pour faire un prototype sur une plaquette de câblage sans soudure.

Photo et brochage du DS1621

Schéma-bloc fonctionnel du DS1621 d'après Maxim Integrated

Ce composant intègre toute la chaîne de traitement, de l'acquisition de la température en analogique jusqu'à la conversion numérique en mot de 16 bits. Il suffit alors d'aller consulter les registres internes selon le protocole I^2C pour récupérer la température. On peut aussi configurer le DS1621 comme un thermostat avec une sortie Tout qui peut changer d'état logique lorsque la température passe en deçà d'un seuil bas (TL), ou au-delà d'un seuil haut (TH).

V-B - Branchements

Relier le DS1621 à une Arduino Uno ne présente pas de difficultés. Une plaquette de câblage rapide et quelques fils suffiront. On rappelle que les résistances pull-up ne sont pas indispensables ici puisqu'intégrées à la carte Arduino.

Les broches SDA et SDL du DS1621 sont connectées respectivement aux connecteurs A4 et A5 de l'Arduino Uno. On alimente le composant avec les broches GND et VDD reliées respectivement aux connecteurs GND et 5V de l'Arduino Uno.

On rappelle que l'adresse d'un composant I^2C est codée avec sept bits (A0 à A6). Les bits de poids faible A0, A1 et A2 peuvent être configurés matériellement, par exemple en reliant les trois broches A0, A1 et A2 à la référence électrique GND comme sur le schéma du montage ci-dessus, on les met au niveau logique bas, A0 = A1 = A2 =0. Les bits A3 à A6 sont fixés par le fabricant du DS1621, ici A3=1, A4=0, A5=0 et A6=1.

L'adresse du composant sur le bus est codée avec sept bits. Les bits A0, A1 et A2 sont configurables.

Ce qui donne pour l'adresse du composant : 100 1000 = 48Hex

Les bits A0, A1 et A2 pouvant être configurés matériellement, le fabricant offre la possibilité d'adresser huit DS1621 sur un même bus.

V-C - Comment envoyer des données

« Écrire » sur la ligne pour envoyer des données de l'Arduino (maître) vers un composant I^2C (esclave) requiert deux choses : l'adresse du composant qui doit être unique sur le bus, et le ou les octets de données à envoyer. Dans le cas de notre DS1621, l'adresse du capteur numérique est fixée à 48 en hexadécimal.

Par exemple, pour lancer une conversion de température, on procède en trois temps :

Wire.beginTransmission(0x48);      // adresse du DS1621

L'adresse est alors écrite sur la ligne de données SDA du bus, ce qui, en quelque sorte, va notifier le composant concerné que des données vont lui être adressées…

Wire.write(0xEE); // 0xEE est la commande Start Convert T

L'octet de données (ici, la commande Start Convert [0xEE] qui permet de lancer la conversion de température) est ensuite envoyé au DS1621 qui l'attend à bras ouverts. Les autres composants sur le bus vont bien sûr ignorer cette donnée. Notez que vous pouvez n'effectuer qu'une seule opération à la fois sur le bus. Quand l'opération d'écriture sur la ligne est terminée, il faut « couper la transmission » afin de libérer la ligne pour l'opération de lecture/écriture suivante :

Wire.endTransmission();

Pour être plus précis et si on veut s'assurer de la bonne transmission, la méthode Wire.endTransmission() peut aussi retourner un code qui indique le statut de la transmission (si égal à 0, la transmission s'est correctement déroulée).

Envoyer des données selon le protocole I^2C - niveau physique

Source : nxp.com

La transmission commence par une condition START (tandis que la ligne SCL est à l'état HIGH, la ligne SDA bascule à l'état LOW). S'ensuivent les 7 bits de l'adresse du composant (Slave Address), puis un bit R/W qui vaut ici 0 pour indiquer une opération d'écriture sur la ligne SDA. Le composant adressé doit envoyer un bit d'acquittement (ACK pour Acknowledgement, selon les spécifications le composant esclave doit abaisser la ligne SDA pour acquitter de la bonne réception). Chaque octet de donnée écrit sur la ligne par le maître doit être acquitté par l'esclave. La transmission s'arrête en finissant par une condition STOP (tandis que la ligne SCL est à l'état HIGH, la ligne SDA bascule à l'état HIGH).

Chronogramme I2C - Opération typique d'écriture sur le bus

La bibliothèque Wire, comme souvent avec les bibliothèques Arduino, vous offre une surcouche logicielle qui permet de vous affranchir des méandres bas niveau du protocole I^2C, mais il est toujours bien d'avoir un aperçu de ce qui se dissimule sous le capot.

Par exemple, si on revient au lancement d'une conversion de température du DS1621, il faut envoyer la commande Start Convert [0XEE], soit le chronogramme suivant d'après le constructeur :

Address Byte = 0x48 (bits A0, A1 et A2 mis à zéro) et Command Byte=0xEE.

En effet, la trame correspondante relevée après essai à l'oscilloscope (avec un module de décodage des trames I^2C) sur les lignes SDA et SCL donne :

On voit que le codage des bits utilisé est de type NRZ (Non Retour à Zéro), c'est-à-dire un niveau HIGH (5 V) pour coder un « 1 », et un niveau LOW (0 V) pour coder un « 0 ». Pour l'échantillonnage, le niveau HIGH ou LOW sur la ligne SDA doit être maintenu stable pendant le niveau HIGH de l'horloge de la ligne SCL.

Encodage d'un bit en I2C - image Wikimedia Commons

V-D - Comment recevoir des données

Une fois la conversion de température terminée, la valeur de température est stockée dans un format bien précis dans un registre du DS1621. L'Arduino doit donc préparer le DS1621 à pointer vers ce registre avant de requérir les deux octets correspondants.

On commence par demander au composant I^2C d'envoyer ses données en lui faisant pointer vers le registre souhaité. Pour cela, on envoie la commande Read Temperature [0xAA] :

Wire.beginTransmission(0x48);
Wire.write(0xAA); // 0xAA = commande 'Read Temperature'
Wire.endTransmission();  // condition STOP

À noter que le protocole I^2C propose une condition particulière nommée RESTART pour signifier le début d'une nouvelle trame dès la fin de la trame précédente sans passer par une condition de STOP pour libérer le bus. Comme la commande Read Temperature doit être suivie d'une opération de lecture sur le bus, on écrira plutôt :

Wire.beginTransmission(0x48);
Wire.write(0xAA); // 0xAA = commande 'Read Temperature'
Wire.endTransmission(false);  // condition RESTART

À partir de là, le composant I^2C retournera l'état de son registre sous forme d'octets de données. L'Arduino doit alors renseigner le nombre d'octets requis.

Wire.requestFrom(0x48, 2); // Deux octets sont requis

Cette instruction est immédiatement suivie par la lecture consécutive des deux octets retournés par le DS1621, par exemple :

if (2 <= Wire.available()) { // si deux octets disponibles
TemperatureMSB = Wire.read();  // lire l'octet de poids fort
TemperatureLSB = Wire.read();  // lire l'octet de poids faible
}

Il n'est pas nécessaire d'utiliser Wire.endTransmission() après la lecture sur la ligne. Une fois que les données demandées sont récupérées dans leur variable respective, il ne reste plus qu'à les traiter.

Recevoir des données selon le protocole I^2C - niveau physique

Source : nxp.com

La transmission commence par une condition START (tandis que la ligne SCL est à l'état HIGH, la ligne SDA bascule à l'état LOW). S'ensuivent 7 bits de l'adresse du composant (Slave Address), puis un bit R/W qui vaut ici 1 pour indiquer une opération de lecture sur la ligne SDA. Le composant adressé doit envoyer un bit d'acquittement (ACK pour Acknowledgement, selon les spécifications le composant esclave doit abaisser la ligne SDA pour acquitter de la bonne réception). Chaque octet de donnée retourné sur la ligne par l'esclave doit être acquitté par le maître. La transmission s'arrête en finissant par une condition STOP (tandis que la ligne SCL est à l'état HIGH, la ligne SDA bascule à l'état HIGH).

Chronogramme I2C - Opération typique de lecture sur le bus

Par exemple, pour récupérer les deux octets de poids fort et de poids faible (MSBYTE et LSBYTE) contenant la valeur de température acquise par le DS1621, le chronogramme selon le constructeur est le suivant :

Address Byte = 0x48 (A0, A1 et A2 mis à zéro), Command Byte = 0xAA (Read Temperature).

Un exemple de trame correspondante relevée à l'oscilloscope donne :

Les deux octets écrits sur la ligne par le DS1621 et récupérés grâce à une opération de lecture par l'Arduino sont 0X13 et 0x80. Ce qui correspond à une température de 19,5 °C comme nous le verrons plus tard.

V-E - Le code de démonstration

Pour cette démonstration, le programme devra faire une acquisition de température, au coup par coup, disons toutes les cinq secondes. Le résultat sera affiché dans le Terminal Série.

La structure du croquis Arduino est la suivante :

# include <Wire.h>  // La bibliothèque Wire gère l'I2C

#define A0_DS1621  0
#define A1_DS1621  0
#define A2_DS1621  0
#define ADDRESS_DS1621  (0x48 | A2_DS1621<<2 | A1_DS1621<<1 | A0_DS1621)

#define ONESHOT 1   // bit 1SHOT=1 si conversion au coup par coup
#define POL     0   // bit POL, non utilisé ici
#define NVB     0   // bit NVB, non utilisé ici
#define TLF     0   // bit TLF, non utilisé ici
#define THF     0   // bit THF, non utilisé ici

/* Configuration du registre */
#define REGISTER_CONFIG   (THF<<6 | TLF<<5 | NVB<<4 | POL<<1 | ONESHOT)

#define DONE_MASK   0x80   // Masque pour bit DONE

/* Commandes du DS1621 */
#define READ_TEMPERATURE  0xAA
#define ACCESS_CONFIG     0xAC
#define START_CONVERT     0xEE
#define STOP_CONVERT      0x22

byte endConversion = 0;
byte temperatureMSB = 0;
byte temperatureLSB = 0;
float temperature;

void setup(){          // Initialisation des ressources
Serial.begin(9600);  // Initialisation Terminal Série
Wire.begin();        // Initialisation I2C

/* Configuration du DS1621 */

} // fin setup

void loop(){ // boucle infinie

/* Lancement de la conversion */

/* Attendre la fin de la conversion */

/* Récupérer les deux octets de température */

/* Faire les calculs et afficher la température en degrés Celcius */

delay(5000);  // Attendre 5 s avant de recommencer

}

Si vous câblez différemment les broches A0, A1 et A2, il vous suffit de modifier les déclarations dans le code.

Commençons par configurer le DS1621 dans le setup :

/* Configuration du DS1621 */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(STOP_CONVERT);
Wire.endTransmission();

Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(ACCESS_CONFIG);   // Accès au registre de configuration
Wire.write(REGISTER_CONFIG); // écriture dans le registre de configuration
Wire.endTransmission();

On stoppe toute conversion en premier lieu. La configuration est non volatile et il se peut qu'une configuration précédente avec des conversions de température en continu soit encore mémorisée.

Pour configurer le DS1621 en mode de conversion « au coup par coup », il faut écrire dans son registre de configuration au format suivant :

Le mode de conversion « au coup par coup » est activé en mettant le bit 1SHOT à 1. Pour notre démonstration, les autres bits peuvent être laissés à zéro.

Dans la boucle loop(), on commence par lancer une conversion de température :

/* Lancement de la conversion */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(START_CONVERT);
Wire.endTransmission();

La conversion de température n'est pas instantanée. Le DS1621 prévient qu'elle est terminée lorsque le bit DONE de son registre de configuration est égal à 1 :

/* Attendre la fin de la conversion */
do {
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(ACCESS_CONFIG);
Wire.endTransmission(false);  // Condition RESTART
Wire.requestFrom(ADDRESS_DS1621, 1); // Un octet est requis
if (1 <= Wire.available()) endConversion = Wire.read() & DONE_MASK;
} while (!endConversion);

Après cela, on peut récupérer la valeur de la température :

/* Récupérer les deux octets de température */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(READ_TEMPERATURE);
Wire.endTransmission(false);  // Condition RESTART
Wire.requestFrom(ADDRESS_DS1621, 2); // Deux octets sont requis
if (2 <= Wire.available()) {
temperatureMSB = Wire.read();  // Octet de poids fort
temperatureLSB = Wire.read();  // Octet de poids faible
}

Les deux octets de poids fort et de poids faible récupérés représentent la température dans un format bien documenté par le constructeur :

Format de stockage de la température en degrés Celcius d'après Maxim Integrated

Le premier octet de poids fort est la partie entière de la température en degrés Celcius avec la méthode du complément à deux.

Dans cette représentation, le bit de poids fort est le bit de signe et vaut 1 pour les entiers négatifs. Pour retrouver la valeur absolue de départ, il faut alors faire l'opération : 256-x.

Par exemple, si l'octet de poids fort vaut 1110 0111, soit 231 en décimal : le bit de poids fort est à 1, c'est donc une valeur négative que l'on veut représenter. On réalise alors l'opération 256-231=25, et on en déduit la température qui est de -25 °C.

Le deuxième octet de poids faible vaut soit 0000 0000, soit 1000 0000. Dans ce deuxième cas, il y a un demi-degré à prendre en compte dans la partie fractionnaire de la température.

On peut maintenant faire les calculs :

/* Faire les calculs et afficher la température en degrés Celcius */
temperature = (float) temperatureMSB;
if (temperatureLSB & 0x80) temperature += 0.5;  // 1/2 °C à prendre en compte
if (temperatureMSB & 0x80) temperature -= 256;  // Température négative

Serial.print("Temperature = ");
Serial.print(temperature);
Serial.println(" degres Celcius");

Le code complet :

#include <Wire.h>

#define A0_DS1621  0
#define A1_DS1621  0
#define A2_DS1621  0
#define ADDRESS_DS1621  (0x48 | A2_DS1621<<2 | A1_DS1621<<1 | A0_DS1621)

#define ONESHOT 1   // bit 1SHOT=1 si conversion au coup par coup
#define POL     0   // bit POL, non utilisé ici
#define NVB     0   // bit NVB, non utilisé ici
#define TLF     0   // bit TLF, non utilisé ici
#define THF     0   // bit THF, non utilisé ici

/* Configuration du registre */
#define REGISTER_CONFIG   (THF<<6 | TLF<<5 | NVB<<4 | POL<<1 | ONESHOT)

#define DONE_MASK   0x80   // Masque pour bit DONE

/* Commandes du DS1621 */
#define READ_TEMPERATURE  0xAA
#define ACCESS_CONFIG     0xAC
#define START_CONVERT     0xEE
#define STOP_CONVERT      0x22

byte endConversion = 0;
byte temperatureMSB = 0;
byte temperatureLSB = 0;
float temperature;

void setup() {
Serial.begin(9600);  // Initialisation Terminal Série
Wire.begin();        // Initialisation I2C

/* Configuration du DS1621 */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(STOP_CONVERT);
Wire.endTransmission();

Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(ACCESS_CONFIG);   // Accès au registre de configuration
Wire.write(REGISTER_CONFIG); // écriture dans le registre de configuration
Wire.endTransmission();
}

void loop() {

/* Lancement de la conversion */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(START_CONVERT);
Wire.endTransmission();

/* Attendre la fin de la conversion */
do {
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(ACCESS_CONFIG);
Wire.endTransmission(false);  // Condition RESTART
Wire.requestFrom(ADDRESS_DS1621, 1); // Un octet est requis
if (1 <= Wire.available()) endConversion = Wire.read() & DONE_MASK;
} while (!endConversion);

/* Récupérer les deux octets de température */
Wire.beginTransmission(ADDRESS_DS1621);
Wire.write(READ_TEMPERATURE);
Wire.endTransmission(false);  // Condition RESTART
Wire.requestFrom(ADDRESS_DS1621, 2); // Deux octets sont requis
if (2 <= Wire.available()) {
temperatureMSB = Wire.read();  // Octet de poids fort
temperatureLSB = Wire.read();  // Octet de poids faible
}

/* Faire les calculs et afficher la température en degrés Celcius */
temperature = (float) temperatureMSB;
if (temperatureLSB & 0x80) temperature += 0.5;  // 1/2 °C à prendre en compte
if (temperatureMSB & 0x80) temperature -= 256;  // Température négative

Serial.print("Temperature = ");
Serial.print(temperature);
Serial.println(" degres Celcius");

delay(5000);  // Attendre 5 s avant de recommencer

}

Et le résultat dans le Terminal Série :

VI - Conclusion

La configuration d'un composant I^2C et l'accès à ses différents registres nécessitent impérativement une lecture attentive et approfondie de la documentation fournie (la fameuse datasheet), souvent en anglais. Mais nous espérons vous avoir fait découvrir les principes de fonctionnement du bus I^2C et que vous pourrez désormais interfacer plus facilement les composants utilisant ce bus dans vos projets Arduino.

Do It Yourself...

VII - Sitographie

- Références à la norme I^2C sur le site de NXP Semiconductors :

- I^2C-bus specification and user manual, Rev. 6 — 4 April 2014 User manual : référence UM10204

- I^2C Manual, Philips Semiconductors March 24, 2003 : référence AN10216

- http://www.i2c-bus.org/

- Bibliothèque Arduino de gestion du bus I^2C : Wire Library

- Datasheet DS1621 chez Maxim Integrated : DS1621 Digital Thermometer and Thermostat

- Quelques fabricants de semi-conducteurs avec un catalogue de composants I^2C bien fourni :

- Microchip

- ST Microelectronics

- Maxim Integrated

- …

VIII - Remerciements

Je remercie sevyc64, deusyss, ram-0000 et Auteur pour leur aide dans l'amélioration de cet article.

Je remercie également Claude Leloup pour sa relecture orthographique.