Trying to reduce I2C delay and communication time.

Greetings,

I am having an I2C speed issue. I have a code where my I2C communication is taking 6 milliseconds for 3 communications, pulse 5 milliseconds in delay commands for 11 millisecond in total (per channel). In general, the questions come down to:

  1. Is there a better way than delay() to wait for my data? The data sheet states a data setup time of 100ns, but I cannot call for a delay of 0.1ms.

  2. Can I just get rid of the delays?

  3. I sped up the I2C library clock to 400 kHz in wire.h. Is there a way to check this to make sure it’s working at 400 kHz?

The projects relates to reading the capacitance form a FDC1004 and writing this do a DAC. In total, I would like to get a cycle speed of 100Hz, but I am currently stuck at 20Hz. In general my code is running nicely, but the writing and reading from 4 channels over I2C is taking 45 milliseconds. Of this, 25 milliseconds is delay time, and 20 milliseconds is eaten up with the I2C transfer.
Here is what I have tried;

  1. I backed my delays down to 1milliseconds each, if I go to zero the code no longer outputs a voltage at my DAC.

  2. I changed the wire.h speed to 400 kHz. However, I am not sure that it is working at this speed as the directions I found on the forums stated I should delete the wire.o files and I could not find any .o files.

Here is the data sheet for the FDC1004:

And the link to the forums I used for changing the I2C clock speed:

Here is part of my code as the code is to long for a post. Notice that the I2C components are called in configure and getdata function found at the bottom of the code.

Thanks in advance for any help.

Code where I call the I2C function, each “block” takes 11 milliseconds in total

      Config(Ch1Register,Ch1Byte1,Ch1Byte2);
      Config(FDC_CONFIG,RegisterValue1,RegisterValueCh1); 
      delay(del);
      Data1=getdata(0x00);
      time2 = millis();
      
      Config(Ch2Register,Ch2Byte1,Ch2Byte2);
      Config(FDC_CONFIG,RegisterValue1,RegisterValueCh2);
      delay(del);
      Data2=getdata(0x02);
 
 
      Config(Ch3Register,Ch3Byte1,Ch3Byte2);
      Config(FDC_CONFIG,RegisterValue1,RegisterValueCh3);
      delay(del);
      Data3=getdata(0x04);
 
 
      Config(Ch4Register,Ch4Byte1,Ch4Byte2);
      Config(FDC_CONFIG,RegisterValue1,RegisterValueCh4);
      delay(del);
      Data4=getdata(0x06);

Here are the functions called in the code before.

void Config(byte addresse, byte val1, byte val2)
{
  Wire.beginTransmission(FDC);
  Wire.write(addresse);
  Wire.write(val1);
  Wire.write(val2);
  Wire.endTransmission();
  delay (del);

}

unsigned long getdata(byte pointer)
{
  unsigned int a,b;
  unsigned long c=0;
  a = getinfo(pointer);
  delay(del);
  b = getinfo(pointer+1);
  delay(del);
  c=a;
  c=c<<16;
  c|=b;
  // Serial.print(a,HEX);
  // Serial.print(b,HEX);
  // Serial.print(c,HEX);
  return(c);

}
  1. Is there a better way than delay() to wait for my data?

Of course. Register an onReceive handler. It will be called when data arrives. No need to wait for anything.

  1. Can I just get rid of the delays?

You can get rid of the delay()s. In some cases, you have useless time-wasting delay()s that you can just delete. In some cases, you can't do something until something else happens. Just getting rid of the delay()(s) will NOT make the event you are waiting for happen any faster, so it is not just a matter of getting rid of the delay()(s), though that is necessary.

tex_downey, are you using the newest Arduino IDE 1.6.6 ? (1.6.7 at december 17).
Set the Wire.h to its original, and use the function: Wire.setClock ( 400000L ) ;

However, using a 400kHz I2C bus is not very usefull if you have delays.

A delay of 0.1ms is possible with: delayMicroseconds ( 100 ) ;

Why do you need delays ? Is that in the datasheet ?
There are chips that require a delay, but I'm not sure about this one.
I really searched well, but I can't get any confirmation that the FDC1004 requires delays.

In the datasheet they say: "The FDC1004 provides an internal 300 ns minimum hold time to bridge the undefined region of the falling edge of SCL".
I don't understand that. If they mean clock pulse stretching, then the Arduino automatically accepts that.

Do you use a library ? which library ?
Please show us the whole sketch.

PaulS

Thanks for your advice.

  1. I was unaware of the Wire.onReceive function. I will incorporate this into the code today.

  2. I went through all of my delays, and I think in some form they are all needed. I tried to reduce them using the delayMicroseconds() command. On some I was able to reduce the delay to 800 microseconds, but others seem to be required for the code to run right.

Thanks Again,
Austin.

Koepel, :slight_smile:

are you using the newest Arduino IDE 1.6.6 ?

No, I am not. I am on 1.0.6, I will try to update this today, and use the Wire.setClock command.

A delay of 0.1ms is possible with: delayMicroseconds ( 100 )

Thanks, I have updated my code. But it seems that I can only drop a few of my delays to 800 microseconds.

Why do you need delays ? Is that in the datasheet ?
There are chips that require a delay, but I’m not sure about this one.
I really searched well, but I can’t get any confirmation that the FDC1004 requires delays.

I agree, I do not know why I need delays. However, if I run my code without the delays, my output locks up. I do not know why I need delays, but I know I need them.

Do you use a library ? which library ?
Please show us the whole sketch.

My libraries are <Wire.h> and <SPI.h>. I have included my code as an attachment.

Thanks again,

I think I need to try to rework my code so I only have to configure each channel once, and than auto increment though all of the registers. I think this would save a lot of time. However, the data sheet in section “8.5.3.1 Measurement Configuration” says

. Configuring only one measurement is allowed, and it can be one of the 4 possible measurement configurations.

I took this as if I want to sample from all four channels, I need to configure each channel before I sample from it. This is why my code reconfigures for every channel on every loop.

Thanks in advance for the help.

fdc1004_fast_try.ino (10.7 KB)

You use the delays because else the output locks up ? You mean that the serial output to the serial monitor just stops ?
If the I2C bus is stuck, then the sketch could halt.

I think the delays are wrong, please remove all of them.

You have to check your I2C bus, probably something is not right with the hardware. Can you make a photo ? You may not use flat ribbon cable and have SDA and SCL next to each other. Never use a twisted pair for SDA and SCL. Any other cable longer than 50cm will probably cause trouble as well. For some cables perhaps 20cm is already too much.

About your sketch:

  • Please do not redefine lowByte and highByte. That is dangerous. Please remove those. They already exist : highByte() - Arduino Reference
  • It is preferred to have "const int analogPin = A0 ;"
  • As soon as you set the 'slaveSelectPin' to OUTPUT, it will be OUTPUT and LOW. Low is active. After making it OUTPUT, please set it HIGH and wait a millisecond.
  • Did I read about repeated start in the datasheet ? I forgot, I think I read that. Then you have to add that to Wire.endTransmission in 'conversion()'. And remove all the delays of course.

At this moment it is pointing towards your hardware : a bad I2C bus due to a cable, or missing decoupling capacitor for the FDC1004, or ground currents or a bad breadboard.

Koepel,

First, I have updated my IDE to 1.6.6. Thanks for that advice.

You use the delays because else the output locks up ? You mean that the serial output to the serial monitor just stops ?
If the I2C bus is stuck, then the sketch could halt.

What happens is when my sketch has delays applied it operates, and outputs as expected. Here is an example of what I get on my serial monitor.

Returned as

[time at start of I2C communication , time at end to I2C communication, time at end of sketch, output to 10 bit DAC channels 1:4, analog read from a0]

45 , 89 , 90 , 575 , 0 , 0 , 0 , 2.81

Here My I2C communication takes 44 mS. and the rest of the code takes 1mS. If I turn off all my delays I get

5 , 8 , 8 , 575 , 0 , 0 , 0 , 2.81

My code still runs and cycles though the loop just fine.

We can see my I2C communication time dropped from 44 milliseconds to 3 milliseconds. This is great, but the values for my analog out in channel one and my voltage in are now constant. By this I mean if I change the value of the capacitor in channel 1, the reported valves do not change. (e.g. I have a 80 pF capacitor hooked up to channel 1, and the serieal port will return 511 for ch1. if I remove the capacitor the reading will remain at 511. Nothing I do to the capacitors on any channel will change the reading being reported by the chip.

Does that clear up what I mean by “locks up”?

You have to check your I2C bus, probably something is not right with the hardware. Can you make a photo ? You may not use flat ribbon cable and have SDA and SCL next to each other. Never use a twisted pair for SDA and SCL. Any other cable longer than 50cm will probably cause trouble as well. For some cables perhaps 20cm is already too much.

I have attached a png of my setup. Good to know about sda and scl being separate.

I again have just a breakout board for the FDC 1004 chip. I plan is to add all the needed passive parts when I do my board layout.

About your sketch:

  1. Please do not redefine lowByte and highByte. That is dangerous. Please remove those. They already exist : https://www.arduino.cc/en/Reference/HighByte

Thanks, I should know all of my functions better. I will read though all of them this week.

It is preferred to have “const int analogPin = A0 ;”

Again, I should of known this.

As soon as you set the ‘slaveSelectPin’ to OUTPUT, it will be OUTPUT and LOW. Low is active. After making it OUTPUT, please set it HIGH and wait a millisecond.

How did this code ever work with it starting on low. :confused: Good thing I only have one SPI chip for now.

Did I read about repeated start in the datasheet ? I forgot, I think I read that. Then you have to add that to Wire.endTransmission in ‘conversion()’. And remove all the delays of course.

Hmm, I don’t see that in the data sheet. You did help me with another CDAC on a different thread that needed a repeat start. Is this what you are referring to.

Also, I never call conversion() I have commented it out on the updated sketch. (attached)

At this moment it is pointing towards your hardware : a bad I2C bus due to a cable, or missing decoupling capacitor for the FDC1004, or ground currents or a bad breadboard.

I see why you would think that.

  1. I do not have a decoupling cap between the 1004 Vdd and ground. I was relying on the Uno to do this. I assume this is a bad idea?

  2. I thought my communication was working fine, just slow. But now I am not sure as sketch seems to return only 30 values, instead of the 1000 that I should get from a 10 bit DAC. If I touch my cap on Ch 1, I get the following voltages in very large, discrete steps 2.81, 2.96.3.12,3.27,3.43… Seems to only output 150 mv steps. I will look into this tonight.

Thanks again,

fdc1004_fast_try_1_6_6.ino (11.1 KB)

Sorry, I had to move the test setup picture to a different reply as it was to large.

Ok,

Quick update. While the DAC only updates in steps of 0.15 volts, it seems my CDC is working just fine. :slight_smile: :slight_smile: I have attached a .jpg if anyone cares to see the resolution of the FDC1004 as I have it set up.

Austin.

1004 cap.jpg

Use 4k7 pullup resistors, often 10k will work, but 33k is a little high.

Can you add a decoupling capacitor of 100nF next to the chip ? near the yellow wires.

I was confused with the "locks up". You mean the values from the sensor are wrong.
Then I will change my mind :wink: It is not hardware and perhaps a delay is needed because the chip needs a little delay to measure the newest data and store it into the registers.

Then it has nothing to do with I2C, so you must remove all the delays anyway. You need to wait for the sensor before requesting data.

Have a look at line 137 of this code Adafruit_VCNL4010/Adafruit_VCNL4010.cpp at master · adafruit/Adafruit_VCNL4010 · GitHub
It is weird, but I guess some chips do need a delay.

Also have a look at line 108 and 116 of this code Adafruit_MPR121/Adafruit_MPR121.cpp at master · adafruit/Adafruit_MPR121 · GitHub
It keeps requesting 1 or 2 bytes until the chip actually returns that number of requested bytes. That means that the chip is busy measuring a new sample.

The FDC1004 has an update rate of 100, 200 or 400 samples per second. With 400 S/s, you should wait 2.5ms.

I do see a repeated start in the datasheet.

unsigned int getinfo(byte pointer)
{
  int a,b;
  unsigned int c;
  Wire.beginTransmission(FDC);
  Wire.write(pointer);
  Wire.endTransmission(false);    // false for repeated start
  Wire.requestFrom(FDC,2);
  a=Wire.read();
  b=Wire.read();
  c=word(a,b);       // high, low
  return(c);
}

word : word() - Arduino Reference

Do you know some clever way to read data when the chip has the data ready ?
Perhaps a software timer with millis().

MAJOR UPDATE :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:

Koepel,

Thanks for all your help I think we are close, at least with this version of the code. :slight_smile:

Firsts, I added a filter cap. The pull-up resistors where 10K, I am not sure what happened yesterday when I measured one. Opps, my bad. :roll_eyes:

I played around with the code in its current format all day. Here is what I know as of now.

  1. I still need the delays or the code output is constant. (code set to 100 S/s).
  2. If I change the sample time to 400 S/s and delete a few delays here and there my output starts to work again.
  3. The placement of my delays makes no sense to me and I think this is where I should focus next. (I also think I should try a different sensor config / read order)

If I look at the 1004 data sheet in section 8.5.3I am told that I need to

  1. Configure measurements (for details, refer to Measurement Configuration).
  2. Trigger a measurement set (for details, refer to Triggering Measurements).
  3. Wait for measurement completion (for details, refer to Wait for Measurement Completion).
  4. Read measurement data (for details, refer to Read of Measurement Result).

In my code I do this for each channel, for every loop of the code.

In my code I configure the channel first (e.g. line 191)
I than configure the CDC and trigger one sample. (e.g. line 192)
I wait, kinda. My delays are used to wait, but the are spread throughout the code and this is the problem with simply deleting them.
I read the data using my getinfo command. (e.g. line 195)

To fix the delay problem I went though the code and deleted all the delay() commands. I than added a delay(FDCWait) to line 173, and the other 4 related lines. This delay is equal to the sampling rate used. So a 100 S/s rate will work with a 11mS delay but will lock up the output with a 9Ms delay.

If I up the sampling to 400 Hz (see code line 45 and 114) and drop the wait time to 3 mS the code still runs, but the total cycle speed does not increase. (due to my serial port speed)

The config register on the FDC1004 has 4 bits [0:3] that display if the data is ready to be read. I built the function waitData() that I can pass the channel number to and it cycles until that channel’s bit turns to a 1. The code seems to be working.

I still had a speed problem at this point (max of 15Hz). So I upped my Baud rate on the Arduino to 250,000. This seemed to help, and to the best of my knowledge I can now get 71 Hz out of the code.

I think the Serial port is still slowing me down a lot. If I want to display all four cap channels the best I can get is 60 Hz, if I just print a time stamp I get 71 Hz. I don’t need the serial port for for this application so I am not that worried. However, I do need to know my sampling rate so I don’t over sample the data at the DAC. Would the best way to find this be to right a HIGH, and a LOW to a digital out and read it with my scope?

Here is my waitData function. I wonder if there is a faster way to see if the I2C is ready to send data? Maybe this function could be speed up? With the Serial.print(c) enabled in the function it takes 0.25mS to cycle.

I am at the point where 1Ms would add 6Hz so every thing helps.

void waitData(int ChReady)
{
  byte a,b,d;
  int c = 0;
  int ReadyBit = 4 - ChReady;  // converts the channel number (1-4) to the correct value for the corresponding DONE_x field (Register 0x0C:bits[3:0])
      Wire.beginTransmission(FDC);
      Wire.write(FDC_CONFIG);
      Wire.endTransmission(false);  // false for repeated start
   while(c==0)
      {
      Wire.requestFrom(FDC,2);
      a=Wire.read();
      b=Wire.read();
      c = bitRead(b,ReadyBit);
      //Serial.print("c  -  ");
      //Serial.println(c);
      }
}

At 400Hz my max speed would be 100Hz minus the time for the rest of the code. I still would like to speed up the code, so I will continue to work on it.

Thanks again for all the help. I will post back if I can get the speed up, or with a link to the final project.

Austin.

fdc1004_fast_try_1_6_6.ino (15 KB)

The sample rate is "free running" I think ? Without specific timing. Therefor all the Serial.print will slow it down.

???

Not going to lie, I am lost on that one.

Side note:

If I set the FDC chip speed to 100 Hz, and only have one Serial.print I can get a speed of 21Hz per channel. So not quite the 25Hz / Ch. But close enough for how.

I ment that you don't use a hardware timer or software timer for the sample rate. The loop() just runs without specific sampling interval, it is only slowed down by the code and the delays. I called it "free running", but I don't know if that is the right English term for it.

ahhh,

Got it. Thanks.

Hi,

I have two sensors sht21 & sht31 and i'm trying to read temperature and humidity via i2c simultaneously.
Please, can you tell me how to do this.I can read data only from sht31 with SHT31test wich is build in arduino library,but i want to read from two sensors sht with diferent i2c addrese 64 and 68.

I'll be greatfull if you help me with some sketch,

Nick.

I have two sensors sht21 & sht31 and i'm trying to read temperature and humidity via i2c simultaneously.

You can NOT read two sensors simultaneously. You can't do ANY two things simultaneously on an Arduino.

Come on back when you have realistic expectations.

While TC1 is counting external pulses, the MCU is busy in the computation; are there not two tasks being carried out simultaneously?

GolamMostafa:
While TC1 is counting external pulses, the MCU is busy in the computation; are there not two tasks being carried out simultaneously?

No; two lumps of hardware are doing their jobs - one is running the user's code, the other is counting pulses