I2C with FDC1004

This is a bit of a long one, but I am trying to be as thorough as possible…

I have been playing with the evaluation kit for the Texas Instruments FDC1004, which comes as 3 modular boards (a simple capacative sensor, the FDC1004 with bare minimum required hardware, and an I2C to USB converter). I started off by using all 3 modules together and interacting with it via the Sensing Solutions EVM GUI application provided by TI and everything was working well.

My next step was to ditch the I2C to USB section and GUI, instead connecting the module with the FDC1004 chip to an Uno’s SDA and SCL pins. I found a great library that took care of reading and writing the registers to give some nice high level functionality such as reading the capacitance of a channel in femto Farads. I was just using the example sketch that came with the library to output the measurements to the serial monitor, and this was working fine. So after initialisation, the function

printCapacitance(0);

would print the measured capacitance of channel 1 to serial, something like

77998,

Yesterday this inexplicably stopped working… I did not change the circuit (it’s just power and I2C connection to the Uno) and I’m still using the example from the library, which I also re-downloaded in case I had somehow corrupted part either the library or example. Instead of printing out the value of a measurement it prints

error
-2147483648,

every time, regardless of the channel selected. The weird thing is that if I detach the FDC1004 module from the Uno, and reattach to the I2C to USB from the eval kit, it responds as it should when using the GUI!

I have started looking into using the Wire library directly, and have managed to read out all the 16-bit registers which all return the correct POR values, so the I2C connection is OK. I want to set up the chip so that each of the 4 measurement registers are linked to each of the 4 capacitance inputs, and for continuous measurement (as opposed to requesting a one off measurement). The registers in question are in the table below.

Register **POR Val ** Desired Val
0x08 - CONF_MEAS1 0x1C00 0x1C00
0x09 - CONF_MEAS2 0x1C00 0x3C00
0x0A - CONF_MEAS3 0x1C00 0x5C00
0x0B - CONF_MEAS4 0x1C00 0x7C00
0x0C - FDC_CONF 0x0000 0x01F0

I can set registers 0x08 through 0x0B, then read them back and they are correct, but once I set 0x0C and then read back registers, all registers have reset to POR values. I have experimented by setting these values via the GUI when using the eval kit and it works as expected. I have tried setting various values for 0x0C via the Wire library that the GUI would accept, but each time I set it it seems to reset all registers.

I have another eval kit on the way in case I have damaged the chip somehow, but can anyone see a glaring mistake I have made? This is my first adventure into setting registers with Wire, so I could easily have messed up somewhere.

The sketch I am using to set and read register values is below:

#include <Wire.h>

bool check = true;

void setup() {
  Wire.begin();
  Serial.begin(9600);
  while (!Serial) {
    // do nothing
  }
  
  setRegister(0x08, 0x1C);      // CONF_MEAS1 to CIN1
  setRegister(0x09, 0x3C);      // CONF_MEAS2 to CIN2
  setRegister(0x0A, 0x5C);      // CONF_MEAS3 to CIN3
  setRegister(0x0B, 0x7C);      // CONF_MEAS4 to CIN4
  printSeconds();
  printAllRegisters();
  delay(1000);
  setRegister(0x0C, 0x01F0);
}

void loop() {
  printSeconds();
  printAllRegisters();
  delay(1000);
}


bool setRegister (uint8_t reg, uint16_t value) {
  Wire.beginTransmission(80);     // transmit to device #80
  Wire.write(reg);                // sets "reg"...
  Wire.write(value);              // ...to "value"
  Wire.endTransmission();         // end transmission
}

bool printSeconds () {
  Serial.println("\n\n======================");
  Serial.println((float)millis() / 1000,  2);
  Serial.println("======================");
}

bool printAllRegisters () {
  for (int i = 0; i <= 255; i++) {
    if (i <= 20 || i >= 254) {
      Serial.print("0x");
      if (i < 16) {
        Serial.print("0");
      }
      Serial.print(i,HEX);
      Serial.print(":\t0x");
      printRegister(i);
    }
  }
}

unsigned int readRegister (uint8_t reg) {
  Wire.beginTransmission(80);     // transmit to device #80
  Wire.write(reg);                // sets pointer to "i"
  Wire.endTransmission();         // end transmission
  
  Wire.requestFrom(80, 2);        // request 2 uint8_ts from slave device #80
  unsigned int i = Wire.read();

  return i;
}

bool printRegister (uint8_t reg) {
  Serial.println(readRegister(reg), HEX);
}

An example of the output this give is below:

======================
0.00
======================
0x00:	0x0
0x01:	0x0
0x02:	0x0
0x03:	0x0
0x04:	0x0
0x05:	0x0
0x06:	0x0
0x07:	0x0
0x08:	0x1C
0x09:	0x3C
0x0A:	0x5C
0x0B:	0x7C
0x0C:	0x0
0x0D:	0x0
0x0E:	0x0
0x0F:	0x0
0x10:	0x0
0x11:	0x40
0x12:	0x40
0x13:	0x40
0x14:	0x40
0xFE:	0x54
0xFF:	0x10


======================
1.26
======================
0x00:	0x0
0x01:	0x0
0x02:	0x0
0x03:	0x0
0x04:	0x0
0x05:	0x0
0x06:	0x0
0x07:	0x0
0x08:	0x1C
0x09:	0x1C
0x0A:	0x1C
0x0B:	0x1C
0x0C:	0x0
0x0D:	0x0
0x0E:	0x0
0x0F:	0x0
0x10:	0x0
0x11:	0x40
0x12:	0x40
0x13:	0x40
0x14:	0x40
0xFE:	0x54
0xFF:	0x10

If you haven't solved this, there are a couple problems.

  1. The FDC registers are 16 bits wide but below from your code you are writing 8 bits.

" setRegister(0x08, 0x1C); // CONF_MEAS1 to CIN1 setRegister(0x09, 0x3C); // CONF_MEAS2 to CIN2 setRegister(0x0A, 0x5C); // CONF_MEAS3 to CIN3 setRegister(0x0B, 0x7C); // CONF_MEAS4 to CIN"

Modify to: " setRegister(0x08, 0x1C00); // CONF_MEAS1 to CIN1 setRegister(0x09, 0x3C00); // CONF_MEAS2 to CIN2 setRegister(0x0A, 0x5C00); // CONF_MEAS3 to CIN3 setRegister(0x0B, 0x7C00); // CONF_MEAS4 to CIN"

2.You then need to modify your SetRegister routine to write the upper byte first and then the lower byte next since I2C only allows for 8 bit writes. See paragraph 8.5.2 in the spec sheet.

change SetRegister: from: "bool setRegister (uint8_t reg, uint16_t value) { Wire.beginTransmission(80); // transmit to device #80 Wire.write(reg); // sets "reg"... Wire.write(value); // ...to "value" Wire.endTransmission(); // end transmission }"

To: bool setRegister (uint8_t reg, uint16_t value) { Wire.beginTransmission(80); // transmit to device #80 Wire.write(reg); // sets "reg"... ** Wire.write((uint8_t) (value >> 8.)); // ...Write upper byte** Wire.write((uint8_t) (value); // ...Write lower byte Wire.endTransmission(); // end transmission }

problem is that you are trying to write 16 bits when I2C only supports 8 bit writes at a time. you are crashing the chip by not completing the writes cycles. If not too late try it.

I was able to implement this chip in my project.

My next step was to ditch the I2C to USB section and GUI, instead connecting the module with the FDC1004 chip to an Uno's SDA and SCL pins.

The FDC1004 is a 3V3 chip while the UNO runs on 5V. Although you can run the FDC1004 on 5V it is not in the recommended voltage range. If you connect the FDC1004 (running on 3V3) to the UNO directly (without a level converter) may damage the chip although the datasheet not explicitly prohibit it.

At least you need external pull-ups as the I2C bus length is probably more than a few centimeters so the internal pull-ups are to weak. I guess the problem is not your code but your circuitry.