This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

INA219: Weird current value

Part Number: INA219

Hi,

I recently try to implement some firmware to read different ina219 on my PCB and I stumble with some issues with the reading of the current value. For some reason I can't find, I am not able to read a correct current value. I don't have any issue reading the correct bus value, but for some reason the current value is always ways off compare to what I read with a multimeter. 

For example, on my 3v3 alim, the multimeter read around 0.2A where the ina219 read 0.88A and it seems like that for all the ina219 on my PCB.

Here's my code :

Ina219.c 

/*
 * INA219.c
 *
 *  Created on: Mar 2, 2024
 *      Author: felix
 */

#include "INA219.h"

#define R_SHUNT 0.001f
#define BUS_VOLTAGE_SHIFT 3
#define BUS_VOLTAGE_LSB 0.004f

enum REG{Config,Shunt_voltage,Bus_voltage,Power,Current,Calibration};
enum Config_Reg{RST = 15,BRNG = 13,PG = 11,BADC = 7,SADC = 3,MODE = 0};//shift value for different parameter of the config register

enum Bus_Voltage_Reg{ovf=1,cnvr=2}; // bit value for mask of bus voltage

enum state{INIT,Wait_Data,Data_RDY}; // State machine possible state

//Buffer for all the i2c communication
struct ina219_buff{
	uint8_t current[sizeof(uint16_t)];
	uint8_t volt[sizeof(uint16_t)];
	uint8_t volt_data_receive;
	uint8_t current_data_receive;

};
//structure to keep the data of each ina219
struct ina219_s{
	uint8_t addr;
	uint8_t volt;
	int16_t current;
	uint16_t config;
	float current_lsb;
	struct ina219_buff buff;
};

// struct with all the var needed to use this file
static volatile struct {
	struct ina219_s ina[NB_INA];
	uint8_t state;
}ina219_t;

/*
* def: package the data into the buff
* var : variable wanted to be package
* buff: buffer where the data will be package
*/
static inline void package_msg(uint16_t var,uint8_t* buff)
{
	buff[0] = (uint8_t)(var>>8);
	buff[1] = (uint8_t)(var);
}

/* 
*	def: initialise one ina219 with the value pass in arg
	ina_var: struct with the data of the ina219
	bus_volt: Value for bus max voltage
	pga : value of pga to set
	Resolution : The adc Resolution/average chosen
	Mode: Choosen mode
	max_current: the max current we are trying to read
*
*/
static inline void init_ina219(volatile struct ina219_s* ina_var,uint8_t bus_volt,uint8_t pga,uint8_t Resolution,uint8_t mode,float max_current)
{

	uint16_t tmp = 0;

	uint8_t buff[sizeof(uint16_t)];
	const float current_lsb = 0.001f;// set at 1 ma because i dont need lower precision
	const float cal = trunc(0.04096f/(current_lsb * R_SHUNT));

	//reset the ina to be sure its blank when we program it
	tmp = (1<<RST);
	package_msg(tmp,buff);
	add_message_tx(ina_var->addr, Config, buff, sizeof(uint16_t),0);

	//set the calibration register
	tmp = (uint16_t)cal;
	package_msg(tmp,buff);
	add_message_tx(ina_var->addr, Calibration, buff, sizeof(uint16_t),0);
	ina_var->current_lsb = current_lsb;

	//set parameter for the INA
	tmp = (bus_volt<<BRNG) | (pga<<PG) | (Resolution<<BADC) | (Resolution<<SADC) | (mode<<MODE);
	package_msg(tmp,buff);
	add_message_tx(ina_var->addr, Config, buff, sizeof(uint16_t),0);

	//look to confirm that init parameter have been set correctly
	add_message_rx(ina_var->addr,Config, (uint8_t*)&ina_var->buff.volt, sizeof(uint16_t), (uint8_t*)&ina_var->buff.volt_data_receive);
	
	//wait for the message to be receive
	while(!ina_var->buff.volt_data_receive);
	//look to be sure the config was correctly write
	ina_var->config = (ina_var->buff.volt[0]<<8)|(ina_var->buff.volt[1]);
	tmp = (bus_volt<<BRNG) | (pga<<PG) | (Resolution<<BADC) | (Resolution<<SADC) | (mode<<MODE);

	if(tmp != ina_var->config)// did not work we retry
	{
		init_ina219(ina_var,bus_volt,pga,Resolution,mode,max_current);
	}

}

/*
* fonction for Wait_Data state
*	Wait until all the data is receive so we can change the state machine to data ready
*/
static inline void wait_data()
{
	uint8_t still_wait = 0;

	for(uint8_t i = 0; i < NB_INA;i++)
	{
		// if data is not receive we increment the counter
		if(!ina219_t.ina[i].buff.volt_data_receive && !ina219_t.ina[i].buff.current_data_receive)
		{

			return;
		}

	}
	ina219_t.state = Data_RDY;
}

/*
*	function for the data_ready state
*	on this function we are gonna analyse and store the value
*	I try to get the current from the shunt voltage directly because the value on the current register
	was not giving me the good answer so i wanted to see if it was the problem, but it doesn't change anything
*/
static inline void data_ready()
{
	// expect 0.195A for 3V3, got 0.88
	// 0.03A empty 5V
	//0.4A for 5v with pi

	float tmp = 0;
	uint16_t tmp1 = 0;
	int16_t tmp_cur = 0;
	for(uint8_t i = 0; i < NB_INA;i++)
	{
		tmp1 = (uint16_t)(ina219_t.ina[i].buff.volt[0]<<8)|(ina219_t.ina[i].buff.volt[1]);

		tmp = (tmp1>>BUS_VOLTAGE_SHIFT)*(BUS_VOLTAGE_LSB);
		ina219_t.ina[i].volt = (uint16_t)(tmp*10); // add *10 to store it in a way that it's ready to be send on the can network
		tmp_cur = (int16_t)((ina219_t.ina[i].buff.current[0]<<8)|(ina219_t.ina[i].buff.current[1]));
		tmp = ((float)tmp_cur*0.00001f)/R_SHUNT;
		ina219_t.ina[i].current = (int16_t)tmp*1000;//add *1000 to store it in a way that it's ready to be send on the can network
	}
	ina219_t.state = INIT;
}


/*
* brief: Initialise all the ina219 ic present on the board
*/
void init_inas219()
{
	ina219_t.ina[V5].addr = INA1_ADDR;
	init_ina219(&(ina219_t.ina[V5]), INA1_BUS_V, INA1_PGA, INA1_RES, INA1_MODE, INA1_MAX_CURRENT);

	ina219_t.ina[V3V3].addr = INA2_ADDR;
	init_ina219(&(ina219_t.ina[V3V3]), INA2_BUS_V, INA2_PGA, INA2_RES, INA2_MODE, INA2_MAX_CURRENT);

	ina219_t.ina[Fet1].addr = INA3_ADDR;
	init_ina219(&(ina219_t.ina[Fet1]), INA3_BUS_V, INA3_PGA, INA3_RES, INA3_MODE, INA3_MAX_CURRENT);

	ina219_t.ina[Fet2].addr = INA4_ADDR;
	init_ina219(&(ina219_t.ina[Fet2]), INA4_BUS_V, INA4_PGA, INA4_RES, INA4_MODE, INA4_MAX_CURRENT);
	
	//remove because of addr conflic
	/*ina[Fet3].addr = INA5_ADDR;
	init_ina219(&ina[Fet3], INA5_BUS_V, INA5_PGA, INA5_RES, INA5_MODE, INA5_MAX_CURRENT);*/

	ina219_t.ina[Fet4].addr = INA6_ADDR;
	init_ina219(&(ina219_t.ina[Fet4]), INA6_BUS_V, INA6_PGA, INA6_RES, INA6_MODE, INA6_MAX_CURRENT);

	ina219_t.ina[Fet5].addr = INA7_ADDR;
	init_ina219(&(ina219_t.ina[Fet5]), INA7_BUS_V, INA7_PGA, INA7_RES, INA7_MODE, INA7_MAX_CURRENT);

	ina219_t.ina[Fet6].addr = INA8_ADDR;
	init_ina219(&(ina219_t.ina[Fet6]), INA8_BUS_V, INA8_PGA, INA8_RES, INA8_MODE, INA8_MAX_CURRENT);
	ina219_t.state = INIT;
}

/*
*	Add the reading value message to the i2C buffer
*/
void Start_conv_ina219()
{
	uint8_t* flg_volt,*flg_current,*buff_v,*buff_c;
	uint8_t addr;
	const uint8_t size = sizeof(uint16_t);

	if(ina219_t.state == INIT)
	{
		for(uint8_t i = 0; i < NB_INA;i++)
		{
			flg_volt = (uint8_t*)&ina219_t.ina[i].buff.volt_data_receive;
			flg_current = (uint8_t*)&ina219_t.ina[i].buff.current_data_receive;
			buff_v = (uint8_t*)ina219_t.ina[i].buff.volt;
			buff_c = (uint8_t*)ina219_t.ina[i].buff.current;
			addr = ina219_t.ina[i].addr;

			add_message_rx(addr, Bus_voltage, buff_v, size,flg_volt);
			add_message_rx(addr, Shunt_voltage, buff_c, size,flg_current);
		}
		ina219_t.state = Wait_Data;
	}
}


/*
 * look to see if the data is ready
 * and pull it
 */
void Look_state_ina219()
{
	switch(ina219_t.state)
	{
	case Wait_Data:
		wait_data();
		break;
	case Data_RDY:
		data_ready();
		break;
	}
}
/*
	return bus value of the ina219
*/
uint8_t get_volt_ina219(uint8_t index)
{
	return ina219_t.ina[index].volt;
}

/*
	Return current value of the ina219s
*/
uint16_t get_current_ina219(uint8_t index)
{
	return ina219_t.ina[index].current;
}
Ina219.h

/*
 * INA219.h
 *
 *  Created on: Mar 2, 2024
 *      Author: felix
 */

#ifndef INC_INA219_H_
#define INC_INA219_H_

#include "I2C.h"
#include <math.h>

#define NB_INA 7

#define BRNG_16V 0
#define BRNG_32V 1

#define RES_9BIT 0
#define RES_10BIT 0x1
#define RES_11BIT 0x2
#define RES_12BIT 0x3
#define RES_AV2 0x9
#define RES_AV4 0xA
#define RES_AV8 0xB
#define RES_AV16 0xC
#define RES_AV32 0xD
#define RES_AV64 0xE
#define RES_AV128 0xF

#define PGA_40mv 0
#define PGA_80mv 0x1
#define PGA_160mv 0x2
#define PGA_320mv 0x3

#define MODE_CONT_SHUNT_BUS 0x7
#define MODE_CONT_SHUNT 0x5

#define INA1_ADDR 0x40
#define INA1_BUS_V BRNG_16V
#define INA1_PGA PGA_40mv
#define INA1_RES RES_AV16
#define INA1_MODE MODE_CONT_SHUNT_BUS
#define INA1_MAX_CURRENT 6

#define INA2_ADDR 0x44
#define INA2_BUS_V BRNG_16V
#define INA2_PGA PGA_40mv
#define INA2_RES RES_AV16
#define INA2_MODE MODE_CONT_SHUNT_BUS
#define INA2_MAX_CURRENT 1

#define INA3_ADDR 0x46
#define INA3_BUS_V BRNG_32V
#define INA3_PGA PGA_40mv
#define INA3_RES RES_AV16
#define INA3_MODE MODE_CONT_SHUNT_BUS
#define INA3_MAX_CURRENT 4

#define INA4_ADDR 0x47
#define INA4_BUS_V BRNG_32V
#define INA4_PGA PGA_320mv
#define INA4_RES RES_AV16
#define INA4_MODE MODE_CONT_SHUNT_BUS
#define INA4_MAX_CURRENT 4

#define INA5_ADDR 0x00
#define INA5_BUS_V BRNG_32V
#define INA5_PGA PGA_40mv
#define INA5_RES RES_AV16
#define INA5_MODE MODE_CONT_SHUNT_BUS
#define INA5_MAX_CURRENT 4

#define INA6_ADDR 0x45
#define INA6_BUS_V BRNG_32V
#define INA6_PGA PGA_40mv
#define INA6_RES RES_AV16
#define INA6_MODE MODE_CONT_SHUNT_BUS
#define INA6_MAX_CURRENT 4

#define INA7_ADDR 0x42
#define INA7_BUS_V BRNG_32V
#define INA7_PGA PGA_320mv
#define INA7_RES RES_AV16
#define INA7_MODE MODE_CONT_SHUNT_BUS
#define INA7_MAX_CURRENT 4

#define INA8_ADDR 0x43
#define INA8_BUS_V BRNG_32V
#define INA8_PGA PGA_320mv
#define INA8_RES RES_AV16
#define INA8_MODE MODE_CONT_SHUNT_BUS
#define INA8_MAX_CURRENT 4



enum Ina_loc{V5,V3V3,Fet1,Fet2,Fet4,Fet5,Fet6};
/* 
*	def: initialise one ina219 with the value pass in arg
	ina_var: struct with the data of the ina219
	bus_volt: Value for bus max voltage
	pga : value of pga to set
	Resolution : The adc Resolution/average chosen
	Mode: Choosen mode
	max_current: the max current we are trying to read
*
*/
void init_inas219();

/*
*	Add the reading value message to the i2C buffer
*/
void Start_conv_ina219();

/*
 * look to see if the data is ready
 * and pull it
 */
void Look_state_ina219();

/*
	return bus value of the ina219
*/
uint8_t get_volt_ina219(uint8_t index);

/*
	Return current value of the ina219s
*/
uint16_t get_current_ina219(uint8_t index);

#endif /* INC_INA219_H_ */

  • Hi Felix, 

    How did you calculate your calibration register? Also, what is the max expected current you intend on measuring? If the current is off but the shunt voltage is correct, it means that the calibration was not set correctly. 

    Best,

    Mohamed 

  • Hi thanks for your answer,

    i calculated the calibration based on a lsb off 1 mA so i calculated the value with this formula :  

    trunc(0.04096f/(current_lsb * R_SHUNT));

    where current_lsb is 0.001 (for 1 mA) and R_SHUNT is 0.001 for the value of my shunt ressistor which is 1 mOhms.

    The max expected current should be around 300mA.

  • Hi Felix,

    The value you are writing in the calibration register is over 15 bits of resolution. The value you have calculated is 0.04096 / (0.001A * 0.001Ω) = 40,960. This exceeds 2^15. Also, I noticed in your code that you aren't shifting your calibration register before inserting it in the variable. You would need to shift the value as the register has a read only B0: 

    It would also be helpful to read the shunt register to verify you are getting the correct value across the shunt. 

    Best,

    Mohamed 

  • Hi Mohamed once again thanks for your answer,

    I try changing my calibration value. I change it for

    trunc(0.04096f/(current_lsb * R_SHUNT));

    with a current_lsb of 2ma and add a shift before sending the message, but the value was still off so i took a look directly at the shunt_voltage register

    and get the current on my side with those line of code : 

    tmp_cur = (int16_t)((ina219_t.ina[i].buff.current[0]<<8)|(ina219_t.ina[i].buff.current[1]));

    tmp = ((float)tmp_cur*0.00001f)/R_SHUNT;

    where I multiply the shunt voltage register by 10uA to get the shunt voltage and divide by the value of the resistor to get the current

    and the result was still too high, the current i calcul was 1.17A where the multimeter told me it was around 30mA

    Do you have any idea, how to fix this issues?

  • Hi Felix, 

    Do you mind supplying the raw value of the shunt register?

  • Yeah sure, the value of the shunt resistor voltage register is 77

  • Hi Felix, 

    The shunt resistor you're using is far too small compared to the accuracy and range of the device. Here's what it looks like when I put your system parameters into our error tool: 

    You can find this tool here: https://www.ti.com/tool/CS-AMPLIFIER-ERROR-TOOL

    Please let me know if you have any questions, 

    Mohamed