cancel
Showing results for 
Search instead for 
Did you mean: 

HAL issue with int8_t instead of uint8_t

Xenon02
Associate III

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. 

2 REPLIES 2
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".