Arduino 15
découverte du bus I2C
Sommaire
Le bus I2C (voir pour débuter http://arduino.cc/en/Reference/Wire) permet de faire communiquer des modules ensemble. Ces modules peuvent être des actionneurs(circuit de multiplexage de 7 segments), des catpeurs (BMP085 pour pression atmosphérique, mais aussi des ARDUINO.
Ce bus comprend 2 fils : SDA pour les datas et SCL pour l'horloge (clock). Il peut admettre jusqu'à 128 esclaves, et un nombre quelconque de maîtres, la limitation est la capacité de ligne, maximum à 400pF, ce qui en fait limite la LONGUEUR de ce bus à environ 7 mètres. Le maître est le module qui initie l'échange et contrôle l'horloge, l'esclave attend des ordres et répond au sollicitations du maître.
Les maîtres ont pour adresse 0. Les esclaves de 1 à 128. Lorsqu'il y a plusieurs maîtres, ils interrogent à volonté les esclaves, chacun à leur tour. La vitesse du bus est assez élevé (100 à 400 kbits/seconde), ce qui permet des échanges assez nourris ...
Par contre, les esclaves ne peuvent pas converser entre eux : on est obligé de demander au maître de retransmettre ... C'est possible !
En pratique, il suffit de relier les broches A4 et A5 entre les 2 ARDUINO, la masse étant commune, le 5V aussi si besoin. Il faut installer aussi 2 résistances de 4,7kOhms entre ces 2 fils de bus et le +5Volts (résistances de tirage).
Les broches A4 et A5 sont d'ailleurs "dupliquées" sur les ARDUNINO UNO R3, après les broches 12,13,GND et Aref.
Certains modules I2C sont en 3,3V. Il faut alors intercaler une interface telle que celle-ci :
 | | Interface entre modules 5V et 3,3V |
On peut envisager d'avoir une ligne en 12 Volts avec ce type d'interafce. CEci pour améliorer l'immunité et peur-être la portée ?
Pour augmenter la distance, on peut interfacer avec des convertisseurs RS485.
Un ARDUINO possède une horloge précise (DS1307 ou horlge DCF-77), il veut envoyer son heure toutes les secondes à des afficheurs.
Pour le maître, partant du programme 14, j'ai ajouté les lignes suivantes pour obtenir _15_I2C_maitre.ino :
//**********************************************************
// DECLARATIONS
// *** Bus I2C ***
#include <Wire.h>
...
//**********************************************************
// INITIALISATIONS
void setup() {
...
// Bus I2C
Wire.begin(); // initialisation en maitre (A4=SDA et A5=SCL)
...
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
...
// opérations à chaque seconde
if (top_1000ms) {
top_1000ms = false;
tours_ps=tours; tours=0;
// I2C vers ARDUINO No 1
Wire.beginTransmission(0x01); // autre Arduino en 1
// préparation du message de type "Tyymmjjhhmmss"
memset (valueff,'\0',15); strcat( valueff, "T" );
i = year(); if (i >= 2000) { i-=2000; } else { i-=1900; }
dtostrf(i,2,0,valuef); strcat( valueff, valuef );
dtostrf(month(),2,0,valuef); strcat( valueff, valuef );
dtostrf(day(),2,0,valuef); strcat( valueff, valuef );
dtostrf(hour(),2,0,valuef); strcat( valueff, valuef );
dtostrf(minute(),2,0,valuef); strcat( valueff, valuef );
dtostrf(second(),2,0,valuef); strcat( valueff, valuef );
// envoi
Wire.write(valueff);
// fin de transmission
i = Wire.endTransmission();
if (i == 0) Serial << "Wire OK=" << valueff << "=" << endl;
if (i != 0) Serial << "Wire ERREUR code=" << i << endl;
}
...
Pour l'esclave, j'ai fait un simple programme qui gère un LCD, affiche en prmière ligne le message recu, et en seconde ligne le nombre de tours du CPU.
Remarquez dans le Setup() la programmation qui, sur réception, déroute vers le sous-programme "receiveEvent" :
Wire.onReceive(receiveEvent); // register event
#define version "15_I2C_esclave"
//**********************************************************
// BIBLIOTHEQUES
// incluses dans le package Arduino de base
// *** LCD *** afficheur LCD
#include <LiquidCrystal.h> // LCD
// LiquidCrystal lcd(RS, Enable, D4, D5, D6, D7)
LiquidCrystal lcd(4, 2, 6, 7, 8, 9);
// ajoutées
// *** GENERAL *** formatage des entrees sorties ASCII
#include <Streaming.h>
// *** Bus I2C ***
#include <Wire.h>
int icol;
byte c;
char bufr[15];
// *** GENERAL ***
// speedomètre = calcul de vitesse ...
unsigned long tours_ps,tours,tours_led;
#define led_a 13
//**********************************************************
// INITIALISATIONS
void setup() {
// initialize the pins
pinMode(led_a, OUTPUT);
digitalWrite(led_a,HIGH);
// définition taille du LCD :
lcd.begin(16, 2);
// annonce version
lcd.clear(); lcd.setCursor(0, 0); lcd << version;
// ligne série (pour traces)
Serial.begin(9600); // ou 9600 selon votre port COM
Serial << "Serial ok " << version << endl;
// Bus I2C
Wire.begin(0x01); // initialisation en esclave (A4=SDA et A5=SCL)
Wire.onReceive(receiveEvent); // register event
delay(2000);
lcd.clear();
} // Fin du SETUP
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
// compte-tours par seconde et led_a "heart-beat" commutée
tours++; tours_led++;
if (tours_led >= 10000) { tours_led = 0 ; digitalWrite(led_a,HIGH); } // allumage led // digitalWrite(led_a, !digitalRead(led_a)); }
if (tours_led == 250) digitalWrite(led_a, LOW); // extinction led
if (tours_led == 0) {
lcd.setCursor(0, 0); lcd << bufr;
lcd.setCursor(0, 1); lcd << tours << " " << icol << " ";
}
}
// I2C
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
while(Wire.available()) // loop through all but the last
{
char c = Wire.read(); // receive byte as a character
icol++; if (icol > 15) icol=0;
if (c=='T') icol=0;
bufr[icol]=c;
}
// int x = Wire.read(); // receive byte as an integer
// Serial.println(x); // print the integer
}
| Discussion sur le sens maître-esclave |
Dans notre idée de "station horaire et météo", on peut se demander dans quel sens attribuer le MAITRE. A priori, on pourrait penser que la station météo soit ESCLAVE, comme une "super-capteur", et que tous les utilisateurs viennent récupérer les données quand ils en ont besoin. Mais, les horloges temps réelles telles que les DS1302,6,7 et les capteurs tel le BMP085(pression-température) sont eux-mêmes esclaves. Il faut donc que notre station météo soit MAITRE, et distribue ses informations aux esclaves utilisateurs ...
Idée pour multi-esclaves de la station Météo
// Etablir une table d'adresses d'esclaves probables :
int meteo_esclave = {1,2,3,4,5}, meteo_nb=5;
// Tenter d'envoyer les données, si code=erreur mettre l'adresse en négatif
for (i=0;i<meteo_nb,i++) {
// si esclave Ok, ou 1 fois de temps en temps si pas ok
if (meteo_esclave[i] > 0 || abs(meteo_esclave[i]) == second() ) {
// envoi des datas
...
Wire.write(valueff);
// fin de gtransmission
i = Wire.endTransmission();
if (i != 0) { meteo_esclave[i] = -abs(meteo_esclave[i]); } else { meteo_esclave[i] = abs(meteo_esclave[i]); }
}
}
Photos de la page :
Dernière mise à jour : 11:40:02 18/09/2020