I2C Multiplexer Shield Testing

I built an I2C multiplexer shield using an Arduino prototyping shield from SchmartBoard a couple of weeks ago. The shield uses a PCA9548A I2C multiplexer and switch chip from Taxes Instruments.

One of the issues with testing I2C using Arduino is that different ICs have different Vcc requirements. While ATMega328P can be powered by either 3.3V or 5V, most Arduino boards are designed to handle only a single supply voltage. So when the supply voltage of the IC is different than that of the Arduino, a voltage level translator such as PCA9517 must be added (you can see an example here).

This is where PCA9548 comes in handy. PCA9548 has 5V tolerate IO ports and when powered with a 3.3V Vcc, it can communicate with either 3.3V or 5V powered I2C devices. And it is also very easy to use. Basically, you first select the channels you want to communicate with and then you can communicate with the I2C device as you would with it directly.

The following code example illustrates this. Here a DS1077 oscillator is connected to the first I2C channel of the PCA9548A (in this example, PCA9548A’s I2C address is set as A2 A1 A0 = 1 0 0).

#define PCA9548ADDR 0x74 //1110100
#define DS1077ADDR 0x58 //1011000

#include <Wire.h>

void setup()
{
  Wire.begin();
  
  //select the first I2C Channel  
  selectI2CChannels(0x1);
  
  //output a 16.67 Mhz clock on output 0
  writeDS1077Reg(0x2, 0x1E, 0x0);  //write mux register CLK/8
}

void loop()
{

}

void selectI2CChannels(int channels) 
{
  Wire.beginTransmission(PCA9548ADDR);
  Wire.write(channels);
  Wire.endTransmission();  
}

void writeDS1077Reg(int cmd, int msb, int lsb) 
{
    Wire.beginTransmission(DS1077ADDR);
    Wire.write(cmd);
    Wire.write(msb);
    Wire.write(lsb);
    Wire.endTransmission();
}

Function writeDS1077Reg sets the output frequency of the DS1077 oscillator to CLK/8 (16.67 Mhz). This is the only line of code needed when the device is attached to Arduino directly.

When attached to PCA9548A, the only difference in code is the selection of the channel on which DS1077 is connected to prior to calling writeDS1077Reg. This is handled via selectI2CChannels function call. This function takes in a single byte that controls which I2C channels are selected. The eight I2C channels of PCA9548A can be addressed using this command byte, with each bit representing a channel.

Multiple I2C channels can be enabled at once. For instance, if both channel 2 and channel 4 are selected the channel command byte would be B00001010, or 0xA. Once the selected channels are activated, they will remain active in subsequent I2C calls until selectI2CChannels is called again to deactivate the channel (i.e. with a 0 in the corresponding bit) or until a power reset.

Besides interfacing I2C devices requiring different supply voltages, PCA9548A is also useful when dealing with multiple I2C devices with the same device address. In this case, we can use PCA9548A to multiplex these devices by ensuring only one channel is active at any given time.

Be Sociable, Share!

14 Comments

  1. Vladimir says:

    Hello. Great explaining of I2C multiplexer function, thank you.
    BTW, you have an error in the link to PCA9548A datasheet (need to add ‘http://’ at the beginning).

  2. casey g says:

    Kerry – I’m working on a project where I need something like this to control 4 ADXL345’s from a single arduino, but I’m a noob when it comes to the electronics side of things. I can get my hand on ADXL345 breakout boards that use 2 different I2C addresses, but was struggling with how I would connect 4 when I only have 2 unique addresses.

    Was curious if you would recommend using the PCA9548A chip (from this write up) or the PCA9517 chip (from http://www.kerrywong.com/2011/05/08/a-dual-temperature-display-with-humidity-measurement/) to solve this?

    And either way, where do you get these chips?

    Cheers.

    Casey.

  3. Reebbhaa Mehta says:

    How did you hook up the TCA9548A to your slave devices? Did you use pull up resistors? I’m having trouble figuring out the values of my pull up resistors.

  4. mario says:

    Hi Kerry !
    Congartulatios for your web site, the contens, explanatios , selected topics, smart solutions!

    i wuould like ask you the next.

    I want connect 2,3, etc FM tuners (https://www.sparkfun.com/products/10663) to one Arduino uno Rev3, i want that each fm tuner has your own frecuency (radio station), but all Si4703 Fm tuners have a only one address, , i saw your topic about the arduino shield to I2C (http://www.kerrywong.com/2012/09/19/prototyping-with-schmartboard-arduino-shield-board/) and i think that is great for this case.

    can you have a example code? , i dont know how to starter with the PCA9548A programming in arduino

    Thanks

  5. Sara says:

    Hello,

    I am using TCA9548A multiplexer and I used your code to read from 4 sensors. When I use channel 1 and 2 the multiplexer works and read the data. However, when I tried the other channels it did not work and I don’t know why! I used the hexadecimal number just like you to indicates the channel (i.e. 0x4, 0x5,…). I have 5 multiplexers and I have tried all of them and I have faced the same problem.
    Could you please help me? This very urgent

    Thanks

    • kwong says:

      Did you put in the correct address for each device? Basically, you first call the routine to set the channel you wanted to address, and then you will address the I2C device as if it were connected to the I2C bus directly. If you can post the code you are having issue with, I can take a look.

    • Adam says:

      I don’t know if you figured it out already, but channel 3 has to be 0x4, channel 4 0x8, channel 5 0x10. It’s best you write it in binary like: B00000001, B00000010 … etc. or in int: 2^x, where x is the channel number from 0-7.

  6. Sara says:

    I used only one multiplexer at each time. I set A0 and A1 as Low and A2 as high so that the address of the multiplexer will be 0x74. Here is the code:

    #include
    #include
    #include
    #include
    #define TCA9548AADDR 0x74 //1110100

    LSM303 compass,compass2;
    L3G gyro,gyro2;
    int AC0 = 2;
    int AC1 = 4;
    int AC2 = 7;

    void setup() {
    Serial.begin(9600);
    Wire.begin();
    pinMode(AC0, OUTPUT);
    pinMode(AC1, OUTPUT);
    pinMode(AC2, OUTPUT);

    digitalWrite(AC0, LOW);
    digitalWrite(AC1, LOW);
    digitalWrite(AC2, HIGH);

    Serial.println(“initing sensor1…”);
    selectI2CChannels(0x1);
    compass.init();
    compass.enableDefault(); // enable default settings of the compass
    while (!gyro.init())
    {
    Serial.println(“Failed to autodetect gyro 1 type!”);
    //setting();
    }

    gyro.enableDefault(); // enable default settings of the gyroscope
    Serial.println(“done”);
    delay(1);

    selectI2CChannels(0x2);
    Serial.println(“initing sensor2…”);
    compass2.init();
    compass2.enableDefault(); // enable default settings of the compass
    if (!gyro2.init())
    {
    Serial.println(“Failed to autodetect gyro 2 type!”);
    //setting();
    }

    gyro2.enableDefault(); // enable default settings of the gyroscope
    delay(1);

    Serial.println(“done”);

    }

    void loop() {

    }

    void selectI2CChannels(int channels)
    {
    Wire.beginTransmission(TCA9548AADDR);
    Wire.write(channels);
    Wire.endTransmission();
    }

    Thanks

    • kwong says:

      The code you posted looks fine. You mentioned that it only happens when you connect more than 2 devices? Curious to see whether the third device works if you swap it to the first channel and hook your gyro which was originally on channel 1 to channel 3 to see if that works. Since TCA9548 is extremely simple (you could try just use one sensor and see if you could address it using other channels), the only thing I could think of is there got to be a coding conflict (maybe in one of the libraries) somewhere.

Leave a Reply