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

  • Thank you for the suggestion, I'll try. By the way I'm sure the command does not fails because I get an answer from the device. You need to consider that I get 2 bytes back and all the other bits (especially the first byte that contains configuration bits such as sampling rate or measurement repetition) is correct. This is why I'm having headache :) 

  • Let's see what happens when you try the suggestions. Meanwhile, you can find other posts where insufficient pull-ups lead to a phantom clock-stretch due to the way the TWI nRF works; such a clock-stretch shifts some of the lower-order bits whilst leaving the high-order bits in situ and potentially generating a good ACK so all seems well. Test by using H0H1 SCK posted above.

    Edit: Just in case it's not clear how to set high drive mode, just start with this:

      // Ensure high drive I2C/TWI/TWIM mode before transaction
      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);
      // Perform measurement on channel 1
      fdc1004_triggerMeasure(FDC1004_MEAS1, true);

  • Hello again,

    unfortunately the scope was busy today, so i was not able to verify the proper shape of the signals on SDA/SCL (i'll do it on Monday). By the way i tested your idea. Unfortunately, no success Disappointed

    To be more specific this is what i did:

    #include <hal/nrf_gpio.h>
    
    
    ...
    ...
    {
    
        //OTHER STUFF HERE//
        
        nrf_gpio_cfg(NRF_GPIO_PIN_MAP(1, 3), 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(NRF_GPIO_PIN_MAP(1, 2), NRF_GPIO_PIN_DIR_INPUT, NRF_GPIO_PIN_INPUT_CONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0D1, NRF_GPIO_PIN_NOSENSE);
        
        fdc1004_triggerMeasure(FDC1004_MEAS1, true);
        while (!fdc1004_isMeasDone(FDC1004_MEAS1))
        {
            printk("Not done!\n");
            k_busy_wait(200000);
        }
        fdc1004_getCapacitance(FDC1004_MEAS1, &capLevel);
        printk("Done!\n");
    }

    I'm using NRF5340DK, with SCL/SDA on pin 1.03 and 1.02. Everything compiles, the software works normally, but all i can read is an infinite series of "Not Done!" debug print.

    Please note that, before performing this measurement, if i ask for device ID to both (FDC1004 and TCS3400) devices, they respond properly:

    [00:00:02.868,530] <inf> TCS3400 ID: 0x90
    [00:00:03.870,758] <inf> FDC1004 ID: 0x1004

    Again, if i remove specifically the TCS3400, everything works fine. Note that if i remove MCP9809 or LSM6DSOX or both but i keep TCS3400 with FDC1004, it doesn't work! It's something related to using TCS3400 and FDC1004 at the same time and ONLY with the function fdc1004_isMeasDone(). Please note that all the functions fdc1004_doSomeStuff() hides the communication routine fdc1004_read16(), that is always the same for ALL the (working) functions that need to read some registers.

  • Ok, very curious; does "remove specifically the TCS3400" mean simply unplug the device - physically remove, or recompile firmware with the device disabled in software, or both? If a change in software between the two tests is being made, does it behave the same without the change in software? Sorry to be pedantic.

  • You're not pedantic. Actually, you 're very supportive. This is really appreciated from my side! By the way, some cases:

    1) if I unplug the device from the breadboard leaving the software as is, fdc1004 works regularly ( triggerMeasurement()-> isMeasDone() -> get capacitance() sequence)

    2) if, while the software is stuck on the "is not done!" print, I unplug from the breadboard one of the following: SCL, SDA or VDD jumper wires, the FDC1004 immediately reacts and the trigger->isMeasDone->getCap sequence instantly work

Related