get signed data from TWI

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey all,

I am using twi_master_trans() function in CodeVision

when I get data it is unsigned char ,

for example TWI_receive[0] is defined in function unsigned char but my sensor send signed data,

How we can separate them ?

for example if 1 sensor send 0xFF the TWI_receive[0] will be 0xFF=255 but we it is -1

how we can solve it?

This topic has a solution.
Last Edited: Mon. Dec 9, 2019 - 09:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

uint8_t un= 0xff;

int8_t signed;

 

 

signed = (int8_t)un; //known as a typecast in C

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most primitive functions for peripherals return uint8_t or uint16_t because it copes with "anything".

 

You just take the raw uint8_t bytes and process them to suit your application.  e.g.

uint8_t TWI_send[2];     //declare a 2-byte buffer
uint8_t TWI_receive[2];  //declare a 2-byte buffer
uint16_t result;
int16_t sresult;  //signed

//how to receive 16-bit signed data e.g. 2 byte
ret = twi_master_trans(TWI_BUS_ADDRESS, TWI_send, 1, TWI_receive, 2);

result = TWI_receive[0];     //big-endian data
result = (result << 8) | TWI_receive[1]; //big-endian data
sresult = (int16_t)result;   //cast the unsigned to signed

//how to receive 8-bit signed data e.g. 1 byte
ret = twi_master_trans(TWI_BUS_ADDRESS, TWI_send, 1, TWI_receive, 1);

result = TWI_receive[0];     //big-endian data
sresult = (int8_t)result;    //cast the unsigned to signed 8-bit

This might look a bit messy but I2C data comes in many forms.   You just have to write and read simple bytes.   Then convert accordingly.

Note that some devices are little-endian.

 

I strongly advise using <stdint.h>  header.

And using uint8_t buffers.   Only use char if you know it is ALWAYS char data and NEVER involved in arithmetic e.g. text messages.

 

David.

 

Edit.   Corrected the int8_t cast.

 

Last Edited: Sun. Dec 8, 2019 - 11:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thank you David

in codevision should I write

sresult = (int)result;

 

???

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My apologies.   I have corrected my example.

 

It is fairly unusual to have a signed 8-bit value.    What I2C hardware chip are you using?

 

Note that I have deliberately used 16-bit result and sresult because arithmetic tends to use integers.

 

C does a lot of silent sign-extension,  truncation and even casting.   The rules are clear.

If you had used an int8_t sresult everything would have worked silently.  e.g.

int8_t sresult8 = TWI_receive[0];

But I guarantee that you will make mistakes if you do this without understanding how or why it works.   And why a C compiler allows it.

 

Don't worry about using multiple statements e.g. in my 16-bit example.

A C compiler will create efficient code.   Just as long as you give it unambiguous statements.

 

David.

Last Edited: Sun. Dec 8, 2019 - 12:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thanks

It is MAG3110,

 

why bellow test code not works ?

signed char x;
twi_mag3110.data[0]=0xff;    //assume this is -1 and received from I2C
        
x=((twi_mag3110.data[0] & 0b10000000)?-1*twi_mag3110.data[0]:twi_mag3110.data[0]); 

by test I have been found that x is not -1

why?!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looking at the MAX3110 datasheet the DIE_TEMP register is int8_t

uint8_t TWI_send[1];     //declare a 1-byte buffer
uint8_t TWI_receive[2];  //declare a 2-byte buffer
int8_t temperature;

TWI_send[0] = 0x0F;      //DIE_TEMP
//how to receive 8-bit signed data e.g. 1 byte
ret = twi_master_trans(TWI_BUS_ADDRESS, TWI_send, 1, TWI_receive, 1);

temperature = (int8_t)TWI_receive[0];     //

Most other registers seem to be 16-bit e.g. OUT_X

But OFF_X seems to require special treatment.

DR_STATUS looks like uint8_t

 

What is the type of

twi_mag3110.data[0]

Please post your actual code.  

 

David.

 

 

Last Edited: Sun. Dec 8, 2019 - 12:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thank you,

It is unsigned char.

 

     twi_mag3110.data[0]=0xff;
     x=twi_mag3110.data[0]<<8;
     twi_mag3110.data[1]=0xff;
     x=x+twi_mag3110.data[1];

I write above code and check x

     if(x==65535) ledblueon;

     if(x==-1) ledgreenon;

Surprising that both led will be on!! whether x is unsigned int or signed int.

 

why?

 

bellow code has been same result

 

     twi_mag3110.data[0]=0xff;
     x=twi_mag3110.data[0]<<8;
     twi_mag3110.data[1]=0xff;
     x=x+twi_mag3110.data[1];    
        
     y=(int)x;
     if(y==65535) bluetgl;
     if(y==-1)    greentgl;

y is signed int .

Last Edited: Sun. Dec 8, 2019 - 12:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hasanzadeh99 wrote:

x=((twi_mag3110.data[0] & 0b10000000)?-1*twi_mag3110.data[0]:twi_mag3110.data[0]); 

if twi_mag3110.data[0] is 255

you have

x = -1 * 255 (this is performed as type int after integer promotions)

x = -255

 

Almost certainly if x is signed char (range -128 to +127 assuming 8bit char) then you will end up with x = 1.

The result is not guaranteed by the C standard, if you convert a value to signed type and the original value is outside the range of values that can be represented in that signed type, the C standard says the result is implementation defined. This means it is up to the compiler to decide what it wants to do.

In practice, assuming you are only concened with compiler for a processor that uses 2's complement for signed values, what the compiler will do in this situation is almost certainly just truncate the value -255, which as signed 16bit  int would be 0xff01 and truncate to 0x01.

The same implementation defined situation arises in your original question where you have

unsigned char u = 255;

signed char s = u;

since s can only reprsent values in the range -128 to +127 but you are assigning it value 255.

In practice, what the compiler will almost certainly do is simply take the value of u (255 or 0xff) and use exactly the same bit pattern for s, in other words it doesn't need to do anyhing, other than it will now interpret that same bit pattern as a 2's complement signed value, so 0xff is now interpreted as -1.

So saying s = u is in practice pretty safe.

 

I think what you were trying to do would be

 

unsigned char u = 0xff;

signed char s = u >= 128 ? u - 256 : u;

 

which the compiler will probably optimise to simply s = u;

(The u -256 will be carried out as int after integer promotion, giving you an int value in range -128 to -1, which now fits in the range of signed char so no problem).

 

EDIT: the above asumes the compiler follows rules of C (eg. regarding integer promotions), I see you mentioned CodeVision, I don't know if this follows those rules exactly or not (some compilers for 8bit processors might have option to do things slightly different to standard C).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thank you Mrkendo,

see follow code


     twi_mag3110.data[0]=0xff;
     x=twi_mag3110.data[0]<<8;
     twi_mag3110.data[1]=0xff;
     x=x+twi_mag3110.data[1];    
        

     if(x==65535) bluetgl;
     if(x==-1)    greentgl;

in this code blue and green led will be toggle

twi_mag3110.data is unsigned char

x whether  signed char or unsigned char the result is same.

why x has 2 value?????

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to standard C (with standard integer promotions), I think the only result that should be true is if x is signed char and for the test if (x == -1)

 

If all 4 cases are saying true, I think this must be some non standard behaviour of CodeVision with the options you are compiling with.

If we assume char is 8 bit and int is 16 bit, according to standard C, if x is unsigned char then both x == 65535 and x == -1 can never be true.

 

EDIT:

likewise, if x is signed char, according to stadard C,  x == 65535 can never be true.

Is there some CodeVision option along the lines of "assume 8 bit int" or  'don't promote 8 bit types" ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am assuming that x is uint16_t and y is int16_t

The most important step is ADD SOME WHITESPACE for readability

     twi_mag3110.data[0] = 0xff;   // regular uint8_t value
     x = twi_mag3110.data[0] << 8; // x = 0xFF00
     twi_mag3110.data[1] = 0xff;   //
     x = x + twi_mag3110.data[1];  // x = 0xFFFF

     y = (int)x;                   // cast 0xFFFF to int16_t i.e. -1
     if (y == 65535) bluetgl;      // illegal value for int16_t
     if (y == -1)    greentgl;     // legal value for int16_t

My explanatory comments would be different if your types are different.

I strongly advise you to treat multi-byte values as uint8_t and "assemble into uint16_t, uint32_t, ..."

When you have collected all the bytes into the appropriate widths,   you can cast to signed.

 

I have typed into the Browser.   I can try my example code in Codevision if necessary.

 

Yes,   Codevision does have some "shortcut options".   I do not advise using them unless you understand the consequences.  e.g.

     //only if you have regular char -> int promtion
     x = twi_mag3110.data[0] << 8; // x = 0xFF00

     //otherwise you get
     x = twi_mag3110.data[0] << 8; // x = 0x0000

     //this should always be safe
     x = twi_mag3110.data[0]; // x = 0xFF
     x = x << 8;              // x = 0xFF00

Untested.   I had better test before you believe me.

 

David.

 

Edit.   Ran a CV project through the AS7.0 Simulator.   You can observe how the variables work.

And if you uncheck the "promote char" option,  you get compilation warnings.

void main(void)
{
    volatile uint16_t x;
    volatile int16_t y;
    volatile int8_t y8;
    struct {
        unsigned char data[2];
    } twi_mag3110;

    twi_mag3110.data[0] = 0xFF;
    //only if you have regular char -> int promtion
    x = twi_mag3110.data[0] << 8; // x = 0xFF00

    //otherwise you get
    x = twi_mag3110.data[0] << 8; // x = 0x0000

    //this should always be safe
    x = twi_mag3110.data[0]; // x = 0xFF
    x = x << 8;              // x = 0xFF00|

    y = x;
    y = (int16_t)x;

    y8 = twi_mag3110.data[0];
    y = y8;

    while (1) {
    }
}

 

Last Edited: Sun. Dec 8, 2019 - 05:20 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, there is some confusion over what types are being used.

In post #11 I was assuming x as unsigned or signed char, because it was responding to #10 which said

hasanzadeh99 wrote:

in this code blue and green led will be toggle

twi_mag3110.data is unsigned char

x whether  signed char or unsigned char the result is same.

But probably x was meant to be unsigned or signed int?

Who knows.

For standard C,

If x is unsigned int, and int is 16 bit, then both x == 65535 and x == -1 are true if x is 65535.

If x is signed int, and int is 16 bit, then only x == -1 can ever be true.

 

EDIT:

If x is signed int, and int is 16 bit, but you say x == 65535U rather than x == 65535, then it would be tue if x == -1, because you now have

(int) == (unsigned int) which means the comparison is peformed as unsigned int and the int value is converted to unsigned int,

whereas with 65535 you have

(int) == (long) and the int is converted to long.

I think this just demonstartes how fiddly and potentially confusing it is if you start mixing unsigned with signed :)

 

EDIT 2:

Just for more fun.

If x is signed int, and int is 16 bit, but you say x == 0xffff rather than x == 65535, then it would be tue if x == -1, because the hex constant 0xffff is unsigned int (compare to unsuffixed decimal constant 65535 which is long).

Basically, if x was signed int, to save yourself some grief don't go comparing it with a value that is outside its range, then life is a lot simpler.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My advice is to assemble multi-byte values with unsigned arithmetic.

Then do any signed casting as required.    Make sure that you use the correct types.

 

Remember that your AVR project is probably going to end up on ARM or ESP targets one day.   So it is important to get sign extension,  promotion, casting syntactically correct.

Then it will build painlessly on a 32-bit int target.

 

David.