TCA9548A – I2C Multiplexer

Über den Beitrag

I2C Bausteine haben meistens einen Satz fester Adressen, die sich über Adresspins oder -jumper einstellen lassen. Einige, wie z. B. der Portexpander MCP23017, sind mit 8 Adressen recht üppig ausgestattet. Andere hingegen, wie z. B. der Helligkeitssensor BH1750FVI haben nur 2 Adressen zur Auswahl. Aber was, wenn ihr damit nicht auskommt? Eine sehr komfortable Lösung dieses Problems ist der I2C Multiplexer TCA9548A, den ich in diesem Beitrag vorstellen werde. Darüber hinaus zeige ich euch, wie ihr alternativ mit einem einfachen MOSFET I2C Multiplexing realisieren könnt.

Der Beitrag ist folgendermaßen gegliedert:

  • Technische Eigenschaften des TCA9548A
  • Anschluss des TCA9548A an den Mikrocontroller
  • Ansteuerung – Grundlagen
  • Verwendung eines einzelnen TCA9548A:
    • ein I2C Bauteil pro Kanal
    • mehrere I2C Bauteile mit unterschiedlichen Adressen pro Kanal
  • Verwendung mehrerer TCA9548A
  • I2C Multiplexing mit MOSFETs

Technische Eigenschaften des TCA9548A

Der TCA9548A erlaubt es euch mithilfe seiner acht Kanäle ebenso viele I2C Bauteile mit identischer Adresse an einem I2C Bus zu betreiben. Für den TCA9548A selbst könnt ihr acht Adressen nach dem folgenden Schema einstellen:

  • 1 1 1 0 A2 A1 A0
    • Ax = 0 wenn an LOW, Ax = 1 wenn an HIGH
    • Mögliche Adressen sind damit: 1110000 bis 1110111 oder 0x70 bis 0x77

Mit acht TCA9548A könntet ihr also 64 I2C Bauteile betreiben, die alle dieselbe I2C Adresse haben.

TCA9548A Modul
TCA9548A Modul

Auf der Mikrocontrollerseite (Anschlüsse SDA / SCL) sind Pull-Up Widerstände integriert, die die Spannung auf das VIN Niveau ziehen. Die acht Kanäle besitzen keine integrierten Pull-Ups. Was zunächst wie ein Nachteil aussieht, ist ein Vorteil, denn es erlaubt euch mit verschiedenen Busspannungen zu arbeiten. Wählt beispielsweise 3.3 Volt für VIN und damit auch für die I2C Leitung zum Mikrocontroller. Davon unabhängig könnt ihr dann die einzelnen Kanäle mit externen Pull-Up Widerständen auf z. B. 5 Volt hochziehen.

Hier weitere technische Eigenschaften im Überblick:

  • Spannungsversorgung: 1.65 – 5.5 Volt
  • Maximale I2C Busfrequenz: 400 kHz
  • Low-aktiver Reset Pin
  • Alle Eingänge 5V tolerant
  • Mehrere oder auch alle Kanäle können gleichzeitig aktiviert werden

Für weitere Details schaut ins technische Datenblatt.

Ihr bekommt den TCA9548A als Modul für wenige Euro in Online-Shops wie Amazon oder AliExpress.

Anschluss des TCA9548A an den Mikrocontroller

Im folgenden Schema seht ihr am Beispiel eines Arduino UNO, wie ihr den TCA9548A anschließt. Der besseren Übersicht halber habe ich nur ein einziges I2C Bauteil abgebildet.

Das HIGH Niveau der Arduino UNO I2C-Leitungen liegt bei 5 Volt. Entsprechend sollte auch VIN mit 5 Volt versorgt werden. Die Adresspins sind in diesem Beispiel unverbunden und damit auf GND Niveau. Die Adresse ist damit 0x70. Auf der Seite des I2C „Sklaven“ Seite werden Pull-Ups benötigt, falls das angeschlossene Bauteil sie nicht mitbringt.  

Ansteuerung – Grundlagen

Der TCA9548A besitzt nur ein einziges, durch euch beschreibbares Register, nämlich das Kontrollregister:

Kontrollregister des TCA9548A
Kontrollregister des TCA9548A

Eine „1“ bedeutet, dass der Kanal aktiv ist, eine „0“ bedeutet, dass der Kanal inaktiv ist. Schreibt ihr beispielsweise eine 151 in das Register, ist das binär 10010111, sprich die Kanäle 0,1,2,4 und 7 sind aktiv. Um bestimmte Bits des Registers zu setzen, verwendet ihr am besten binäre Operationen:

Control\; Register=151 = (1<<7) |(1<<4)|(1<<2)|(1<<1)|(1<<0)

In den Beispielen dieses Beitrages ist aber immer nur jeweils ein Kanal aktiv. Der folgende Minisketch öffnet Kanal 0, indem er das Bit 0 des Kontrollregisters setzt:

#include<Wire.h>
#define TCA9548A_I2C_ADDRESS  0x70
#define TCA9548A_CHANNEL_0    0

void setup() {
  Wire.begin();
  setTCAChannel(TCA9548A_CHANNEL_0);
}

void loop() { 
}

void setTCAChannel(byte i){
  Wire.beginTransmission(TCA9548A_I2C_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

 

Zum Einstieg könnt ihr Folgendes probieren: Nehmt euer Lieblings-I2C Bauteil und baut die obige Schaltung nach. Dann ladet einen I2C Scanner Sketch hoch. So etwas findet ihr z.B. hier. Der Scanner sollte die Adresse 0x70 finden. Dann ladet ihr den TCA9548A_set_channel Sketch hoch, ohne den TCA9548A zwischendurch vom Strom zu trennen. Dann ladet ihr wieder den Scanner Sketch hoch. Er sollte dann die 0x70 und die Adresse eures I2C Bauteils anzeigen.

Im Grunde ist damit schon alles erklärt. Ihr müsst lediglich die Kanäle gezielt an- und ausschalten, um das jeweils gewünschte I2C Bauteil anzusprechen. Und wenn ihr eine „0“ in das Kontrollregister schreibt, sind alle Kanäle deaktiviert. In der Praxis können die Dinge aber schnell unübersichtlich werden, insbesondere wenn jedes I2C Bauteil als Objekt angelegt wird.

Einen einzelnen TCA9548A verwenden

Ein TCA9548A mit einem I2C Bauteil pro Kanal

In den folgenden Beispielen verwende ich den A/D Wandler ADS1115 als I2C Baustein. Ihr müsst euch nicht mit den Details des ADS1115 auseinandersetzen, um den Beitrag nachvollziehen zu können. Nehmt einfach zur Kenntnis, dass jeder einzelne ADS1115 initialisiert werden muss und ein paar Einstellungen benötigt (setVoltageRange_mV, setCompareChannels und setMeasureMode).

Die Beschaltung ist hinsichtlich der I2C Leitungen wohl keine Überraschung. Die orangefarbenen Leitungen und die Widerstände dienen nur dazu, ein paar unterschiedliche Spannungen am Eingang A0 messen zu können. Da die Adresspins der ADS1115 Module unverbunden sind, ist die I2C Adresse für alle Module 0x48.

TCA9548A - 4 I2C Kanäle, 4 I2C Geräte
TCA9548A – 4 I2C Kanäle, 4 I2C Geräte
TCA9548: und so sah der Aufbau real aus.
Und so sah es in der Realität aus

Im zugehörigen Sketch wird für jeden ADS1115 ein individuelles Objekt erzeugt. Es folgt die Initialisierung und die Einstellung der Parameter. In der Hauptschleife werden die Messwerte der ADS1115 Module abgefragt und ausgegeben.

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS 0x48
#define TCA_I2C_ADDRESS    0x70

ADS1115_WE adc_0(AD1115_I2C_ADDRESS);
ADS1115_WE adc_1(AD1115_I2C_ADDRESS);
ADS1115_WE adc_2(AD1115_I2C_ADDRESS);
ADS1115_WE adc_3(AD1115_I2C_ADDRESS);

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  setTCAChannel(0);
  if(!adc_0.init()){
    Serial.print("ADS1115 No 0 not connected!");
  }
  adc_0.setVoltageRange_mV(ADS1115_RANGE_6144); // setting voltage range
  adc_0.setCompareChannels(ADS1115_COMP_0_GND); // setting adc channel
  adc_0.setMeasureMode(ADS1115_CONTINUOUS); // setting adc mode

  setTCAChannel(1);
  if(!adc_1.init()){
      Serial.print("ADS1115 No 1 not connected!");
  }
  adc_1.setVoltageRange_mV(ADS1115_RANGE_6144); // setting parameters
  adc_1.setCompareChannels(ADS1115_COMP_0_GND);
  adc_1.setMeasureMode(ADS1115_CONTINUOUS); 

  setTCAChannel(2);
  if(!adc_2.init()){
      Serial.print("ADS1115 No 2 not connected!");
  }
  adc_2.setVoltageRange_mV(ADS1115_RANGE_6144); // setting parameters
  adc_2.setCompareChannels(ADS1115_COMP_0_GND);
  adc_2.setMeasureMode(ADS1115_CONTINUOUS); 

  setTCAChannel(3);
  if(!adc_3.init()){
    Serial.print("ADS1115 No 3 not connected!");
  }
  adc_3.setVoltageRange_mV(ADS1115_RANGE_6144); // setting parameters
  adc_3.setCompareChannels(ADS1115_COMP_0_GND);
  adc_3.setMeasureMode(ADS1115_CONTINUOUS); 
}

void loop() {
  float voltage = 0.0;
  
  setTCAChannel(0);
  voltage = adc_0.getResult_V();
  Serial.print("Voltage [V], ADS1115 No 0: ");
  Serial.println(voltage);
  
  setTCAChannel(1);
  voltage = adc_1.getResult_V();
  Serial.print("Voltage [V], ADS1115 No 1: ");
  Serial.println(voltage);
  
  setTCAChannel(2);
  voltage = adc_2.getResult_V();
  Serial.print("Voltage [V], ADS1115 No 2: ");
  Serial.println(voltage);
  
  setTCAChannel(3);
  voltage = adc_3.getResult_V();
  Serial.print("Voltage [V], ADS1115 No 3: ");
  Serial.println(voltage);
  
  Serial.println("****************************");  
  delay(1000);
}

void setTCAChannel(byte i){
  Wire.beginTransmission(TCA_I2C_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

 

Ausgabe der Sketche

Die Ausgabe dieses und aller folgenden Sketche ist gleich:

TCA9548A Beispielsketch:
Ausgabe TCA9548A_4_channels
Ausgabe TCA9548A_4_channels

Einige Vereinfachungen

Mit dem letzten Sketch wollte ich das Prinzip verdeutlichen. Der Code lässt sich noch deutlich kürzen. In der folgenden, vereinfachten Version habe ich die ADS1115 Objekte als Array definiert und mehrfach verwendeten Code in Funktionen ausgelagert. 

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS 0x48
#define TCA_I2C_ADDRESS    0x70

ADS1115_WE adc[4];

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  for(int i=0; i<4; i++){
    adc[i] =  ADS1115_WE(AD1115_I2C_ADDRESS);
    setTCAChannel(i);
    setupAdc(i);
  }
}

void loop() {
  float voltage = 0.0;
  
  for(int i=0; i<4; i++){
    setTCAChannel(i);
    voltage = adc[i].getResult_V();
    Serial.print("Voltage [V], ADS1115 No ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(voltage);
  }
  Serial.println("****************************");  
  delay(1000);
}

void setTCAChannel(byte i){
  Wire.beginTransmission(TCA_I2C_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

void setupAdc(byte i){
  if(!adc[i].init()){
    Serial.print("ADS1115 No ");
    Serial.print(i);
    Serial.println(" not connected!");
  }
  adc[i].setVoltageRange_mV(ADS1115_RANGE_6144);
  adc[i].setCompareChannels(ADS1115_COMP_0_GND);
  adc[i].setMeasureMode(ADS1115_CONTINUOUS); 
}

 

Weitere Vereinfachung: nur ein Objekt

Vielleicht ist dem einen oder anderen aufgefallen, dass alle Moduleinstellungen gleich sind. Es ist deswegen nicht notwendig, für jedes ADS1115 Modul ein eigenes Objekt zu erzeugen. Dadurch wird der Code noch einmal einfacher. Wenn die I2C Bauteile individuelle Einstellungen (Kalibrierfaktoren o. ä.) benötigen, ist diese Vereinfachung so nicht möglich.

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS 0x48
#define TCA_I2C_ADDRESS    0x70

ADS1115_WE adc(AD1115_I2C_ADDRESS);

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  for(int i=0; i<4; i++){
    setTCAChannel(i);
    setupAdc(i);
  }
}

void loop() {
  float voltage = 0.0;
  
  for(int i=0; i<4; i++){
    setTCAChannel(i);
    voltage = adc.getResult_V();
    Serial.print("Voltage [V], ADS1115 No ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(voltage);
  }
  Serial.println("****************************");  
  delay(1000);
}

void setTCAChannel(byte i){
  Wire.beginTransmission(TCA_I2C_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

void setupAdc(byte i){
  if(!adc.init()){
    Serial.print("ADS1115 No ");
    Serial.print(i);
    Serial.println(" not connected!");
  }
  adc.setVoltageRange_mV(ADS1115_RANGE_6144);
  adc.setCompareChannels(ADS1115_COMP_0_GND);
  adc.setMeasureMode(ADS1115_CONTINUOUS); 
}

 

Ein TCA9548A mit mehreren I2C Geräten pro Kanal

Falls ihr mehr als acht I2C Bauteile ansteuern wollt und diese eine Einstellung der I2C Adresse zulassen, könnt ihr mehrere I2C Geräte pro Kanal benutzen. Der ADS1115 lässt die Einstellung vier verschiedener Adressen zu. Damit könntet ihr problemlos 32 ADS1115 Module mit einem einzigen TCA9548A betreiben. Ich zeige das Prinzip anhand von vier ADS1115 Modulen, die auf zwei Kanäle verteilt sind. Hier zunächst die Schaltung:

TCA9548A - 2 I2C Kanäle, 4 I2C Geräte
TCA9548A – 2 I2C Kanäle, 4 I2C Geräte

Beachtet, dass die Adresspins der ADCs 1 und 3 mit VCC verbunden sind. Deren Adresse ändert sich damit auf 0x49. Da die Einstellungen für die ADS1115 bis auf die I2C Adresse identisch sind, gäbe es auch hier Potenzial für weitere Vereinfachungen.

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS_A  0x48
#define AD1115_I2C_ADDRESS_B  0x49
#define TCA_I2C_ADDRESS       0x70

ADS1115_WE adc[4];

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  setTCAChannel(0);
  adc[0] =  ADS1115_WE(AD1115_I2C_ADDRESS_A);
  setupAdc(0);
  adc[1] =  ADS1115_WE(AD1115_I2C_ADDRESS_B);
  setupAdc(1);
  
  setTCAChannel(1);
  adc[2] =  ADS1115_WE(AD1115_I2C_ADDRESS_A);
  setupAdc(2);
  adc[3] =  ADS1115_WE(AD1115_I2C_ADDRESS_B);
  setupAdc(3);
}

void loop() {
  float voltage = 0.0;
  
  for(int i=0; i<4; i++){
    if(i<2){
      setTCAChannel(0);
    }
    else{
      setTCAChannel(1);
    }
    voltage = adc[i].getResult_V();
    Serial.print("Voltage [V], ADS1115 No ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(voltage);
  }
  Serial.println("****************************");  
  delay(1000);
}

void setTCAChannel(byte i){
  Wire.beginTransmission(TCA_I2C_ADDRESS);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

void setupAdc(byte i){
  if(!adc[i].init()){
    Serial.print("ADS1115 No ");
    Serial.print(i);
    Serial.println(" not connected!");
  }
  adc[i].setVoltageRange_mV(ADS1115_RANGE_6144);
  adc[i].setCompareChannels(ADS1115_COMP_0_GND);
  adc[i].setMeasureMode(ADS1115_CONTINUOUS); 
}

 

Mehrere TCA9548A verwenden

Es ist auch nicht schwierig, mehrere TCA9548A zu steuern. In meinem Beispiel dazu verwende ich zwei TCA9548A, an denen jeweils zwei ADS1115 Module in getrennten Kanälen hängen.

2 TCA9548A - je 2 I2C Kanäle
2 TCA9548A – je 2 I2C Kanäle

Bei Verwendung mehrerer TCA9548A müsst ihr darauf achten, dass immer nur einer von ihnen einen offenen Kanal hat. Ich habe den Sketch dazu recht allgemein geschrieben, sodass er mit geringen Anpassungen auf eine größere Anzahl an TCA9548A Module und I2C Bauteile übertragbar ist.

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS   0x48
byte tcaI2CAddress[] = {0x70,0x71};
byte numberOfTCAs = 2;
byte numberOfDevicesPerTCA = 2;
const int numberOfDevices = 4;

ADS1115_WE adc[numberOfDevices];

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  for(int i=0; i<numberOfDevices; i++){
    adc[i] =  ADS1115_WE(AD1115_I2C_ADDRESS);
    setupAdc(i);
  }
}

void loop() {
  float voltage = 0.0;
  
  for(int i=0; i<numberOfDevices; i++){
    byte tca = setTCAAndChannel(i);
    voltage = adc[i].getResult_V();
    Serial.print("Voltage [V], ADS1115 No ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(voltage);
    disableTCA(tca);
  }
  Serial.println("****************************");  
  delay(1000);
}

byte setTCAAndChannel(byte i){
  byte tca = i/numberOfTCAs;
  byte channel = i%numberOfDevicesPerTCA;
  
  Wire.beginTransmission(tcaI2CAddress[tca]);
  Wire.write(1 << channel);
  Wire.endTransmission();

  return tca;
}

void disableTCA(byte tca){
  Wire.beginTransmission(tcaI2CAddress[tca]);
  Wire.write(0);
  Wire.endTransmission();  
}

void setupAdc(byte i){
  byte tca = setTCAAndChannel(i);
  
  if(!adc[i].init()){
    Serial.print("ADS1115 No ");
    Serial.print(i);
    Serial.println(" not connected!");
  }
  adc[i].setVoltageRange_mV(ADS1115_RANGE_6144);
  adc[i].setCompareChannels(ADS1115_COMP_0_GND);
  adc[i].setMeasureMode(ADS1115_CONTINUOUS);
  disableTCA(tca); 
}

 

Jetzt könnte man noch einen Schritt weiter gehen und mehrere TCA9548A, mehrere Kanäle und mehrere I2C Geräte pro Kanal verwenden. Ich spare mir das jedoch. Wenn ihr das Grundprinzip verstanden habt, sollte das kein Problem sein.

I2C Multiplexing mit MOSFETs

2N7000 MOSFET

Bei der Beschäftigung mit dem TCA9548A kam mir der Gedanke, ob man eine I2C Leitung nicht auch auf anderem Wege an- und ausschalten kann. Genauer gesagt geht es darum, die SDA Leitung, also die Datenübertragung gezielt zu unterbrechen. Zuerst kamen mir dabei Transistoren als Schalter in den Sinn, das funktionierte aber nicht. Was hingegen zum Erfolg führte, war der Einsatz der nahen Verwandten, den MOSFETs (Metal-Oxide Semiconductor Field-Effect Transistor).

Grundlagen zur Funktion von MOSFETs findet ihr hier. An dieser Stelle nur soviel: Der MOSFET hat drei Anschlüsse, nämlich Drain, Source und Gate. Den Stromfluss von Drain nach Source steuert ihr über die Spannung an Gate. Es gibt n- und p-Kanal MOSFETs. Bei einem n-Kanal MOSFET, wie dem von mir verwendeten IRF540, müsst ihr eine gewisse positive Mindestspannung an Gate anlegen, damit er öffnet. So funktioniert das Teil dann wie ein Schalter. Also vom Prinzip her ähnlich wie bei den Transistoren.

Wenn der MOSFET sperrt, dann muss die SDA Leitung trotzdem auf Spannung gehalten werden. Jedes Absenken würde als Versuch einer Datenübertragung gewertet. Deswegen sind Pull-Up Widerstände erforderlich. Hier eine schematische Darstellung:

MOSFETs als I2C Multiplexer: I/O 1 und 2 schalten die MOSFETs
MOSFETs als I2C Multiplexer: I/O 1 und 2 schalten die MOSFETs

Ich habe mir als Ziel gesetzt, vier ADS1115 mit zwei MOSFETs zu steuern. Dazu habe ich die folgende Schaltung verwendet:

Die TCA9548A Alternative: Mosfets
Die TCA9548A Alternative: Mosfets

Ich habe zunächst mit dem I2C Scanner und einem einzelnen MOSFET probiert, ob ich auf die hinter dem MOSFET liegenden ADS1115 Module zugreifen kann. Als MOSFET hatte ich einen IRF540 ausgewählt. Dann habe ich mich gewundert, dass das Ganze bei 100 kHz erst funktionierte als ich einen zusätzlichen 10 kOhm Pull-Up installierte. Bei 400 kHz musste ich zu 2.2 kOhm Pull-Ups greifen. Grund dafür ist die Kapazität des MOSFETs – darauf hat mich ein freundlicher Leser aufmerksam gemacht. Eine kleine Überschlagsrechnung: Der IRF540 hat bei 5 Volt eine Ausgangskapazität von ca. 1200 pF = 1.2×10-9 F. Die Ladezeit τ eines Kondensators hängt von seiner Kapazität C und dem Widerstand R (hier 10 kOhm) ab:

\tau = C\cdot R=1.2\cdot10^{-9}\cdot 10^4=1.2\cdot10^{-5}\;[\text{s}]

Der Kondensator ist nach 1 τ auf 63 % geladen. Bei 100 kHz Frequenz muss das HIGH Niveau nach einer Absenkung nach 10-5 s wieder erreicht sein. Passt also nicht! Mit 2 parallelen Pull-Ups von 10 kOhm ging es gerade eben. Am Oszilloskop war das schön zu sehen. Hier zunächst ohne MOSFETs:

I2C-Signale ohne MOSFET
I2C-Signale ohne MOSFET

Und so mit IRF540 und zwei parallelen 10 kOhm Pull-Ups:

I2C-Signale mit MOSFET
I2C-Signale mit IRF540 MOSFET und zwei 10 kOhm Pull-Ups

Nehmt also kleine MOSFETs wie zum Beispiel einen 2N7000 oder einen BS170. Die haben einen Bruchteil der Ausgangskapazität eines IRF540.

Mit Mikrocontrollern, die auf 3.3 Volt laufen, werdet ihr wahrscheinlich Schwierigkeiten bekommen, da es mit der notwendigen Gatespannung eng wird.

Nun zum Sketch: Da in diesem Beispiel zwei ADS1115 auf einer Leitung liegen, müssen wieder zwei unterschiedliche I2C Adressen verwendet werden. Ansonsten gibt es eigentlich nichts Neues, außer dass die Kanäle über die Steuerpins 8 und 9 geschaltet werden.

#include<ADS1115_WE.h> 
#include<Wire.h>
#define AD1115_I2C_ADDRESS_A  0x48
#define AD1115_I2C_ADDRESS_B  0x49
#define I2C_CHANNEL_0_PIN     8
#define I2C_CHANNEL_1_PIN     9

ADS1115_WE adc[4];

void setup() {
  Wire.begin();
  Serial.begin(9600);
  pinMode(I2C_CHANNEL_0_PIN, OUTPUT);
  pinMode(I2C_CHANNEL_1_PIN, OUTPUT);
  
  setI2CChannel(0);
  adc[0] =  ADS1115_WE(AD1115_I2C_ADDRESS_A);
  setupAdc(0);
  adc[1] =  ADS1115_WE(AD1115_I2C_ADDRESS_B);
  setupAdc(1);
  
  setI2CChannel(1);
  adc[2] =  ADS1115_WE(AD1115_I2C_ADDRESS_A);
  setupAdc(2);
  adc[3] =  ADS1115_WE(AD1115_I2C_ADDRESS_B);
  setupAdc(3);
}

void loop() {
  float voltage = 0.0;
  
  for(int i=0; i<4; i++){
    if(i<2){
      setI2CChannel(0);
    }
    else{
      setI2CChannel(1);
    }
    voltage = adc[i].getResult_V();
    Serial.print("Voltage [V], ADS1115 No ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(voltage);
  }
  Serial.println("****************************");  
  delay(1000);
}

void setI2CChannel(byte i){
  if(i==0){
    digitalWrite(I2C_CHANNEL_1_PIN, LOW);
    digitalWrite(I2C_CHANNEL_0_PIN, HIGH);  
  }
  else if(i==1){
    digitalWrite(I2C_CHANNEL_0_PIN, LOW);
    digitalWrite(I2C_CHANNEL_1_PIN, HIGH);  
  }
}

void setupAdc(byte i){
  if(!adc[i].init()){
      Serial.print("ADS1115 No ");
      Serial.print(i);
      Serial.println(" not connected!");
    }
    adc[i].setVoltageRange_mV(ADS1115_RANGE_6144);
    adc[i].setCompareChannels(ADS1115_COMP_0_GND);
    adc[i].setMeasureMode(ADS1115_CONTINUOUS); 
}

 

Auch hier gäbe es wieder Potenzial zur Vereinfachung, denn zumindest zwei ADS1115 sind in diesem Beispiel identisch.

Danksagung

Die Fritzing Bauteile ADS1115 und TCA9546A stammen von der Firma Adafruit. Wenn ihr auch mal Fritzing Bauteile sucht, dann habt ihr große Chancen hier fündig zu werden.

5 thoughts on “TCA9548A – I2C Multiplexer

  1. Hi,

    ich schätze Deine Seite sehr, auch vielen Dank für die Anregungen durch diesen Beitrag!
    Das „merkwürdige Verhalten“ mit den MOSFETs kommt durch das einbringen von großen Kapazitäten in die SDA & SCL Leitungen zustande, wie im Datenblatt des IRF540 gut zu lesen. In Verbindung mit diesem PDF: https://www.ti.com/lit/an/slva689/slva689.pdf? wird es dann klar ersichtlich. Also am besten nur „kleine“ MOSFETs verwenden.

    Gruß André

    1. Vielen Dank nochmal – ich habe es mit kleineren MOSFETs probiert und es funktioniert wunderbar ohne zusätzliche Pull-Ups. Den Beitrag habe ich angepasst.

      1. Hi,

        mir ist klar, daß nicht jeder SOT-23 löten will, oder kann, aber ich würde die MOSFETs nehmen, die man auch für LevelShifter benutzt, BSS138. Diese funktionieren bis 2,5V garantiert. SOT-23 kann man problemlos auf Lochraster löten, selbst schon mehrfach gemacht.

        Gruß André

        1. Ist natürlich kompakter. Bezüglich der Kapazität nimmt sich das zumindest mit dem 2N7000 nicht viel. Danke für noch einen nützlichen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.