High Speed (HS) I2C with INA226

Hello everybody,

I am doing a project where I need to get fast current and voltage and to store it in arrays. I would like to reach MHz (don't know if possible). Therefor I choosed INA226 because it is pretty accurate and can handle high speed (HS) mode (at least the datasheet says that: http://www.ti.com/product/INA226/datasheet ).
I found this nice library already made for Arduino and INA226 GitHub - jarzebski/Arduino-INA226: INA226 Bi-directional Current/Power Monitor Arduino Librar. I use it and it works, but I cant speed it up.
I was googleing a lot around and tried to change twi.h, wire.h and ina226.h / ina226.ccp and TWBR, but it all didnt help.
I controlled the speed by using millis().

Here is my simple code to try if I can reach HS (Wire.setClock(xxxxxx) didn't work as well):

/*
    INA226 Bi-directional Current/Power Monitor. Simple Example.
    Read more: http://www.jarzebski.pl/arduino/czujniki-i-sensory/cyfrowy-czujnik-pradu-mocy-ina226.html
    GIT: https://github.com/jarzebski/Arduino-INA226
    Web: http://www.jarzebski.pl
    (c) 2014 by Korneliusz Jarzebski
*/

#include <Wire.h>
#include <INA226.h>

INA226 ina;

void checkConfig()
{
  Serial.print("Mode:                  ");
  switch (ina.getMode())
  {
    case INA226_MODE_POWER_DOWN:      Serial.println("Power-Down"); break;
    case INA226_MODE_SHUNT_TRIG:      Serial.println("Shunt Voltage, Triggered"); break;
    case INA226_MODE_BUS_TRIG:        Serial.println("Bus Voltage, Triggered"); break;
    case INA226_MODE_SHUNT_BUS_TRIG:  Serial.println("Shunt and Bus, Triggered"); break;
    case INA226_MODE_ADC_OFF:         Serial.println("ADC Off"); break;
    case INA226_MODE_SHUNT_CONT:      Serial.println("Shunt Voltage, Continuous"); break;
    case INA226_MODE_BUS_CONT:        Serial.println("Bus Voltage, Continuous"); break;
    case INA226_MODE_SHUNT_BUS_CONT:  Serial.println("Shunt and Bus, Continuous"); break;
    default: Serial.println("unknown");
  }
  
  Serial.print("Samples average:       ");
  switch (ina.getAverages())
  {
    case INA226_AVERAGES_1:           Serial.println("1 sample"); break;
    case INA226_AVERAGES_4:           Serial.println("4 samples"); break;
    case INA226_AVERAGES_16:          Serial.println("16 samples"); break;
    case INA226_AVERAGES_64:          Serial.println("64 samples"); break;
    case INA226_AVERAGES_128:         Serial.println("128 samples"); break;
    case INA226_AVERAGES_256:         Serial.println("256 samples"); break;
    case INA226_AVERAGES_512:         Serial.println("512 samples"); break;
    case INA226_AVERAGES_1024:        Serial.println("1024 samples"); break;
    default: Serial.println("unknown");
  }

  Serial.print("Bus conversion time:   ");
  switch (ina.getBusConversionTime())
  {
    case INA226_BUS_CONV_TIME_140US:  Serial.println("140uS"); break;
    case INA226_BUS_CONV_TIME_204US:  Serial.println("204uS"); break;
    case INA226_BUS_CONV_TIME_332US:  Serial.println("332uS"); break;
    case INA226_BUS_CONV_TIME_588US:  Serial.println("558uS"); break;
    case INA226_BUS_CONV_TIME_1100US: Serial.println("1.100ms"); break;
    case INA226_BUS_CONV_TIME_2116US: Serial.println("2.116ms"); break;
    case INA226_BUS_CONV_TIME_4156US: Serial.println("4.156ms"); break;
    case INA226_BUS_CONV_TIME_8244US: Serial.println("8.244ms"); break;
    default: Serial.println("unknown");
  }

  Serial.print("Shunt conversion time: ");
  switch (ina.getShuntConversionTime())
  {
    case INA226_SHUNT_CONV_TIME_140US:  Serial.println("140uS"); break;
    case INA226_SHUNT_CONV_TIME_204US:  Serial.println("204uS"); break;
    case INA226_SHUNT_CONV_TIME_332US:  Serial.println("332uS"); break;
    case INA226_SHUNT_CONV_TIME_588US:  Serial.println("558uS"); break;
    case INA226_SHUNT_CONV_TIME_1100US: Serial.println("1.100ms"); break;
    case INA226_SHUNT_CONV_TIME_2116US: Serial.println("2.116ms"); break;
    case INA226_SHUNT_CONV_TIME_4156US: Serial.println("4.156ms"); break;
    case INA226_SHUNT_CONV_TIME_8244US: Serial.println("8.244ms"); break;
    default: Serial.println("unknown");
  }
  
  Serial.print("Max possible current:  ");
  Serial.print(ina.getMaxPossibleCurrent());
  Serial.println(" A");

  Serial.print("Max current:           ");
  Serial.print(ina.getMaxCurrent());
  Serial.println(" A");

  Serial.print("Max shunt voltage:     ");
  Serial.print(ina.getMaxShuntVoltage());
  Serial.println(" V");

  Serial.print("Max power:             ");
  Serial.print(ina.getMaxPower());
  Serial.println(" W");
}

void setup() 
{
  Serial.begin(115200);

  Serial.println("Initialize INA226");
  Serial.println("-----------------------------------------------");

  // Default INA226 address is 0x40
  ina.begin();
 

  // Configure INA226
  ina.configure(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_140US, INA226_SHUNT_CONV_TIME_140US, INA226_MODE_SHUNT_BUS_CONT);

  // Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
  ina.calibrate(0.004356, 5);

  // Display configuration
  checkConfig();

  Serial.println("-----------------------------------------------");
  
int l;
float v_data[l];
float i_data[l];
}

void loop()
{
float v;
float i;
int l=10000;
float v_data[l];
float i_data[l];

unsigned long previousMillis= millis();
unsigned long currentMillis = millis();
Serial.println(currentMillis - previousMillis);

for (int g = 0; g < l; g = g + 1) {
  v = ina.readBusVoltage(), 3;//g+0.0001;//
  i = ina.readShuntCurrent(), 3;//g+1.001;//
  v_data[g]=v;
  i_data[g]=i;
}

currentMillis = millis();

Serial.println(currentMillis - previousMillis);

Serial.print("Bus voltage:   ");
Serial.print("\t");
Serial.print(v_data[8888], 3);
Serial.println(" V");
Serial.print("\n");

Serial.print("Shunt current: ");
Serial.print("\t");
Serial.print(i_data[8888], 3);
Serial.println(" A");
Serial.print("\n");

delay(500);
}

I would be really happy about any kind of help and suggestion. I am on one's last legs.. >:( ..:stuck_out_tongue:

Thank you in advance!

PS: The *.ino data is attached.

INA226_SpeedTest.ino (5.25 KB)

I would be even happy if I could reach 10kHz. But now each element needs 1ms so for two different values it is a frequency of 2ms per pair.

And by the way...I use an Arduino Due.

You might give my multispeed I2C scanner a try to see what speeds works. Check

Hello Robtillaart,

thx for your code and help!
I checked my INA226 with Due and it says:

TIME	DEC	HEX		50	100	200	250	400	500	800	[KHz]
------------------------------------------------------------------------------------------------
24614	0	0x00		.	.	.	.	.	.	.
24616	1	0x01		.	.	.	.	.	.	.
24619	2	0x02		.	.	.	.	.	.	.
24621	3	0x03		.	.	.	.	.	.	.
24624	4	0x04		.	.	.	.	.	.	.
24626	5	0x05		.	.	.	.	.	.	.
24628	6	0x06		.	.	.	.	.	.	.
24631	7	0x07		.	.	.	.	.	.	.
24633	8	0x08		.	.	.	.	.	.	.
24636	9	0x09		.	.	.	.	.	.	.
24638	10	0x0A		.	.	.	.	.	.	.
24641	11	0x0B		.	.	.	.	.	.	.
24644	12	0x0C		.	.	.	.	.	.	.
24646	13	0x0D		.	.	.	.	.	.	.
24649	14	0x0E		.	.	.	.	.	.	.
24651	15	0x0F		.	.	.	.	.	.	.
24654	16	0x10		.	.	.	.	.	.	.
24656	17	0x11		.	.	.	.	.	.	.
24659	18	0x12		.	.	.	.	.	.	.
24662	19	0x13		.	.	.	.	.	.	.
24664	20	0x14		.	.	.	.	.	.	.
24667	21	0x15		.	.	.	.	.	.	.
24669	22	0x16		.	.	.	.	.	.	.
24672	23	0x17		.	.	.	.	.	.	.
24674	24	0x18		.	.	.	.	.	.	.
24677	25	0x19		.	.	.	.	.	.	.
24680	26	0x1A		.	.	.	.	.	.	.
24682	27	0x1B		.	.	.	.	.	.	.
24685	28	0x1C		.	.	.	.	.	.	.
24687	29	0x1D		.	.	.	.	.	.	.
24690	30	0x1E		.	.	.	.	.	.	.
24692	31	0x1F		.	.	.	.	.	.	.
24695	32	0x20		.	.	.	.	.	.	.
24698	33	0x21		.	.	.	.	.	.	.
24700	34	0x22		.	.	.	.	.	.	.
24703	35	0x23		.	.	.	.	.	.	.
24705	36	0x24		.	.	.	.	.	.	.
24708	37	0x25		.	.	.	.	.	.	.
24710	38	0x26		.	.	.	.	.	.	.
24713	39	0x27		.	.	.	.	.	.	.
24716	40	0x28		.	.	.	.	.	.	.
24718	41	0x29		.	.	.	.	.	.	.
24721	42	0x2A		.	.	.	.	.	.	.
24723	43	0x2B		.	.	.	.	.	.	.
24726	44	0x2C		.	.	.	.	.	.	.
24728	45	0x2D		.	.	.	.	.	.	.
24731	46	0x2E		.	.	.	.	.	.	.
24734	47	0x2F		.	.	.	.	.	.	.
24736	48	0x30		.	.	.	.	.	.	.
24739	49	0x31		.	.	.	.	.	.	.
24741	50	0x32		.	.	.	.	.	.	.
24744	51	0x33		.	.	.	.	.	.	.
24746	52	0x34		.	.	.	.	.	.	.
24749	53	0x35		.	.	.	.	.	.	.
24752	54	0x36		.	.	.	.	.	.	.
24754	55	0x37		.	.	.	.	.	.	.
24757	56	0x38		.	.	.	.	.	.	.
24759	57	0x39		.	.	.	.	.	.	.
24762	58	0x3A		.	.	.	.	.	.	.
24764	59	0x3B		.	.	.	.	.	.	.
24767	60	0x3C		.	.	.	.	.	.	.
24770	61	0x3D		.	.	.	.	.	.	.
24772	62	0x3E		.	.	.	.	.	.	.
24775	63	0x3F		.	.	.	.	.	.	.
24778	64	0x40		V	V	V	V	V	V	V
24780	65	0x41		.	.	.	.	.	.	.
24782	66	0x42		.	.	.	.	.	.	.
24785	67	0x43		.	.	.	.	.	.	.
24788	68	0x44		.	.	.	.	.	.	.
24790	69	0x45		.	.	.	.	.	.	.
24793	70	0x46		.	.	.	.	.	.	.
24795	71	0x47		.	.	.	.	.	.	.
24798	72	0x48		.	.	.	.	.	.	.
24800	73	0x49		.	.	.	.	.	.	.
24803	74	0x4A		.	.	.	.	.	.	.
24806	75	0x4B		.	.	.	.	.	.	.
24808	76	0x4C		.	.	.	.	.	.	.
24811	77	0x4D		.	.	.	.	.	.	.
24813	78	0x4E		.	.	.	.	.	.	.
24816	79	0x4F		.	.	.	.	.	.	.
24819	80	0x50		V	V	V	V	V	V	V
24821	81	0x51		.	.	.	.	.	.	.
24824	82	0x52		.	.	.	.	.	.	.
24826	83	0x53		.	.	.	.	.	.	.
24829	84	0x54		.	.	.	.	.	.	.
24831	85	0x55		.	.	.	.	.	.	.
24834	86	0x56		.	.	.	.	.	.	.
24836	87	0x57		.	.	.	.	.	.	.
24839	88	0x58		.	.	.	.	.	.	.
24842	89	0x59		.	.	.	.	.	.	.
24844	90	0x5A		.	.	.	.	.	.	.
24847	91	0x5B		.	.	.	.	.	.	.
24849	92	0x5C		.	.	.	.	.	.	.
24852	93	0x5D		.	.	.	.	.	.	.
24854	94	0x5E		.	.	.	.	.	.	.
24857	95	0x5F		.	.	.	.	.	.	.
24860	96	0x60		.	.	.	.	.	.	.
24862	97	0x61		.	.	.	.	.	.	.
24865	98	0x62		.	.	.	.	.	.	.
24867	99	0x63		.	.	.	.	.	.	.
24870	100	0x64		.	.	.	.	.	.	.
24873	101	0x65		.	.	.	.	.	.	.
24875	102	0x66		.	.	.	.	.	.	.
24878	103	0x67		.	.	.	.	.	.	.
24880	104	0x68		.	.	.	.	.	.	.
24883	105	0x69		.	.	.	.	.	.	.
24886	106	0x6A		.	.	.	.	.	.	.
24888	107	0x6B		.	.	.	.	.	.	.
24891	108	0x6C		.	.	.	.	.	.	.
24894	109	0x6D		.	.	.	.	.	.	.
24896	110	0x6E		.	.	.	.	.	.	.
24899	111	0x6F		.	.	.	.	.	.	.
24902	112	0x70		.	.	.	.	.	.	.
24904	113	0x71		.	.	.	.	.	.	.
24907	114	0x72		.	.	.	.	.	.	.
24910	115	0x73		.	.	.	.	.	.	.
24912	116	0x74		.	.	.	.	.	.	.
24915	117	0x75		.	.	.	.	.	.	.
24918	118	0x76		.	.	.	.	.	.	.
24920	119	0x77		.	.	.	.	.	.	.
24923	120	0x78		.	.	.	.	.	.	.
24926	121	0x79		.	.	.	.	.	.	.
24928	122	0x7A		.	.	.	.	.	.	.
24931	123	0x7B		.	.	.	.	.	.	.
24934	124	0x7C		.	.	.	.	.	.	.
24936	125	0x7D		.	.	.	.	.	.	.
24939	126	0x7E		.	.	.	.	.	.	.
24942	127	0x7F		.	.	.	.	.	.	.

2 devices found in 332 milliseconds.
<print=found>
TIME	DEC	HEX		50	100	200	250	400	500	800	[KHz]
------------------------------------------------------------------------------------------------
25983	64	0x40		V	V	V	V	V	V	V
25992	80	0x50		V	V	V	V	V	V	V

So it looks for my like speeds up to 800KHz and more should be possible. Whats your point of view?

So it looks for my like speeds up to 800KHz and more should be possible. Whats your point of view?

If it can detect the devices at 800 KHz you can certainly give it a try in your code.

I tried really a lot to enhance the I2C frequency of my Due to communicate with INA226, but nothing worked...is there maybe a bug? I can only slow it down to sth. like Wire.setClock(50000L), but 400000L is just the regular frequency...does anybody sth. know about that?
By the way, I tried the same Code with Arduino Uno and just Wire.setClock(200000L) was the fastes on which did work...thats normal?

I am happy about any help

I gave it a lot of tries and I cant speed up over 100kHz...slowing down is possible.
If I try to use the SoftI2CMaster library an error appears... looks like it doesnt find the <avr\io.h> ...
but manually I found it under ...Arduino\hardware\tools\avr\avr\include\avr

strange things are happening here with arduino and I2C :o

The INA226 is a 3.3V chip up to 400kHz for the I2C bus.
Therefor Wire.setClock(400000L) should be faster.

Do you have long wires ? Do you have (the wrong value for the) pullup resistors ?

I'm not familiar with the Due, sorry, I can't answer your question.

Thx for your answer!

I use Doc | Tinkerforge but I just connect SDA SCL and GND to the Due. Here ist the Schematic https://github.com/Tinkerforge/voltage-current-bricklet/raw/master/hardware/voltage-current-schematic.pdf .
Do you think it could be really the cable? I have 0.5m unshielded.
The Pullup is integrated in the brick? Or not?
For what do I need these pullups and if I need them, why does it work with lower speeds and if I try to set higher speeds it still works, but slower than expected.

UPDATE:
And as I just saw, have PIN 20 and PIN 21 internal pull-ups:
"TWI 1: 20 (SDA) and 21 (SCL)
TWI 2: SDA1 and SCL1.
Support TWI communication using the Wire library. SDA1 and SCL1 can be controlled using the Wire1 class provided by the Wire library. While SDA and SCL have internal pullup resistors, SDA1 and SCL1 have not. Adding two pullup resistor on SDA1 and SCL1 lines is required for using Wire1."

Why it is slower than expected, I don't know. But first get the I2C bus right, and test with that.

In the schematic are no pullup resistors. Without pullup resistors the I2C is high impedance and won't work properly.

An actual piece of "cable" is always bad for I2C. The SDA and SCL should preferrably not be next to each other in the same cable, and the maximum capacitance to GND is 400pF for I2C. The worst thing is when SDA and SCL use twisted pair or are next to each other in a ribbon cable.

The schematic show SDA and SCL next to each other, and I think that is a flat flex cable ?
That is for sure a design mistake.

Add pullup resistors to 3.3V. Start with 4k7 and see if that is enough to overcome the trouble with the cable. You can lower the value to 2k2 (or even 1k5) if needed.

I tried what you wrote, but nothing works...maybe I really try another cable...or I have to be happy with 100kHz.
I am really getting crazy...so frustrating...why the hell does it not work?!
Wire.setClock() and Due are no friends....

Do you use the newest Arduino IDE 1.6.6 ? (1.6.7 at december 17)
You can test with the i2c_scanner : Arduino Playground - I2cScanner
Keep it running while you try to add pullup resistors.

The internal pullup resistors for the Due are 50k to 150k, that is by far not enough to compensate for the cable trouble.

The "setClock" for the Due is here: https://github.com/arduino/Arduino/blob/master/hardware/arduino/sam/system/libsam/source/twi.c
The code sets dividers. Perhaps there is a bug.
After some searching, others confirm that setClock(400000L) increases the clock speed, although someone reported that actual frequency is 333kHz instead of 400kHz.

If you are about to throw the module out the window, then don't use the cable. Solder seperate wires to the module for the 3.3V, GND, SCL, SDA.

Thank you man! I am really done now.
I will try your advices tomorrow and if it will not work I have to be happy with what I have at one's disposal...

I have got the solution!!!

There was a really stupid line in the INA226.cpp library which puts a delay to 1 ms....So didn't matter how fast the I2C is, the delay limits the steps from one to an other measurement.

I have put the limit to delayMicroseconds(140) instead of delay(1) and it is way more faster now. The struggle has an end:).

So easy - but not easy to find!

But thank you really much!

I have got the solution!!!

There was a really stupid line in the INA226.cpp library which puts a delay to 1 ms....So didn't matter how fast the I2C is, the delay limits the steps from one to an other measurement.

I have put the limit to delayMicroseconds(140) instead of delay(1) and it is way more faster now. The struggle has an end:).

So easy - but not easy to find!

But thank you really much!

I should have asked in my first reply, which code or library you use :frowning:

Do you use this ?

I think the delay is for the conversion time of the INA226.
In that code are three wrong lines, the Wire.requestFrom() should not be encapsulated by Wire.beginTransmission and Wire.endTransmission, and the while(!Wire.available()) is wrong.
Those are common mistakes, but only made by people who don't understand how to use the Wire library.

Yes I used the library of jarzebski.

I think so too...but some micros ( delayMicroseconds(140) ) shoulb be enough with 800kHz or 2.4MHz, or what do you think? My INA226_XXX_CONV_TIME_140US in my code means it is 140us.

Do you have a correct version of the library or can you please say which lines you mean? Does it not proper work without these changes?

Yeah I just started with I2C and found already a lot of different problems...but now I am hopefully smarter:)

140us is a long time for the Due. I would make a software timer with micros().

If the INA226 is in continues mode, then I don't know why to wait after setting the register address. Perhaps it is in the datasheet, but I didn't read it. I'm not sure, therefor I print an error in the example below.

Is the value from the registers a signed integer ? Because readRegister16() returns a signed integer.

int16_t INA226::readRegister16(uint8_t reg)
{
    int16_t value;

    Wire.beginTransmission(inaAddress);
    Wire.write(reg);
    Wire.endTransmission();                       // or use parameter 'false' for repeated start.

    int n = Wire.requestFrom(inaAddress, 2);
    if( n==2 )
    {
      uint8_t vha = Wire.read();
      uint8_t vla = Wire.read();

      value = word(vha, vla);
    }
    else
    {
      Serial.println("ERROR, the INA226 was not ready");
      value = -1;              // to indicate an error, this might be a bad idea.
    }
    return value;
}

If some waiting is needed, then wait in the loop.

void loop()
{
  ...readRegister16(...)
  delayMicroseconds(140);
}

I don't have a Due or a INA226, so I'm just guessing what I think is best.

Did you know that Wire.write() does not transmit data ? It only fills the transmit buffer inside the Wire library. The Wire.endTransmission() transmits everything and wait until everything has finished.

The Wire.requestFrom() starts an I2C transmission, and waits until all the data has been received and finishes the I2C transmission. Only after that it returns. The received bytes are waiting in a receive buffer inside the Wire library.

Did you know that Wire.write() does not transmit data ? It only fills the transmit buffer inside the Wire library. The Wire.endTransmission() transmits everything and wait until everything has finished.

The Wire.requestFrom() starts an I2C transmission, and waits until all the data has been received and finishes the I2C transmission. Only after that it returns. The received bytes are waiting in a receive buffer inside the Wire library.

The received bytes are waiting in a receive buffer inside the Wire library or inside the microcontroller? The entity buffer is a real hardware; should it not be within the MCU?