0

I have a setup using 13 MCP23017 boards communicating on the I2C. Since it is possible only for 3-bit addressing, I am using TCA9546 for multiplexing the I2C bus: 5 on 1st channel, 4 on 2nd channel and 4 on the 3rd channel.

Initially everything seemed to work fine, but now I am facing issues with failures on the MCP23017 boards only on one channel of the multiplexer - Channel 0. The same microcontroller also drives some LEDs through 3 MOSFETs. As soon as I send a drive signal, the MCPs starts misbehaving. This I fear is because of the I2C picking up interference from the MOSFET signals only Channel 0 of the multiplexer. I managed to improve the performance by reducing the cable lengths but now I still see a failure every now and then.

To make it more robust, I am connecting the 5 MCP23017 boards to the Feather M4 bus directly and the rest of the 8 MCP23017 boards to the multiplexer.

Is there a solution changing the software or the hardware?

#include <MCP23017.h>
#include <I2C_Switch_driver.h>

// defines Pins
#define BACKLIGHT_PIN_1 5
#define BACKLIGHT_PIN_2 6
#define BACKLIGHT_PIN_3 9

// define TCA I2C Mux address
#define TCAADDR 0x70

I2C_Switch I2C_Switch(I2C_Switch_ADRESS_0);

// IO 
MCP23017 Mcp1_1(MSP23017_ADRESS_0);
MCP23017 Mcp1_2(MSP23017_ADRESS_1);
MCP23017 Mcp1_3(MSP23017_ADRESS_2); 
MCP23017 Mcp1_4(MSP23017_ADRESS_3); 
MCP23017 Mcp1_5(MSP23017_ADRESS_4); 
MCP23017 Mcp2_1(MSP23017_ADRESS_2); 
MCP23017 Mcp2_2(MSP23017_ADRESS_3); 
MCP23017 Mcp2_3(MSP23017_ADRESS_6);
MCP23017 Mcp2_4(MSP23017_ADRESS_7);
MCP23017 Mcp3_1(MSP23017_ADRESS_0);
MCP23017 Mcp3_2(MSP23017_ADRESS_1);
MCP23017 Mcp3_5(MSP23017_ADRESS_4);
MCP23017 Mcp3_6(MSP23017_ADRESS_5);

// IO initialization
uint8_t mcp1_1PortA = 0x00;
uint8_t mcp1_1PortB = 0x00;
uint8_t mcp1_2PortA = 0x00;
uint8_t mcp1_2PortB = 0x00;
uint8_t mcp1_3PortA = 0x00;
uint8_t mcp1_3PortB = 0x00;
uint8_t mcp1_4PortA = 0x00;
uint8_t mcp1_4PortB = 0x00;
uint8_t mcp1_5PortA = 0x00;
uint8_t mcp1_5PortB = 0x00;
uint8_t mcp2_1PortA = 0x00;
uint8_t mcp2_1PortB = 0x00;
uint8_t mcp2_2PortA = 0x00;
uint8_t mcp2_2PortB = 0x00;
uint8_t mcp2_3PortA = 0x00;
uint8_t mcp2_3PortB = 0x00;
uint8_t mcp2_4PortA = 0x00;
uint8_t mcp2_4PortB = 0x00;
uint8_t mcp3_1PortA = 0x00;
uint8_t mcp3_1PortB = 0x00;
uint8_t mcp3_2PortA = 0x00;
uint8_t mcp3_2PortB = 0x00;
uint8_t mcp3_5PortA = 0x00;
uint8_t mcp3_5PortB = 0x00;
uint8_t mcp3_6PortA = 0x00;
uint8_t mcp3_6PortB = 0x00;

// Functions
void i2ctest();
void tcaselect(uint8_t i);

void setup()
{
  Serial.begin(9600);
  Serial.print("Initialisation started\n");
  Serial.println();

  // Scan I2C devices
  i2ctest();

  // Init IO
  I2C_Switch.setChannel(0);
  Mcp1_1.initInputPortAB(); // all 16 IOs used as input
  Mcp1_2.initInputPortAB(); // all 16 IOs used as input
  Mcp1_3.initInputPortAB(); // all 16 IOs used as input
  Mcp1_4.initInputPortAB(); // all 16 IOs used as input
  Mcp1_5.initInputPortAB(); // all 16 IOs used as input
  I2C_Switch.disableChannel();

  //I2C_Switch.setChannel(1);
  Mcp2_1.initInputPortAB(); // all 16 IOs used as input
  Mcp2_2.initInputPortAB(); // all 16 IOs used as input
  Mcp2_3.initInputPortAB(); // all 16 IOs used as input
  Mcp2_4.initInputPortAB(); // all 16 IOs used as input
  //I2C_Switch.disableChannel();

  //I2C_Switch.setChannel(2);
  Mcp3_1.initInputPortAB(); // all 16 IOs used as input
  Mcp3_2.initInputPortAB(); // all 16 IOs used as input
  Mcp3_5.initInputPortAB(); // all 16 IOs used as input
  Mcp3_6.initInputPortAB(); // all 16 IOs used as input
  //I2C_Switch.disableChannel();
  delay(10);

  Serial.print("Initialisation done\n");
  Serial.println();
  digitalWrite(LED_BUILTIN, LOW);

  delay(10);
}

void loop()
{
  // read Inputs
  I2C_Switch.setChannel(0);
  mcp1_1PortA = Mcp1_1.readPortA(mcp1_1PortA); // read MCP1_1 only PortA
  mcp1_1PortB = Mcp1_1.readPortB(mcp1_1PortB); // read MCP1_1 only PortB
  mcp1_2PortA = Mcp1_2.readPortA(mcp1_2PortA); // read MCP1_2 only PortA
  mcp1_2PortB = Mcp1_2.readPortB(mcp1_2PortB); // read MCP1_2 only PortB
  mcp1_3PortA = Mcp1_3.readPortA(mcp1_3PortA); // read MCP1_3 only PortA
  mcp1_3PortB = Mcp1_3.readPortB(mcp1_3PortB); // read MCP1_3 only PortB
  mcp1_4PortA = Mcp1_4.readPortA(mcp1_4PortA); // read MCP1_4 only PortA
  mcp1_4PortB = Mcp1_4.readPortB(mcp1_4PortB); // read MCP1_4 only PortB
  mcp1_5PortA = Mcp1_5.readPortA(mcp1_5PortA); // read MCP1_5 only PortA
  mcp1_5PortB = Mcp1_5.readPortB(mcp1_5PortB); // read MCP1_5 only PortB
  I2C_Switch.disableChannel();

  //I2C_Switch.setChannel(1);
  mcp2_1PortA = Mcp2_1.readPortA(mcp2_1PortA); // read MCP2_1 only PortA
  mcp2_1PortB = Mcp2_1.readPortB(mcp2_1PortB); // read MCP2_1 only PortB
  mcp2_2PortA = Mcp2_2.readPortA(mcp2_2PortA); // read Mcp2_1 only PortA
  mcp2_2PortB = Mcp2_2.readPortB(mcp2_2PortB); // read Mcp2_1 only PortB
  mcp2_3PortA = Mcp2_3.readPortA(mcp2_3PortA); // read Mcp2_2 only PortA
  mcp2_3PortB = Mcp2_3.readPortB(mcp2_3PortB); // read Mcp2_2 only PortB
  mcp2_4PortA = Mcp2_4.readPortA(mcp2_4PortA); // read Mcp2_3 only PortA
  mcp2_4PortB = Mcp2_4.readPortB(mcp2_4PortB); // read Mcp2_3 only PortB
  //I2C_Switch.disableChannel();

  //I2C_Switch.setChannel(2);
  mcp3_1PortA = Mcp3_1.readPortA(mcp3_1PortA); // read MCP3_0 only PortA
  mcp3_1PortB = Mcp3_1.readPortB(mcp3_1PortB); // read MCP3_0 only PortB
  mcp3_2PortA = Mcp3_2.readPortA(mcp3_2PortA); // read MCP3_1 only PortA
  mcp3_2PortB = Mcp3_2.readPortB(mcp3_2PortB); // read MCP3_1 only PortB
  mcp3_5PortA = Mcp3_5.readPortA(mcp3_5PortA); // read MCP3_4 only PortA
  mcp3_5PortB = Mcp3_5.readPortB(mcp3_5PortB); // read MCP3_4 only PortB
  mcp3_6PortA = Mcp3_6.readPortA(mcp3_6PortA); // read MCP3_5 only PortA
  mcp3_6PortB = Mcp3_6.readPortB(mcp3_6PortB); // read MCP3_5 only PortB
  //I2C_Switch.disableChannel();

  Serial.print(mcp1_1PortA, BIN);
  Serial.println();
  Serial.print(mcp1_2PortB, BIN);
  Serial.println();
  Serial.print(mcp1_3PortA, BIN);
  Serial.println();  
  Serial.print(mcp1_4PortB, BIN);
  Serial.println();
  Serial.print(mcp1_5PortA, BIN);
  Serial.println();
  Serial.print(mcp2_1PortB, BIN);
  Serial.println();
  Serial.print(mcp2_2PortA, BIN);
  Serial.println();
  Serial.print(mcp2_3PortB, BIN);
  Serial.println();
  Serial.print(mcp2_4PortA, BIN);
  Serial.println();
  Serial.print(mcp3_1PortB, BIN);
  Serial.println();
  Serial.print(mcp3_2PortA, BIN);
  Serial.println();
  Serial.print(mcp3_5PortB, BIN);
  Serial.println();

  analogWrite(BACKLIGHT_PIN_1, 255);
  analogWrite(BACKLIGHT_PIN_2, 255);
  analogWrite(BACKLIGHT_PIN_3, 255);
  delay(1000);
}

void i2ctest()
{
  Wire.begin();
  Serial.println("\nScanning I2C devices...");

  for (uint8_t t = 0; t < 4; t++)
  {
    tcaselect(t);
    Serial.print("TCA Port #");
    Serial.println(t);

    for (uint8_t addr = 0; addr <= 127; addr++)
    {
      if (addr == TCAADDR)
        continue;

      Wire.beginTransmission(addr);
      if (!Wire.endTransmission())
      {
        Serial.print("Found I2C device on 0x");
        Serial.println(addr, HEX);

        delay(10);
      }
    }
  }
  Serial.println("\nI2C device scanning completed");
  delay(10);
}

void tcaselect(uint8_t i)
{
  if (i > 7)
    return;

  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();
}
12
  • It is not clear to me, what you mean with "bridge" here. Can you please explain a bit more. Adding a wiring diagram would help. And have you tried setting the address pins of the TCAs dynamically, like described in this answer? That is about MPUs, but maybe this also works for the TCA9546. Then you wouldn't need a multiplexer
    – chrisl
    Commented Jun 28 at 8:58
  • 1
    @chrisl By bridge I mean, I shorted the cables together. The I2C lines of the 3 channels of the multiplexer I2C was connected to the main I2C for short duration. Now I have brought everything to the original config, so it is working fine. TCA9546 is not broken for sure. But the issue still remains the same, if I want to change bring the devices of 1 channel to the main I2C bus, the TCA9546 prints all devices on all channels which is wrong.
    – dreamcoder
    Commented Jun 28 at 10:07
  • 1
    All the pull-ups are installed correctly. I am using arduino and the standard Wire.h library. You will also find the I2C switch logic in the code above. How can I change the I2C clock speed in arduino?
    – dreamcoder
    Commented Jun 28 at 10:57
  • 1
    "5 MCP boards will be connected to the multiplexer channel 0 and the rest of the 8 MCP boards will be connected to the Feather M4 I2C directly" I don't understand how that's supposed to work if they're all connected to the same MCU I2C bus. If that's what you meant you either have 8 uniquely addressable MCP23017 with mux channel 0 disabled or you have 3 uniquely addressable MCP23017 and 5 pairs of conflicting MCP23017s on the bus with mux channel 0 enabled. Am I missing something?
    – timemage
    Commented Jun 29 at 2:12
  • 1
    As @timemage has clearly pointed out, you cannot have I2C devices in the same address range both directly on the I2C bus and on the multiplexer. Of course the scanner will always report the devices on the bus irrespective of what channel is selected by the multiplexer. You have to focus on your original problem of occasional "TCA9546 failures". The multiplexer is unlikely to be the problem, assuming you give it enough time to switch channel, and I2C is prone to the effects of high wiring capacitance, or overload through multiple pull-up resistors etc.
    – 6v6gt
    Commented Jun 29 at 7:39

0

Browse other questions tagged or ask your own question.