One of the major limitations to I2C is the inherent address conflicts when you try to bus multiple sensors of the same type/family together. While all sensors have a default address, some give an option for a secondary, and fewer give tertiary addresses or beyond. Since most systems only have one I2C bus, you are limited in how many of those sensors are used.
The 9548 family of modules solves this problem: they are multiplexer.
The 9548A / 9548A Family
This family of modules from various manufacturers solves this problem quite cleanly. These modules are known as Multiplexers, or Muxers for short.
These are effectively electronic DPDT switches. Depending on what channel is selected, SDA and SCA are routed to SD0/SC0 through SD7/SC7.
Selecting a channel
How these channels are selected is quite straight-forward: Each channel is a bit, whichever bit is a “1” is the one activated.
Bit 7 (MSB) | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 (LSB) | |
---|---|---|---|---|---|---|---|---|
Channel 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
Channel 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
Channel 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Channel 3 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
Channel 4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Channel 5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
Channel 6 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Channel 7 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
void tcaselect(uint8_t i) {
if (i > 7){
return; // escapes invalid input
}
Wire.beginTransmission(TCAADDR);
Wire.write(1 << i);
Wire.endTransmission();
}
Communicating with your sensor
The 9548 modules are fully transparent. As a result, communicating with your end sensors does not change!
All you do is call the select function with the channel number you are activating.
In the circuit below, a PCA9548A board is being used to interface six LSM6DSOX IMUs to a Mega2560.
Two IMUs are connected to channels 0, 1, and 2. On each of those channels, one IMU is default addressed 0x6A and the other has D0 pulled high for the secondary address of 0x6B.
tcaselect(0);
soxA.beginI2C(0x6A)
soxB.beginI2C(0x6B)
tcaselect(1);
soxC.beginI2C(0x6A)
soxD.beginI2C(0x6B)
tcaselect(2);
soxE.beginI2C(0x6A)
soxF.beginI2C(0x6B)
...
...
...
...
tcaselect(0);
soxA.getEvent(&accelA, &gyroA, &tempA);
soxB.getEvent(&accelB, &gyroB, &tempB);
tcaselect(1);
soxC.getEvent(&accelC, &gyroC, &tempC);
soxD.getEvent(&accelD, &gyroD, &tempD);
tcaselect(2);
soxE.getEvent(&accelE, &gyroE, &tempE);
soxF.getEvent(&accelF, &gyroF, &tempF);