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]
SPI.transfer(0x12);
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.
LarryD
January 18, 2016, 11:57pm
2
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);
MarkT
January 19, 2016, 12:17am
4
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
FFFF8283
818283
LarryD
January 19, 2016, 12:33am
5
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.
Pete
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.
Cheers!