Issue with I2C communication in with multiple I2C sensors

Hello,

i'm going crazy trying to solve an issue involving I2C communication. I have the following setup: 

- ncs 2.4.2 (can't upload to latest because i have an issue with that...i have an issue open on DevZone)

- NRF5340DK

- I2C sensor A (TCS3400)

- I2C sensor B (MCP9808)

- I2C sensor C (LSM6DSOX)

- I2C sensor D (FDC1004)

I developed all the libraries to communicate with these sensors (yes, i know that many of them are supported by Zephyr but drivers were lacking many features such as interrupt, etc.). All the libraries have been tested arranging the breakouts of the sensors on a breadboard.

Now, this is the problem: when i try to read data from the FDC1004 (using the required sequence of commands: trigger measure -> check if measure is done -> read measure) AND the sensor A is connected, the "MEASURE_DONE" bit is NEVER set. Please note that the I2C function that reads the register is working (i can read the register content and all the other bits are correctly set). If i unplug the SDA or SCL or VDD for the breakout of sensor A the MEAS_DONE bit is instantly set.

Please note that:

- Connecting or disconnecting sensor B and C is totally irrelevant. If they are connected they work fine, if they are disconnected i still have the exact same problem with A and D. This exclude any problem about the loading of the SDA/SCL paths. Moreover, as i said, ALL the functions with ALL the sensors are working, with the exception of the function "isMeasDone()";

- The only function with this strange behaviour is "isMeasDone()". All the other functions set and read all the correct values in all the registers of every sensor;

- Each sensor by itself works with the code i wrote. This means that there are no issues with the addresses and the write/read functions;

- Sensors do not have the same I2C address. I double-checked it and, moreover, i highlight again that i have this issue ONLY with that specific function;

- The TCS3400 breakout is the only one without pull-up resistors for SDA and SCL on the board, but because of the fact that the other sensor breakouts have it, this is not needed. I measured the overall pull-up values (the parallel of all the pull-up of the several breakouts) is 5.2k, so this should be fine;

- Logging is active in the project. This allows me to track any error on the comunication...and there is not.

Unfortunately i cannot share the project because it has tens of source files and would be very unpractical. Here below you can find the definition of the of the function that is giving me trouble. Again, this function works perfectly if sensor A is not connected, so there should be no issue with the code....however...here you go:

bool fdc1004_isMeasDone(fdc1004_measCh_t measNum) { 

	//Read actual register value
	uint16_t regData = 0;
	if (fdc1004_read16(FDC1004_REG_FDC_CONF, &regData) != 0)
		return false;

	switch (measNum) {
		case FDC1004_MEAS1:
		return (regData & (1 << 3));
		break;
		case FDC1004_MEAS2:
		return (regData & (1 << 2));
		break;
		case FDC1004_MEAS3:
		return (regData & (1 << 1));
		break;
		case FDC1004_MEAS4:
		return (regData & (1 << 0));
		break;
	}	

	return false;
}

int fdc1004_read16(uint8_t regAddr, uint16_t* data) {
	
  // Initialize read and write buffers
  const uint8_t wbuffer = regAddr;
  uint8_t rbuffer[2];
	
  // Read register
  int ret = i2c_write_read_dt(fdc1004_dev,&wbuffer,1,rbuffer,2);
  if (ret != 0) {
	  LOG_ERR("%s error: Failed to read register 0x%0x", __func__, regAddr);
	  return SENSOR_ERROR_READ_FAILED;
    }

  // Prepare data to return
  *data = rbuffer[0]<<8|rbuffer[1];
  
  return SENSOR_SUCCESS;
}

Just for the sake of completeness, here below is the section of code that is generating my headache:

    // Perform measurement on channel 1
    fdc1004_triggerMeasure(FDC1004_MEAS1, true);
    while (!fdc1004_isMeasDone(FDC1004_MEAS1))      //ISSUE: THIS NEVER BECOMES TRUE WHEN SENSOR A IS CONNECTED!
    {
        k_busy_wait(100000);
    }
    fdc1004_getCapacitance(FDC1004_MEAS1, &capLevel);

I'm not able to get out of this on my own. I am not even able to understand if this is an hardware or a software issue. I hope your experience can provide me some hints to solve it.

Thank you,
Frax

  • This may be a simple bus loading issue; 5k2 is insufficient pull-up for 400kHz and only sufficient for a single device at 100kHz; for multiple devices 1k0 would be a better choice. The loading is more related to capacitive load than current load, since the capacitive load causes the slow rise times on I2C. Simple to test, make the pull-ups 1k0 and see if it starts working.

    A second issue may be the TCS3400 colour sensor; the threshold should match the application I2C voltage to minimise loading:

    TCS34001FNM 0x39 I²C VBUS = VDD Interface
    TCS34003FN  0x39 I²C bus = 1.8V Interface

    The last item to verify is that the I2C SCL and SDA ports are using H0D1 not S0D1. Indeed, SCL can use H0H1 if none of the I2C devices does clock-stretching or acts as a master.

  • Hi hmolesworth,

    thank you for your comments. Unfortunately what you suggested seems not to apply to my case. Let's go in order:

    1) I don't agree about the loading issue. In any case, i tried to check what i could and a) I2C speed is set to 100kHz and b) if i put 1k Ohm as pull-up. The result was that not a single sensor was able to communicate

    2) The TCS3400 that i'm using is the TCS34001FNM. As i said in the first post, i'm able to comunicate with all the sensors taken as single

    3) I still need to verify the H0D1/S0D1 element, but still i don't understand why because just 1 bit of tons of register comunication should be related with that :).

    I'm planning to use a logic analyzer soon. I'll keep the post updated. In the meantime, if anyone has any good idea is more than welcome

  • Item 1: The test you performed says it all; you are using S0D1 which will not work reliably with multiple sensors; you can see this on an oscilloscope but probably not on a logic analyzer. A logic analyzer hides the true signal and makes it look "square" with sharp edges, but I2C fails due to slow rise times and poor low levels only really visible on an oscilloscope. Bet you a pint :-)

  • I didn't configure S0D1, but from what I understood that is the default, so I'm sure I'm using it. Still, I don't get why this setting should affect the specific bit of a specific i2c transaction among the 40-50 transaction I do for setting/reading that sensor and all the other ones...

    Can you please explain me why this issue should affect only the 4th bit of the FDC_CONF register among all the other register of all the sensors? (They are tens if not hundreds of register). If slow time rise/poor low level is the issue, this should affect randomly any i2C transaction on the bus, shouldn't it? 

    In any case I'll use a digital scope asap.

  • More likely it's not messing with a single bit, the trigger write command simply fails. Try adding this just before doing a write-read to get H0D1:

      nrf_gpio_cfg(PIN_SCL, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0D1, NRF_GPIO_PIN_NOSENSE);
      nrf_gpio_cfg(PIN_SDA, NRF_GPIO_PIN_DIR_INPUT,  NRF_GPIO_PIN_INPUT_CONNECT,    NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0D1, NRF_GPIO_PIN_NOSENSE);

    Even better make the clock active high as well as active low but be aware this is not possible if any of the slaves want to use clock-stretching or indeed want to be masters:

      nrf_gpio_cfg(PIN_SCL, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);

    Edit: datasheet only specifies tf (fall time), but not tr (rise time); tf is specified at 300nSec max; the 'scope will show how tight this is, not possible with 5k2 pull-ups and 4 x I2C loading.

Related