How to use 74LS138 or 74HC595 to muliplex two I2C signals?

Hello folks. I should begin by saying that I have some experience with Arduino, basic experience with I2C, and no experience with multiplexing.

What I have is two magnetometers which both utilise I2C, and an Arduino that can only read one I2C at once. Because the magnetometers are the exact same (and I can't edit the address of either) I am resorting to multiplexing to toggle between the two when need be.

The issue with this is that I have no idea what I'm doing. I have access to both the 74HC595N and T74LS138B1, and was wondering if anyone could give me some pointers as to how to actually use them. I can find plenty of resources on using them as outputs, but how to I use them as inputs for two I2C components, and how do I change which is outputted from the multiplexer at any given time.

Cheers in advance!

You won't be able to use those parts to cross connect and select between two i2c buses.
If you have a couple of extra pins you could run two separate buses by using a software i2c library:
http://playground.arduino.cc/Main/SoftwareI2CLibrary

--- bill

This question is of interest to me as well.

Is there a chip that can be used to multiplex I2C signals?

Here’s a thought straight off the top of my head… YMMV and it might be total crap but here it is:

Use a small transistor to switch one or other of the I2C SDA/SCL lines on each device. Each transistor base would be under the control of an i/o pin, and the logic would have only one base and therefore only one device active at a time.

Pic below is kind of what I have in mind.

Or… look for hardware with settable addresses :stuck_out_tongue:

Both SDA and SCL are open drain as both the master and the slave can pull them down.
You could something similar by sharing the SCL lines and then using an analog switch (which is bi-directional)
instead of transistor to select which bus to use.

--- bill

Won't work.
You could use HC138 to select which device gets SCL, tie all SDA together.
Drive G2A/ and G2B/ with SCL, let address lines pick which output will mimic SCL.
Only Arduino can be I2C master tho as '138 will drive SCL high when I2C clock is inactive.

Pretty sure the Slave does not drive SCL - only the master does.

CrossRoads:
Pretty sure the Slave does not drive SCL - only the master does.

That would be incorrect.
SCL can be driven low by either.
The slave holds SCL down during clock stretching.
http://www.i2c-bus.org/clock-stretching/

--- bill

Ok, found that in the '328P datasheet too, 22.3.5.
Stick with an I2c mux then unless it is known the slaves do not need clock stretching.

When I said "won't work", I wasn't referring to the analog switch, altho the high impedance of a switch could impact clock edges if the line had too high of a value pullup resistor (10K vs 4.7K for example) or had a lot of capacitance.

Woah.... way too complicated a discussion for me :P. Clock stretching? What now?

If I ever need to have I2C devices with the same addy, I'll go with the mux that CR linked, that looks dead simple.

It does keep the signals nice & clean that way.

bperrybap:
You won’t be able to use those parts to cross connect and select between two i2c buses.
If you have a couple of extra pins you could run two separate buses by using a software i2c library:
http://playground.arduino.cc/Main/SoftwareI2CLibrary

— bill

I’ve decided to have a go at using the Software I2C Library that bperrybap suggested, and I’m a bit unsure of how to use it to read x, y, and z coordinates. I’m pretty sure I’ve got the initialisation and setup right, but I’ve got no idea how to actually read from the magnetometer. My code so far is below (the I2C related bits, at least), and if anyone has any suggestions I would very much appreciate them.

#include <SoftI2C.h>
#define address 0x1E
#define SCL_PIN 21
#define SCL_PORT PORTF
#define SDA_PIN 20
#define SDA_PORT PORTF
uint_8 msb,lsb;

void setup() {
  ic2_init();
}

void main() {
  ic2_start(address|I2C_READ);
  // Find x coord
  // Find y coord
  // Find z coord
  ic2_stop();
}

And if it helps, here’s the code I’m using to retrieve the x, y, z values from the proper I2C.

  Wire.beginTransmission(address);
  Wire.send(0x03);
  Wire.endTransmission();
  Wire.requestFrom(address,6);
  if(6<=Wire.available()){
    x = Wire.receive()<<8;    //X msb
    x |= Wire.receive();      //X lsb
    z = Wire.receive()<<8;    //Z msb
    z |= Wire.receive();      //Z lsb
    y = Wire.receive()<<8;    //Y msb
    y |= Wire.receive();      //Y lsb
  }

acegard:
This question is of interest to me as well.

Is there a chip that can be used to multiplex I2C signals?

Do you really need to?

I2C can easily be done in software on any two I/O pins (probably using less pins than it would take to control a multiplexor!)

fungus:
I2C can easily be done in software on any two I/O pins (probably using less pins than it would take to control a multiplexor!)

Any suggestions or pointers regarding how to go about that?

Lewis1708:

fungus:
I2C can easily be done in software on any two I/O pins (probably using less pins than it would take to control a multiplexor!)

Any suggestions or pointers regarding how to go about that?

This page has a lot of information: LMGTFY - Let Me Google That For You

fungus:

Lewis1708:

fungus:
I2C can easily be done in software on any two I/O pins (probably using less pins than it would take to control a multiplexor!)

Any suggestions or pointers regarding how to go about that?

This page has a lot of information: http://lmgtfy.com/?q=software+i2c+on+arduino

Yeah, tried that and couldn’t understand how to actually derive the x, y, and z, hence why I asked.

Lewis1708:

fungus:

Lewis1708:

fungus:
I2C can easily be done in software on any two I/O pins (probably using less pins than it would take to control a multiplexor!)

Any suggestions or pointers regarding how to go about that?

This page has a lot of information: LMGTFY - Let Me Google That For You

Yeah, tried that and couldn't understand how to actually derive the x, y, and z, hence why I asked.

OK, assuming you click on the first result (ie. http://playground.arduino.cc/Main/SoftwareI2CLibrary ), that would lead you here: GitHub - felias-fogg/SoftI2CMaster: Software I2C Arduino library

What have you done with that?

fungus:
OK, assuming you click on the first result (ie. http://playground.arduino.cc/Main/SoftwareI2CLibrary ), that would lead you here: GitHub - felias-fogg/SoftI2CMaster: Software I2C Arduino library

What have you done with that?

As I said earlier, I've got the initialisation set-up, but the actually reading of x, y, and z isn't working for me. In the example this code is used:

  xval = readOneVal(false);
  yval = readOneVal(false);
  zval = readOneVal(true);

I cannot understand what's being used to differentiate between xval and yval (as they both use "false"), and I don't get why he uses false for those two, and true for zval.

One thing to keep in mind is that the playground web page mentions several different
i2c libraries. The main two are SoftI2CMaster and i2cMaster. They are not the same
and work very differently.
Neither is API compatible with the Wire library.
SoftI2CMaster is very fast but only supports s/w i2c.
i2cMaster supports both s/w and h/w i2c.
The example and API documentation on the playground page are for SoftI2CMaster.

Lewis1708:
I cannot understand what’s being used to differentiate between xval and yval (as they both use “false”), and I don’t get why he uses false for those two, and true for zval.

You are going to have to look at the actual code and read the API documentation for the library.
In that example code readOneVal(last) is not returning 1 byte but rather one 16 bit value
which is two 8 bit bytes which requires reading two bytes from the i2c bus:

int readOneVal(boolean last)
{
  uint8_t msb, lsb;
  lsb = i2c_read(false);
  msb = i2c_read(last);
  return (int)((msb<<8)|lsb)/64;
}

Then go read the API documentation (it is above the example code on that playground page)
i2c_read(last) takes a parameter which tells the library if the read is the last
read before the bus is to be released.

The other library:
i2cMaster, while not as fast for s/w i2c, is a bit more c++ like and supports both s/w and h/w i2c.
one advantage of i2cMaster is the API for h/w and the s/w i2c will be the same when using this library
since it supports both.

— bill

Lewis1708:
As I said earlier, I’ve got the initialisation set-up, but the actually reading of x, y, and z isn’t working for me. In the example this code is used:

  xval = readOneVal(false);

yval = readOneVal(false);
  zval = readOneVal(true);




I cannot understand what's being used to differentiate between xval and yval (as they both use "false"), and I don't get why he uses false for those two, and true for zval.

There’s nothing being used to differentiate them, the I2C device is presumably sending all three values one after the other.

The ‘true’ passed for zval tell the I2C library to close the connection after reading.

Hint: Once you’ve picked an I2C library that you like it’s probably best to read both devices using software I2C (on different Arduino pins). That way you don’t have different code/library for each device.