mardi 9 février 2021

TCA9548A : multiplexer le bus I2C

 


TCA9548A : multiplexer le bus I2C

 

Comme vous le savez probablement le bus I2C permet de dialoguer avec des composants à l'aide de seulement deux fils et on peut théoriquement en brancher un bon nombre :

Sur ce schéma que j'ai déjà publié sur ce blog je compare 7 capteurs météo. Le composant avec lequel on désire dialoguer est sélectionné par le logiciel.

Les composants I2C possèdent souvent une ou plusieurs broches de sélection d'adresse. Par exemple le capteur de température et humidité HDC2080 possède une broche ADD que l'on peut relier au 3.3V pour changer son adresse I2C (0x41 au lieu de 0x40 si elle est reliée à GND).

Mais certains composants ne disposent que d'une seule adresse (HTU21D, SI7021, afficheurs OLED I2C, etc.).

Le cas doit se produire plutôt rarement, mais que faire lorsque l'on est dans l'obligation d'utiliser deux composants I2C ayant la même adresse ?

Un autre cas peut se produire : certains composants mal conçus cohabitent  difficilement avec d'autres sur le bus.

Le TCA9548 permet de multiplexer 8 bus I2C. Il a les caractéristiques suivantes :

  • 8 bus esclaves
  • alimentation 1.65V - 5.5V
  • 3 broches de sélection d'adresse

C'est un circuit CMS 24 pins, mais il est facile de trouver des modules "breakboard" équipés de ce circuit (voir image ci-dessus).

1. Le schéma

Sur ce schéma le TCA9548 est alimenté sous 5V (une alimentation sous 3.3V serait parfaitement possible) et il contrôle deux modules I2C également alimentés sous 5V.

Les deux modules I2C sont branchés sur les ports I2C 2 et 3 du TCA9548.

Les broches d'adresse A0, A1 et A2 sont reliées à GND. L'adresse I2C du TCA9548 sera donc 0x70. D'autres choix sont possibles, entre 0x70 et 0x77.

2. La librairie

Il existe quelques librairies. Celle que je vais utiliser est la suivante :

https://github.com/sparkfun/SparkFun_I2C_Mux_Arduino_Library

Elle est installable depuis l'IDE ARDUINO.

3. Le code

3.1. Capteurs SHT31D

J'ai câblé un petit montage avec une carte ARDUINO NANO, le TCA9548 et 2 capteurs SHT31D A et B. L'alimentation des 3 modules est en 3.3V. Cela fonctionnerait de la même façon sous 5V, le SHT31D supportant de 2.4V à 5.5V. 

La librairie Adafruit_SHT31 est nécessaire (installable depuis l'IDE). 

Voici le code :

#include <Wire.h>
#include <SparkFun_I2C_Mux_Arduino_Library.h>
#include <Adafruit_SHT31.h>

QWIICMUX myMux;
Adafruit_SHT31 sht31A = Adafruit_SHT31();
Adafruit_SHT31 sht31B = Adafruit_SHT31();

void setup()
{
  Serial.begin(115200);
  Wire.begin();

  if (myMux.begin() == false) {
    Serial.println("Mux not detected. Freezing...");
    while (1);
  }
  Serial.println("Mux detected");

  byte currentPortNumber = myMux.getPort();
  Serial.print("CurrentPort: ");
  Serial.println(currentPortNumber);

   // initialisation SHT31 A
  myMux.setPort(2);
  if (!sht31A.begin(0x44)) {
    Serial.println("Couldn't find sensor A");
    while (1) delay(1);
  }
  // initialisation SHT31 B
  myMux.setPort(3);
  if (!sht31B.begin(0x44)) {
    Serial.println("Couldn't find sensor B");
    while (1) delay(1);
  }
}


void loop()
{
  myMux.setPort(2);
  float temp = sht31A.readTemperature();
  float hum = sht31A.readHumidity();
  Serial.print("SHT31 A temperature = ");
  Serial.print(temp);
  Serial.println(" *C");
  Serial.print("SHT31 A humidity = ");
  Serial.print(hum);
  Serial.println(" %\n");
  myMux.setPort(3);
  temp = sht31B.readTemperature();
  hum = sht31B.readHumidity();
  Serial.print("SHT31 B temperature = ");
  Serial.print(temp);
  Serial.println(" *C");
  Serial.print("SHT31 B humidity = ");
  Serial.print(hum);
  Serial.println(" %\n");
  delay(10000);
}

Mon doigt est posé sur le capteur A. Voici ce qu'affiche le sketch :

Mux detected
CurrentPort: 255
SHT31 A temperature = 30.34 *C
SHT31 A humidity = 69.97 %
 
SHT31 B temperature = 22.81 *C
SHT31 B humidity = 41.96 %

Les deux modules SHT31 A et B sont branchés sur les ports I2C 2 et 3 du TCA9548.

A chaque fois que l'on veut communiquer avec le module A il suffit d'appeler :

  myMux.setPort(2);

A chaque fois que l'on veut communiquer avec le module B il suffit d'appeler :

  myMux.setPort(3);

3.2. Afficheurs OLED

On peut aisément faire la même chose avec 2 petits afficheurs OLED SSD1306, pour afficher des informations sur l'un ou sur l'autre.

Ici encore l'alimentation du TCA9548 et des écrans est en 3.3V.

La librairie de Bill Greiman SSD1306Ascii est nécessaire (installable depuis l'IDE).

Voici le code :

#include <Wire.h>
#include <SparkFun_I2C_Mux_Arduino_Library.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

QWIICMUX myMux;
SSD1306AsciiAvrI2c displayA;
SSD1306AsciiAvrI2c displayB;

void setup()
{
  Serial.begin(115200);
  Wire.begin();
  pinMode(13, OUTPUT);

  if (myMux.begin() == false) {
    Serial.println("Mux not detected. Freezing...");
    while (1);
  }
  Serial.println("Mux detected");

  byte currentPortNumber = myMux.getPort();
  Serial.print("CurrentPort: ");
  Serial.println(currentPortNumber);

  delay(2000); // nécessaire à la mise sous tension


  // initialisation OLED A
  myMux.setPort(2);
  Serial.println("initialisation OLED A");
  displayA.begin(&Adafruit128x32, 0x3C);
  displayA.setFont(System5x7);
  displayA.set1X();
  displayA.setCursor(0, 0);
  displayA.clearToEOL();
  displayA.print("INITIALIZED");
  // initialisation OLED B
  myMux.setPort(3);
  Serial.println("initialisation OLED B");
  displayB.begin(&Adafruit128x32, 0x3C);
  displayB.setFont(System5x7);
  displayB.set1X();
  displayB.setCursor(0, 0);
  displayB.clearToEOL();
  displayB.print("INITIALIZED");
  delay(500);
  displayA.set2X();
  displayB.set2X();
  digitalWrite(13, HIGH);
}

void loop()
{
  static int toggle = -1;

  myMux.setPort(2);
  displayA.setCursor(0, 0);
  displayA.clearToEOL();
  if (toggle != HIGH) {
    displayA.print("SCREEEN A");
  }
  myMux.setPort(3);
  displayB.setCursor(0, 0);
  displayB.clearToEOL();
  if (toggle != LOW) {
    displayB.print("SCREEEN B");
  }
  delay(1000);
  toggle = toggle == LOW ? HIGH : LOW;
}

Un délai d'au moins une seconde est nécessaire entre l'initialisation du TCA9548 et celle des écrans, si l'on met sous tension le montage. Lors d'un reset ce délai n'est pas utile. Je n'ai pas d'explication.

3.3. Adresse du TCA9548

A noter : si l'on désire utiliser une adresse alternative du TCA9548 il suffit de passer cette adresse à la méthode begin() :

  if (myMux.begin(0x71) == false) {
    Serial.println("Mux not detected. Freezing...");
    while (1);
  }

Dans ce cas (0x71) il faudra relier la broche A0 au +5V, ou 3.3V, suivant le cas.

4. Conclusion

Voici un petit composant bien pratique lorsque l'on est confronté à une situation de conflit sur un bus I2C.


Cordialement

Henri


9 commentaires:

  1. Merci Henri pour cet article, je vais essayer de mettre en application avec 2 écrans 128x32 ( adresse i2c unique) que je possède pour un affichage d'infos de torque moteur dans mon simulateur d'avion à partir d'une Nano
    Bernard

    RépondreSupprimer
  2. Si les deux afficheurs sont éloignés l'un de l'autre, ou sur deux faces d'un boîtier, c'est intéressant, sinon il existe des 128x64.

    RépondreSupprimer
  3. Bonjour et merci pour ce petit tuto , j aimerais savoir si la démarche est la même avec deux matrix-bicolore 8x8 ayant la même adresse? car j'ai testé a ma manière en suivant un peu la tient mais aucun résultat.
    Pavrick

    RépondreSupprimer
    Réponses
    1. matrix-bicolore 8x8 : c'est assez imprécis comme description. Que dire ?
      Si ce sont bien des matrices I2C du genre Adafruit LED Backpack, il n'y a pas de raison que cela ne fonctionne pas, avec la librairie qui convient à ces matrices.

      Supprimer
    2. Effectivement il s'agit bien de ces matrice. voici mon code au niveau du setup je pense que c'est la ou viens le problème vu que' au niveau du terminal il y'a ecrit MAT NOT DETECTED .j'aurais vraiment besoin d'une aide de votre part pour m identifier ce qui ne va pas.Merci
      #include
      #include
      #include "Adafruit_LEDBackpack.h"
      #include

      QWIICMUX TCA;

      Adafruit_BicolorMatrix matrixG = Adafruit_BicolorMatrix();
      Adafruit_BicolorMatrix matrixD = Adafruit_BicolorMatrix();

      void setup(void) {
      Serial.begin(9600);
      Wire.begin();

      if (TCA.begin(0x74) == false) {
      Serial.println("Mat not detected\n");
      while (1);
      }
      Serial.println("8x8 LED Matrix Test");
      TCA.setPort(2);
      if (!matrixG.begin(0x70)) {
      Serial.println("Couldn't find matrixG");
      while (1) delay(1);
      }
      TCA.setPort(6);
      if (!matrixD.begin(0x70)) {
      Serial.println("Couldn't find matrixG");
      while (1) delay(1);
      }

      }

      Supprimer
    3. voici les librairies utilisées
      #include < Wire.h >
      #include < Adafruit_GFX.h >
      #include "Adafruit_LEDBackpack.h"
      #include < SparkFun_I2C_Mux_Arduino_Library.h >

      Supprimer
    4. Je conseillerais d'utiliser le sketch i2c-scanner (chercher sur google), afin de déterminer la vraie adresses I2C du TCA9548.

      Supprimer
    5. merci beaucoup pour votre aide finalement sa fonctionne il s'agit bien de trouver la bonne adresse encore merci

      Supprimer
  4. sans oublier que j'utilise un Arduino méga 2560

    RépondreSupprimer