TCA9548A I2C Multiplexer

Hardware

Beschreibung

I2C erlaubt nur eindeutige Adressen pro Bus. In den meisten Fällen reicht das auch völlig aus, da man bei vielen I2C-Geräten die Adresse per Jumper ändern kann. Bei einigen geht es gar nicht oder nicht im nötigen Umfang. Z.B. unterstützt der Lichtsensor nur BH1750FVI nur 2 Adressen bei mehr als 2 Räumen müsste man sich eine Alternative suchen.

Hier kommt nun der Multiplexer ins Spiel. Der TCA9548A unterstützt 8 I2C-Bus-Systeme. Der Multiplexer erlaubt eine Adressierung per Jumper zwischen 0x70 und 0x77. D.h. man könnte 8 solcher Multiplexer mit jeweils 8 Bussystemen verbauen. Sprich 256 Module wie Sensoren o.ä. mit gleicher Adresse könnte man so problemlos an den Master-I2C hängen.

Ich habe ihn für 7,50 € zuzüglich Versand bestellt. Die Steckbrücke liegt separat dabei und müsste bei Bedarf aufgelötet werden.

Programierung

Die Programmierung ist denkbar einfach. Man sagt dem Multiplexer welchen Bus man ansprechen möchte und kann wie gewohnt die Geräte auf dem Bus nutzen. Sprich man schickt an den Multiplexer ein Byte, je nach dem welches Bit gesetzt ist, dessen Bus aktiviert man.

Bei mir z.B. ist der I2C-Master (Banana Pi) am Bus 2. Beim Multiplexer ließ ich alle Adress-Jumper offen, damit hat er die Adresse 0x70. Mittels i2ctools kann man mit dem Befehl i2cset 0x70 0x80 den Multiplexer mitteilen, dann man den Sub-BUS an den Pins SD7/SC7 nutzen möchte. Danach werden alle Lese- und Schreib-Operationen an und von dem Bus weitergereicht.

Testaufbau

Schaltung

Grau ist jede Theorie. Um zu prüfen, ob das wirklich alles so funktioniert, habe ich einen kleinen Testaufbau gestartet. Inbesondere soll Lesen und Schreiben von und zu verschiedenen Sub-Bus-Systemen getestet werden.

Damit ich einfach sehe ob das Schreiben funktioniert, habe ich einfach an den Bus7 den I2C-Portexpander PCF8574 geschaltet und auf Pin IO0 eine LED gegen UCC angeschlossen. Ziel ist es diese LED zum blinken zu bringen.

Da ich bereits ein Script zum Auslesen des Temperatur und Feuchtigkeitssensor HDC1008 gebastelt hatte, lag es nah, diesen auch an einen weiteren Bus (hier 1) anzuschließen und auszulesen.


Schreiben zum I2C-Portexpander PCF8574


#!/usr/local/bin/perl -w

package main;

use strict;
use warnings;
use Time::HiRes qw(usleep);

use Device::SMBus;

# Multiplexer am Adresse 0x70
my $devMp = Device::SMBus->new(
  I2CBusDevicePath => '/dev/i2c-2',
  I2CDeviceAddress => 0x70,
);


# Auf SDA/SCL 7 am Multiplexer TCA9548A setzen
my $nr =  7; 
print 1 << $nr;
$devMp->writeByte(1 << $nr);


# Zugriff auf den PCF8574-Expander mit Adresse 0x20
my $devExp = Device::SMBus->new(
  I2CBusDevicePath => '/dev/i2c-2',
  I2CDeviceAddress => 0x20,
);

my $i = 0;
# eine LED ist auf dem PCF8574-IO auf Pin 0 gegen UCC mit Vorwiderstand 
# geschalten d.h. TTL - Level ist umgedreht, TTL-Low ist also Lampe an
while($i < 5)  # 5 mal an und ausschalten. 
{
	
	$devExp->writeByte(0x00); # setze LED an 
	
	$i += 1;
	usleep(200000);
	$devExp->writeByte(0x01); # setze LED an
	usleep(200000);

}

Supi, es funzt.

Lesen vom Temperatur- und Feuchtigkeitssensor HDC1008

Nun die zweite Runde.

#!/usr/local/bin/perl -w

package main;

use strict;
use warnings;
use Time::HiRes qw(usleep);

use IO::File;
use Fcntl;

use Device::SMBus;

# Multiplexer am Adresse 0x70
my $devMp = Device::SMBus->new(
  I2CBusDevicePath => '/dev/i2c-2',
  I2CDeviceAddress => 0x70,
);


my $nr =  1; # Auf SDA/SCL 1 am Multiplexer TCA9548A setzen
print 1 << $nr;
$devMp->writeByte(1 << $nr);


# ab hier interessiert uns der Multiplexer nicht mehr
my $dev = Device::SMBus->new(
  I2CBusDevicePath => '/dev/i2c-2',
  I2CDeviceAddress => 0x40,
);

# gibs leider nicht per SMBus, daher eigenes nachgerüstet
sub I2CReadWord
{	
	my ($dev) = @_;

    my $fh = IO::File->new( $dev->I2CBusDevicePath, O_RDWR );
    if ( !$fh ) {
        croak "Unable to open I2C Device";
        return -1;
    }
    $fh->ioctl( Device::SMBus::I2C_SLAVE,  $dev->I2CDeviceAddress );

	my $temp1='';
	sysread ( $fh, $temp1, 2);
	my @x = split(//, $temp1);
	
	my $msb = ord($x[0]);
	my $lsb = ord($x[1]);

	close($fh);

	return $msb*256 + $lsb;

}

$dev->writeWordData(0x02,0x1000);
Time::HiRes::sleep(0.2);


my $i = 0;
while($i < 1000) 
{

	$dev->writeByte(0x00);
	Time::HiRes::sleep(0.07); 
	my $tempWord = I2CReadWord($dev);
	printf ( "Temp: %f\n", (($tempWord /65536.0)*165.0)-40.0 );

	$dev->writeByte(0x01);
	Time::HiRes::sleep(0.07); 
	my $humWord = I2CReadWord($dev);
	printf ( "Hum: %f\n", ($humWord /65536.0)*100.0) ;

	print "durchlauf $i\n\n";

	$i += 1;

}
Artikel von Karsten Grüttner, letzte Änderung 13.10.2015