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_ */

