![]() |
I2C-Multiplexer PCA9516Prof. Jürgen Plate |
Es gibt spezielle I2C-Bausteine, die nur über eine Adresse verfügen. Was tun, wenn man mehrere dieser Chips verwenden möchten? Eine Lösung dieses Problems stellt das IC PCA9516 dar, denn er kann den I2C-Bus multiplexen. Die I2C-Bus-Hubs (Repeater) PCA9515 und PCA9516 dienen dazu, die Verbindung von I2C-Bussen zu ermöglichen, ohne die kapazitive Last eines Busses zum anderen Bus zu übertragen. Im Datenblatt des PC9516 wird angegeben, dass es sich um einen 5-Kanal-Hub handelt. Da ein Kanal der Master (Anschluss des Rechners) ist, schaltet der Multiplexer den Master-Bus auf vier unabhängige Slave-Busse durch.
Eine Adresse, die auf dem Master-Bus schon belegt ist, steht auf den Slave-Bussen logischerweise nicht mehr bereit. In der Praxis macht so der PCA9516 eine Adresse nur je einmal an jedem der vier Slave-Busse des PCA9516 verfügbar. Das folgende Bild zeigt das Blockdiagramm aus dem Datenblatt:

Die als "Buffer" bezeichneten Blöcke sind nichts weiter als bidirektionale Verstärker, der so ausgelegt ist, dass ein "lock-up" verhindert wird.

Die Eigenschaften des Bausteins können sich sehen lassen:

Die Pinbelegung ist:
| Symbol | Pin | Description |
|---|---|---|
| SCL0 | 1 | serial clock bus 0 |
| SDA0 | 2 | serial data bus 0 |
| SCL1 | 3 | serial clock bus 1 |
| SDA1 | 4 | serial data bus 1 |
| EN1 | 5 | active HIGH bus 1 enable input |
| SCL2 | 6 | serial clock bus 2 |
| SDA2 | 7 | serial data bus 2 |
| GND | 8 | supply ground |
| EN2 | 9 | active HIGH bus 2 enable input |
| SCL3 | 10 | serial clock bus 3 |
| SDA3 | 11 | serial data bus 3 |
| EN3 | 12 | active HIGH bus 3 enable input |
| SCL4 | 13 | serial clock bus 4 |
| SDA4 | 14 | serial data bus 4 |
| EN4 | 15 | active HIGH bus 4 enable input |
| VCC | 16 | supply power |
Eine typische Anwendung ist schematisch im Bild unten gezeigt. In diesem Beispiel hat der System-Master einen 3,3-V-I2C-Bus, während einige Slaves einen 5-V-Bus besitzen. Alle Busse arbeiten mit 100 kHz solange Slave 3 aktiviert ist. Ist Slave 3 abgeschaltet, können der Master-Bus und Slave 1 und Slave 2 mit 400 kHz laufen. Jedes Segment des Hub kann mit jedem anderen Segment des Hub kommunizieren. Unbenutzte Ports sollten durch Ziehen des jeweiligen Enable-Pins (ENn) auf GND-Potential isoliert werden. Die Leitungen SDAx und SCLx müssen bei jedem Port die entsprechenden Pull-Up-Widerstände nach Vcc haben. Der primäre Bus-Master ist normalerweise an SDA0/SCL0 angeschlossen. Werden diese beiden Ports nicht verwendet, müssen die Anschlusspins trotzdem durch entsprechend dimensionierten Widerstände auf Vcc gezogen werden. Die PCA9516A ist 5,5-V-tolerant, so ist keine zusätzliche Schaltung erforderlich, um zwischen den verschiedenen Bus-Spannungen umzusetzen. Wird eine Seite des PCA9516A von einem Gerät auf dem I2C-Bus auf LOW gezogen, erkennt ein CMOS-Hysterese-Eingang die fallende Flanke und bewirkt, dass der interne Treiber auf der anderen Seite eingeschaltet wird, damit die andere Seite auch auf LOW gehen kann.

Das folgende Bild zeigt eine Applikationsschaltung, wobei hier alle Ports an 3,3 V angeschlossen sind. Es ist jedoch möglich, auch Peripherie mit 5 V Versorgungsspannung anzuschliessen. Es muss dann lediglich der Pin 1 des entsprechenden Jumpers (JP2 bis JP5) und der zugehörige Pull-Up-Widerstand (R11 bis R14) an die 5-V-Versorgung angeschlosenn werden anstatt an 3,3 V. JP1 wird an digitale GPIO-Ausgänge des Raspberry Pi angeschlossen, die dann den jeweiligen Kanal zwischen SDA0/SCL0 und SDAx/SCLx freischalten. Per Default werden diese Leitungen durch R7 bis R10 auf LOW-Pegel gezogen.

Das folgende Python-Programm scannt den I2C-Bus 1 auf angeschlossene Devices. Dabei wird zuerst der Bus selbst untersucht und dann alle alle Möglichkeiten, Devices über den Multiplexer zu finden. Dazu wird jeweils eine Enable-Leitung auf HIGH gesetzt und dann auf aktive I2C-Bausteine gescannt. Damit kann man auch erkennen, ob eine mit dem PCA9516 bestückte Platine wie gewünscht funktioniert.
#!/usr/bin/env python
#
# ======================================================================
# i2cmdetect.py
#
# Program to detect I2C slaves that are attached directly to bus 1
# but also checks for slaves attached to a multiplexer (PCA 9516).
# CH1 = GPIO4, CH2 = GPIO17, CH3 = GPIO27, CH4 = GPIO22
#
# (C) Thomas Baumann 2014, JPL 2016
# ======================================================================
#
import sys
import time
import os
import smbus
# Import GPIO module
try:
import RPi.GPIO as GPIO
except RuntimeError:
print("Error importing RPi.GPIO!")
print("This is probably because you need superuser privileges. ")
except:
print("Error importing RPi.GPIO!")
print("Unexpected error: ", sys.exc_info()[0])
# end try
# Initialize GPIO
CH1 = 4
CH2 = 17
CH3 = 27
CH4 = 22
GPIO.setmode(GPIO.BCM) # use chip numbers
GPIO.setwarnings(False) # suppress already in use warnings
GPIO.setup(CH1, GPIO.OUT) # CH1 enable
GPIO.setup(CH2, GPIO.OUT) # CH2 enable
GPIO.setup(CH3, GPIO.OUT) # CH3 enable
GPIO.setup(CH4, GPIO.OUT) # CH4 enable
# Scanning routine
def PerformScan(I2C):
Found = False
for i in range(0x03, 0x78):
try:
I2C.write_quick(i)
print("Device found at " + hex(i).upper().replace("X", "x"))
Found = True
except:
pass
#end try
#next
if Found == False:
print(" no device found.")
#end if
#end def
try:
# Open I2C Bus
I2C = smbus.SMBus(1)
# Loop through multiplexer
for i in range(0, 5):
if i == 0: # direct bus scan
GPIO.output(CH1, False)
GPIO.output(CH2, False)
GPIO.output(CH3, False)
GPIO.output(CH4, False)
print("Scanning bus directly ...")
PerformScan(I2C)
elif i == 1: # scan bus on channel 1
GPIO.output(CH1, True)
GPIO.output(CH2, False)
GPIO.output(CH3, False)
GPIO.output(CH4, False)
print("Scanning bus channel " + str(i) + " ...")
PerformScan(I2C)
elif i == 2: # scan bus on channel 2
GPIO.output(CH1, False)
GPIO.output(CH2, True)
GPIO.output(CH3, False)
GPIO.output(CH4, False)
print("Scanning bus channel " + str(i) + " ...")
PerformScan(I2C)
elif i == 3: # scan bus on channel 3
GPIO.output(CH1, False)
GPIO.output(CH2, False)
GPIO.output(CH3, True)
GPIO.output(CH4, False)
print("Scanning bus channel " + str(i) + " ...")
PerformScan(I2C)
elif i == 4: # scan bus on channel 4
GPIO.output(CH1, False)
GPIO.output(CH2, False)
GPIO.output(CH3, False)
GPIO.output(CH4, True)
print("Scanning bus channel " + str(i) + " ...")
PerformScan(I2C)
#end if
#next
print("Done ...")
except:
print("Can't access bus 1")
#end try