Combine three twos compliment bytes to a long int.

Consider that I've just pulled three bytes in from a 24 bit ADC. The datasheet tells me:

The ADS1246/7/8 output 24 bits of data in binary twos complement format. The least significant bit (LSB) has a weight of (VREF/PGA)/(223 – 1). The positive full-scale input produces an output code of 7FFFFFh and the negative full-scale input produces an output code of 800000h.

My three bytes are in a byte array:

byte data[3]
  data[0] = SPI.transfer(0x0);
  data[1] = SPI.transfer(0x0);
  data[2] = SPI.transfer(0x0);

And now I wish to combine the bytes to form a regular long.

I know I can use:

unsigned long n = data[2]|(data[1]<<8)|(data[0]<<16);

to combine them, but my results are weird on account (I believe) that the bytes are in twos compliment.

Is data[0] the MSB

Make your array type unsigned long.

Try it this way:

unsigned long n = data[2] | (((unsigned long)data[1])<<8) | (((unsigned long)data[0])<<16);

No, byte is unsigned - but you are shifting a byte left 16 bits to give zero.

The default type in an expression is int and int is 16 bits on most Arduinos.

unsigned long n = data[2]|(data[1]<<8)|(data[0]<<16);  // data[0] is shifted 16 in a signed int context

Thus to shift a byte left by 16 places you must use a long variable:

unsigned long res = data[0] ; res <<= 8 ;  // all the shifting is in unsigned long context
res |= data[1] ; res <<= 8 ;
res |= data[2] ;

Thus if you run this sketch (except on Due!):

void setup() 
  Serial.begin (115200) ;
  byte data[3] ;
  data[0] = 0x81 ;
  data[1] = 0x82 ;
  data[2] = 0x83 ;
  unsigned long n = data[2]|(data[1]<<8)|(data[0]<<16);
  Serial.println (n, HEX) ;
  unsigned long res = data[0] ; res <<= 8 ;  // all the shifting is in unsigned long context
  res |= data[1] ; res <<= 8 ;
  res |= data[2] ;
  Serial.println (res, HEX) ;

void loop()

it will print


Maybe one instruction per line makes things more readable.

res = data[0] ; 
res <<= 8 ;
res |= data[1] ;
res <<= 8 ;
res |= data[2] ;

You still need to handle the sign bit. After MarkT's second example, if the sign bit in data[0] is set, set the top result byte to 0xFF.

if (data[0] & 0x80)res |= 0xff000000L;

Now you have a signed 32-bit number.


Sorry for the delay in replying, I've been out of action for a while. But thank you very much for your answers, I've learned some things, and got the code working.