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

Parents
  • 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

  • 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

  • I'll look at this again; meanwhile I realise the nRF5340 has a special setting for high-drive open-drain I2C/TWI/TWIM: E0E1; perhaps try this if possible (note E0E1 for I2C is supposed to be open-drain, though the port CNF description in the product description is confused on this point: "E0E1 11 Extra high drive '0', extra high drive '1'")

    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_E0E1, 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_E0E1, NRF_GPIO_PIN_NOSENSE);

    This only applies to the pins P1.02 and P1.03 which you are using.

    More Regarding the nRF5340 DK: 

    First there are two 4k7 pull-up resistors affected by plugging in an Arduino-compatible board:
    "When a shield is connected, two analog switches connect the two 4k7 pull-up resistors to the I2C bus lines (SDA and SCL). This function uses one ground pin on the Arduino shield to control the switch. This feature can be disabled by cutting SB33. To permanently enable pull-up resistors, short SB32"


    Second the I2C/TWI/TWIM pins are not just connected to the user nRF5340 but also to the interface nRF5340, and we have no idea what that does and no way to isolate them - this is a question for the Nordic team

    User nRF5340 U1        Interface nRF5340 U2 Arduino Board            On-board Pull-ups        Connectors
    ====================== ==================== ======================== ======================== =================================
    U1 P1.02 AE1 P1.02/I2C U2 P1.02 AE1         via 0R0 R20 to P1.02 SDA R48 4k7 PU via U6 switch P10 pin  9  P4 pin  9  P16 pin 18
    U1 P1.03 AF2 P1.03/I2C U2 P1.03 AF2         via 0R0 R24 to P1.03 SCL R49 4k7 PU via U6 switch P10 pin 10  P4 pin 10  P16 pin 20

    I think a schematic of the breadboard might be useful. nRF5340 DK schematic is here:

    nrf5340-development-kit---hardware-files-2_0_0

  • I'll test everything on Monday, when I go back to work. I'll even provide a picture of the breadboard. By the way, it's a really simple configuration where I use 4 horizontal long lines to have VDD, GND, SCL and SDA and jumper wires to connect every sensor breakout to the 4 lines. The only extra connection is for two resistors that goes between VDD and SCL/SDA lines that I use to modulate the total value of the pull-up resistors. This is the list of the breakouts I am using:

    1) TCS3400 (of course only the sensor part, not the AMS interface)

    2) MCP9808

    3) LSM6DSOX

    4) FDC1004 (in this case I broke the board in three parts and soldered some pin stripe on the one with the sensor)

    As you can see, some of the breakout has already pull-up resistors on it, some has not. I evaluate the total just using a multimeter between SCL/SDA line and VDD, being all of them in parallel

    Again, thank you Slight smile

Reply
  • I'll test everything on Monday, when I go back to work. I'll even provide a picture of the breadboard. By the way, it's a really simple configuration where I use 4 horizontal long lines to have VDD, GND, SCL and SDA and jumper wires to connect every sensor breakout to the 4 lines. The only extra connection is for two resistors that goes between VDD and SCL/SDA lines that I use to modulate the total value of the pull-up resistors. This is the list of the breakouts I am using:

    1) TCS3400 (of course only the sensor part, not the AMS interface)

    2) MCP9808

    3) LSM6DSOX

    4) FDC1004 (in this case I broke the board in three parts and soldered some pin stripe on the one with the sensor)

    As you can see, some of the breakout has already pull-up resistors on it, some has not. I evaluate the total just using a multimeter between SCL/SDA line and VDD, being all of them in parallel

    Again, thank you Slight smile

Children
  • As an aside "4 horizontal long lines" in general means serious capacitive loading which in turn means high-drive becomes more of an issue.

    I raised this issue in a separate ticket to get a Nordic response:

    nrf5340-conflicting-documentation-on-i2c-aka-twi-twim

  • Hello,

    i tried the drive setting you suggested (EOE1), without any change on the final result.

    I even acquired the I2C plots that you can see here below. The plots are obtained with the following settings:

    - I2C speed = 100kHz (i never specified but it was always 100kHz in ALL the experiments)

    - Normal drive (no H or E settings on the pins as you suggested because i was interested in verifying the shape of the I2C SDA line).

    The plots show the behaviour of the FDC1004 during the I2C transaction related to the "isMeasDone()" function.

    As you can see, aside from the fact that unplugging the TCS3400 (bottom plot) the "guilty bit" triggers, there are no other remarkable difference in the quality of the shape (no loading effect).

    I'm confident in saying that, whatever the issue is, it's not related to loading effect. My head hurts...

    Frax

  • Hello Frax,

    What about the SCL line?

    Could you measure both at the same time?

    Best regards,

    Michal

  • Yes, without the SCL to mark the bits it's hard to decode; assuming only Meas 1 is enabled and complete there should be a '1' for only meas 1 and not 2, 3 and 4 but I'm not seeing that in the traces above (al least not without SCL as a guide). Does the 'scope have an I2C decode function? That would obviously help.

    With the TCS3400 in place, does shorting the capacitive sense CIN or guard SHLD pin to Gnd have the same adverse effect as removing the TSC3400? Maybe something is stopping the measure clock which in turn defeats the completion of measurement.

  • Dear  ,  

    here below you can find what you asked. Unfortunately my scope has not I2C decode function Disappointed

    There is something i'm not convinced on watching these plots. I had no time to review them carefully so i'm taking tomorrow for that, but if you compare these plots with the previous ones you will notice a significant difference between the "TCS3400 plugged in" case of yesterday and today for one bit. They have been taken in identical conditions, so i expect them to be identical. I need to investigate it.

    The plots refer to a I2C read request (isMeasDone() function) to the register FDC_CONF (address 0x0C) of the FDC1004 device (I2C address 0x50). The register is structured as follows:

    The configuration of this register just before the "isMeasDone()" function is: 100Sample/s, Repeat Meas enabled,  MEAS_1 Initiate measurement enabled. 

    Another thing i want to try tomorrow is to power down the TCS3400 while measuring with FDC1004. Cannot test it right now because i didn't write the function to power down TCS3400. It would be a workaround and not a solution, but it could provide some useful info.

    Last but not least, i got another FDC1004 chip and i want to try to use that one to see if anything changes. Low chances, but it comes for free.

    I'll keep you updated!

    Very thanks for the continous support,

    Frax

Related