cancel
Showing results for 
Search instead for 
Did you mean: 

HAL issue with int8_t instead of uint8_t

Xenon02
Senior

Hello ! 

I've been working on with LIS3MDL a magnetometer from STM, but there was one problem because reading X,Y,Z it was showing good values but sometimes instead of value it just spitted 0, for example X = 0.14, Y = 0.30, Z = 0.14 and sometimes it just randomly zeroed random axis like X = 0.14, Y = 0.0, Z = 0.14

The culprit was here : 

 

    uint8_t data[6];
    HAL_I2C_Mem_Read(&hi2c1, LIS3MDLTR_Address, LIS3MDLTR_X_Y_Z_Axis, 1, data, 6, HAL_MAX_DELAY);

 

The problem  firstly I used int8_t data[6] instead of uint8_t data[6], I don't know why HAL had problem with it ?? Binnary it should be the same data but somehow it didn't work. Someone know maybe why ?

 

    *x = ((int16_t)data[1] << 8 | (int16_t)data[0]);
    *y = ((int16_t)data[3] << 8 | (int16_t)data[2]);
    *z = ((int16_t)data[5] << 8 | (int16_t)data[4]);

 

Here I still changed it into int16_t so the binary data is not interpreted differently decimally, but in HAL_I2C it should make any difference because it is binary. 

In summary why puttin int8_t data[6] into HAL_I2C function made it work wierdly ??? Like cutting some data or puttin 0 instead of value. 

3 REPLIES 3
unsigned_char_array
Senior III

Because you store the LSB as signed byte it can become negative if it has an unsigned value > 127. By typecasting it to a signed 16 bit int you are doing sign extension of an int8_t to an int16_t and oring those upper bits with the MSB. Proof:

 

#include <stdio.h>

int main()
{
   
   uint8_t data_unsigned[2];
   int8_t data_signed[2];
   int16_t a,b;
   
   data_unsigned[0] = 128;
   data_unsigned[1] = 0;
   
   data_signed[0] = (int8_t)128; // this is the problem
   data_signed[1] = 0;
        
   
   a = ((int16_t)data_unsigned[1] << 8 | (int16_t)data_unsigned[0]);
   b = ((int16_t)data_signed[1] << 8 | (int16_t)data_signed[0]);
   
   printf("%d, %d\n", a,b);
   
   
   return 0;
}

 

(you can test this quickly with an online compiler such as https://cpp.sh/ or https://www.onlinegdb.com/online_c_compiler)

Better to do shifting and bit-wise logic with unsigned unless you know what you are doing (such as deliberate sign extension of an int that's not a power of 2 i.e. 13-bit int).

This is how to do it properly:

 

 a = (int16_t) (data_unsigned[1] << 8 | data_unsigned[0]); // no need to typecast LSB as this automatically happens when using bitwise operators, cast to signed after shifting

 

In case you have an odd size two's complement integer such as 13 bit:

 

a = ((int16_t)((data_unsigned[1] << 8 | data_unsigned[0]) << (16-13))) >> (16-13); // 13-bit sign extension

 

This works by first doing an unsigned shift to shift the sign bit in place and then doing a signed shift back to scale it back while retaining the sign bit. But in your case I assume the data is 16-bit so that's not needed.

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.
TDK
Guru

The int8_t and uint8_t data types are different. You need to use the correct one for your scenario. The value -1 can't be expressed in uint8_t for example.

If you feel a post has answered your question, please click "Accept as Solution".

I do have a question though. 
Because the function : 

HAL_I2C_Mem_Read(&hi2c1, LIS3MDLTR_Address, LIS3MDLTR_X_Y_Z_Axis, 1, data, 6, HAL_MAX_DELAY);

Is taking the address of variable "data". Isn't it that  the same bite values are placed in this variable ? 

For example 

uint8_t data; 

//Incoming data from I2C is 01011100
//So this value 01011100 is placed in data so 

data = 01011100; 

//Hence if data was int8_t type then the same bit values from I2C is placed in data so 

int8_t data;

data = 01011100; 

 

So in both case data from I2C is taken the same way, of course the interpretation in decimal is different.
Or perhaps the data placed in "data" is not in bits but in decimals ?

Because changing only the type int8_t data to uint8_t data changed it, although the data from I2C or UART etc, is taken from buffer and placed into variable so in bits it should be the same. I guess ? 
I mean in the buffer of I2C is 01011100 and it is placed in "data" variable so it should be the same ? Or perhaps not ? 

PS. 

So is it that because it was int8_t and not uint8_t this "data" variable, the extension made additional "1" values ? that weren't supposed to be there ?