cancel
Showing results for 
Search instead for 
Did you mean: 

How to properly write and read a 16 bit I2C register with HAL_I2C_Mem_Write/Read()?

Fishmaster257
Associate III

Hello,

so I am trying to set up a INA3221 current/voltage/power sensor with my STM32F469I-Disoc board. The I2C_adress ist 0x43 (or 0x86 if shifted left). I set up my I2C with CubeMX in 400kHz mode and already used to interact with a BME280 sensor. The interaction with this sensor works without a problem, however it uses 8 bit registers.

The INA3221 sensor has 16 bit registers. I am just getting startet and wanted to check/read the value of the configuration register (0x00) after writing my own config to it (config value: 0x7FFF).

I guess I am doing the write operation wrong but I dont quite now what to change:

extern I2C_HandleTypeDef hi2c1;
 
typedef struct
{
	uint16_t checkv;
}INA_DATA;
 
INA_DATA INA;
 
//Initialization of the INA3221
void INA3221_Init(void)
{
	HAL_StatusTypeDef status=HAL_OK;
 
	uint16_t mycfg=0x7FFF; //All channels enabled, 1024 averaging samples, 8.2ms BV CT, 8.2ms SV CT, and all continuous measuring
	//uint16_t mycfg=0x7927; //All channels enabled, 128 averaging samples, 1ms BV CT, 1ms SV CT, and all continuous measuring
 
	//INA_ID=0x86 (SCL solder pad), Config=register address=0x00,
	status=HAL_I2C_Mem_Write(&hi2c1,INA_ID,Config,I2C_MEMADD_SIZE_16BIT,(uint8_t*)&mycfg,1,0x10000);
 
	if(status!=HAL_OK)Error_Handler();
 
	status=HAL_I2C_Mem_Read(&hi2c1,INA_ID,Config,I2C_MEMADD_SIZE_16BIT,(uint8_t*)&INA.checkv,1,0x10000);
 
	if(status != HAL_OK) Error_Handler();
 
	if(INA.checkv!=mycfg)
		{
			for(uint8_t k=0;k<3;k++)
			{
			HAL_Delay(500);
			Error_Handler();
			}
		}
}

So I tried changing the amount of data to 2, if I do so HAL_I2C_Mem_Write(); returns a HAL_ERROR as a status. If I read from the config address with data amount=1, the stored data (checkv) is 0, if I read more data (amount=2) the stored data is 0xFF00.

If I set the memory size to 8 and the amount of data to 2 in both the read an write function I get 0x2771 as my register value which is also not what it should be. 0x2771 might represent the power on reset value of 0x7127.

Below is the register map of the INA3221.

0693W000008wOjXQAU.png0693W000008wOjhQAE.pngI appreciate any hint on what I am getting wrong.

0693W000008wOyDQAU.pngEDIT: 

This is how writing/reading worked for me with the HAL_I2C_Mem_Read/Write:

When writing simply swap the MSByte and the LSByte you want to write. I wanted to write 0x7D3F to the register and simply swapped bytes.

When reading a register and using the read data you have to swap the bytes again.

I did this with the code in the comment down below.

uint16_t mycfg=0x3F7D;
 
//INA_ID=0x86 (SCL solder pad), Config=register address=0x00,
status=HAL_I2C_Mem_Write(&hi2c1,INA_ID,Config,I2C_MEMADD_SIZE_8BIT,(uint8_t*)&mycfg,2,0x10000);
 
//check for config value
status=HAL_I2C_Mem_Read(&hi2c1,INA_ID,Config,I2C_MEMADD_SIZE_8BIT,(uint8_t*)&INA.checkv,2,0x10000);

I put the Datasheet of the INA3221 in the appendix, if you want to look at it more closely.

1 ACCEPTED SOLUTION

Accepted Solutions
Ozone
Lead

The I2C bus has a specification, and this spec defines only byte (8-bit) transfers.

I suppose this specifies the transfer order in the sensor datasheet:

0693W000008wOxPQAU.jpg 

View solution in original post

5 REPLIES 5
Ozone
Lead

The I2C bus has a specification, and this spec defines only byte (8-bit) transfers.

I suppose this specifies the transfer order in the sensor datasheet:

0693W000008wOxPQAU.jpg 

So in other words the HAL_I2C_Mem_Write/Read work the other way around right? LSB first and MSB last. So every time I was trying to configure the INA3221 I actually reset it beacuse the MSbit=Resetbit was 1.

If I write "uint16_t mycfg=0xFF7F;" to the register, the value I read from the register is the same.

Thanks, was kind of obvious but somehow I missed it.

Ozone
Lead

MSB might also mean Most Significant Byte.

Look at page 30 of the datasheet, approximately in the middle.

Tha chapter beginning there describes the chip's I2C interface in more detail.

Many i2c devices have "big endian" convention.

Try to read some register with well known content, like the device version, then you will know.

-- pa

So I have "solved" it, I simply use some operations to switch the read bytes:

INA.ch1_sv=0x281F;
 
uint8_t MSB=0;
uint16_t LSB=0;
uint16_t sv_uint_temp=0;
 
//deletes the LSB of chx_sv and writes it into an uint8_ variable
MSB=INA.ch1_sv;
 
//shifts out the MSB leaving only the LSB
LSB=INA.ch1_sv>>8;
//put it back together
sv_uint_temp=(MSB<<8)+LSB;

In debug mode you can see sv_uint_temp is now 0x1F28