2's compliment to binary

Thread Starter

kevinnas

Joined Jul 31, 2017
77
Hello.
I have an Arduino uno reading output values from a 24 bit adc ADS1231 I am able to get the data in 2's compliment format as expected, but the problem is I would like it to be in Decimal format with a positive range as an example (0 to 20000) instead of (800000h to 7FFFFFh) and or at least initially a format like (-10000 to + 9999). Please help with how I could do this conversion. I know that 2's compliment to Decimal this is what I have to do:

first check if the number is negative or positive by looking at the sign bit. If it is positive, simply convert it to decimal. If it is negative, make it positive by inverting the bits and adding one. Then, convert the result to decimal. The negative of this number is the value of the original binary. But how do I actually do this in an arduino platform c. To read the adc values at the moment I am using:
void readADS1231(void)
{
adcvalue=0;
while(digitalRead(D_OUT));//WAIT UNTIL DATA IS READY
//24-->21 bits
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x800000;} //24
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x400000;}//23
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x200000;}//22
.... etc

upload_2017-9-27_9-1-44.png
 

Attachments

MrChips

Joined Oct 2, 2009
34,812
You have a common misconception of how numbers are represented in a computer.
The stored number is in binary. There is no need to convert it into decimal. The number will appear in decimal on your display device according to the function that you choose to perform the display.

What you have is 24-bit binary. You need to store it in a 32-bit signed integer data type. In order to preserve the 2's complement representation you need to shift the number to the left by 8 bits.
 

Thread Starter

kevinnas

Joined Jul 31, 2017
77
Hello. Thank you for your reply.
The stored number is in binary. There is no need to convert it into decimal. The number will appear in decimal on your display device according to the function that you choose to perform the display.
Understood.
What you have is 24-bit binary. You need to store it in a 32-bit signed integer data type. In order to preserve the 2's complement representation you need to shift the number to the left by 8 bits.
My code does give me the data as shown in the data sheet so I believe it is correct? ( If I press on the sensor and chose to output the data in Hex or any other format I get readings as I would expect them according to the data sheet.
My problem is I would like to use only a positive scale to represent the numbers ( e.g if I press the sensor in a negative direction I would like the value to come out as positive etc. example (0 - 1000 being the full scale) and 0-499 representing the negative values, 500 being zero and 501-1000 being positive etc.
I am having a hard time with this as I am new to C. Do you have a skeleton code I could use to work with?
 
You have 24 bit 2's complement and you need 32 bit two complement. it's easy, although I can't give you code.

All you have to do, is replicate the sign bit and extend it to 32 bits. The sign bit is 2^23; Whatever that bit is, you make that bit fill the 2^24 through 2^31. That's it.

example. 4 bit 2's complement to 8 bit 2's complement.

4 bit, -7 to 8; 8 bit 2's complement is -127 to 128.

so 1111 is -1; if you sign extend it to 8 bits, you get 1111 1111 or -1
do, 0001 is 1; and if you sign extend that, you get 0000 0001 or 1

You just replicate the sign bit.

In 2's complement, there isn't two representations of zero.

You may be able to type cast it into binary. Look at the 24 bit sign bit and if it;s 1 OR 0b111111111100000000000000000000000 into what you read and if it's zero AND
0b000000000011111111111111111111111 into what you read.
--------------------s
s is the sign bit.

And type cast back to 32 bit signed integer (32 bit 2's complement).
 
Last edited:

MrChips

Joined Oct 2, 2009
34,812
If you simply want to convert the 24-bit 2's complement number to positive values, add 0x00800000 to the number stored in a 32-bit unsigned integer data type.

Then scale the number as you wish.
 

Thread Starter

kevinnas

Joined Jul 31, 2017
77
Thank you both for your reply's.
I have tried to write some code to get the number as 32 bit instead of 24 bits...my code writting skills are basic so is the following okay?

long adcvalue;
uint32_t adcvalue_32bit;
unsigned long bit24;
long X;

void readADS1231(void)
{
adcvalue = 0;
X = 0;
bit24 = 0x800000; // sign bit of the 24 bit adcvalue - bit 24 ( or 23rd bit)
adcvalue_32bit = 0;

while(digitalRead(D_OUT));//WAIT UNTIL DATA IS READY
//24-->1
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x800000;} //24
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x400000;}//23
// Bit 24 all the way down to bit 1:
//
clock ();
if(digitalRead(D_OUT)){adcvalue=adcvalue+0x1;}//1
clock ();//TO PULL DATA READY TO HIGH STATE --PAGE No.14 ON ADS1231 DATASHEET

// Sign Extending adcvalue (24 bit) to 32 bit
X = bit24 & adcvalue; // zero-ing the rest of the adcvalue bits except the sign bit.
if(X==bit24) // if sign bit is = 1
{
adcvalue_32bit = adcvalue+0xFF000000; // sign extend adc value to 32 bit
}
else // sign bit is = 0
{
adcvalue_32bit = adcvalue+0x00000000; // sign extend adc value to 32 bit
}
//
digitalWrite(CLOCK, LOW);
}
 

MrChips

Joined Oct 2, 2009
34,812
We don't know how you are interfacing to the ADS1231 but we will assume that you are reading the data serially into one port bit.

Declare a 32-bit data type:
uint32_t adcvalue;

Set to zero:
adcvalue = 0;

Read the data bit and loop through 24 times:
adcvalue = (adcvalue << 1) + data_bit;

When you have acquired 24 bits, make positive:
adcvalue = adcvalue + 0x00800000;
Edited: this should read
adcvalue = (adcvalue + 0x00800000) & 0x00FFFFFF;

Scale as you wish:
adcvalue = FULLSCALE * adcvalue / 0x01000000;
 

dl324

Joined Mar 30, 2015
18,327
is the following okay?
Your code will be more readable if you use a code box; slightly more readable in your case...
Code:
long adcvalue;
uint32_t adcvalue_32bit;
unsigned long bit24;
long X;

void readADS1231(void) {
    adcvalue = 0;
    X = 0;
    bit24 = 0x800000; // sign bit of the 24 bit adcvalue - bit 24 ( or 23rd bit)
    adcvalue_32bit = 0;

    while(digitalRead(D_OUT));//WAIT UNTIL DATA IS READY
    //24-->1
    clock ();
    if(digitalRead(D_OUT)){adcvalue=adcvalue+0x800000;} //24
    clock ();
    if(digitalRead(D_OUT)){adcvalue=adcvalue+0x400000;}//23
    // Bit 24 all the way down to bit 1:
    //
    clock ();
    if(digitalRead(D_OUT)){adcvalue=adcvalue+0x1;}//1
    clock ();//TO PULL DATA READY TO HIGH STATE --PAGE No.14 ON ADS1231 DATASHEET

    // Sign Extending adcvalue (24 bit) to 32 bit
    X = bit24 & adcvalue; // zero-ing the rest of the adcvalue bits except the sign bit.
    if(X==bit24) // if sign bit is = 1
    {
      adcvalue_32bit = adcvalue+0xFF000000; // sign extend adc value to 32 bit
    }
    else        // sign bit is = 0
    {
      adcvalue_32bit = adcvalue+0x00000000; // sign extend adc value to 32 bit
    }
    //
    digitalWrite(CLOCK, LOW);
}
 
Last edited:

Thread Starter

kevinnas

Joined Jul 31, 2017
77
Thank you!
With my current code as it is, It seems to be working okay (This is while keeping the data positive and negative):
If I press my sensor in a positive or negative direction I get values from (zero+offset) to 0x7FFFFF for positive direction and from (0+offset) to 0xFF8000000 for negative which seems to be working as expected right? 24 bits for positive and 32 bit for negative?
If all this is okay then I am guessing I need to then do what you mentioned below?:

W
Scale as you wish:
adcvalue32bit = FULLSCALE * adcvalue32bit/ 0x01000000;
 

Thread Starter

kevinnas

Joined Jul 31, 2017
77
Basically this is what I have in 24 bit:
// two's comp min = 0x800000 = -8388608; What I want it to be after it is converted = 0
// two's comp zero = 0x000000 = 0; What I want it to be after it is converted = 2^23 = 8,388,608
// two's comp max = 0x7FFFFF = +8388607; What I want it to be after it is converted = (2^24)-1 = 16,777,215
The problem comes when I extend the sign bit and turn it into a 32 bit, things just get a bit confusing for me
When I extended the sign bit this is what I think it will turn into?:
// two's comp min = 0xFF800000 = -4286578688; What I want it to be after it is converted = 0
// two's comp zero = 0x00000000 = 0; converted = 2^31 = 2,147,483,648 (Max/2)
// two's comp max = 0x007FFFFF = +8388607; converted = (2^32)-1 = 4294967295 ??

If all this is okay, to impliment this do I just add 4286578688 to my 32bit adc values?
e.g scaled32bitadcval = adcvalue_32bit + 0xFF800000
 
Last edited:

MrChips

Joined Oct 2, 2009
34,812
Sorry, I forgot to remove the overflow bit in post #7.

Here is the corrected code for 24-bit unsigned integer saved in 32-bit data type:

adcvalue = (adcvalue + 0x00800000) & 0x00FFFFFF;
 
adcvalue_32bit = adcvalue+0xFF000000; // sign extend adc value to 32 bit
I'm not sure adding is the same as .OR. ing when you have negative numbers. e.g. use 8 bits 2's complement 127 = 0b1111+ 0b001 = 1
=0b1110 = -128

I think I did it right. You might get an overflow. In a 16 bit system I used 32767+1 = -32768, but adding 32767+2 would give an integer overflow. The 32767+1 was probably never caught. It could be exploited to crash the machine. Technically 32767+1 does not cause an overflow.

I do suggest ORing.

The number YOU compute in 24 bit should be the same in 32 bit. Positive numbers are the same either way. The negative numbers are the only
ones affected.

If your register is read as a 32 bit number, then you need to .AND. to remove the extraneous bits.

Set bits = number .OR. bits value ex: 1110 or 0001 = 1111
Reset bits = number .AND. .NOT. bit value' ex: 1110 .AND. .NOT. 0001 = 1110 .AND. 1110 = 1110

adcvalue_32bit = adcvalue+0x00000000; // sign extend adc value to 32 bit
This doesn't do anything. Add zero to any number, you get 0. If you have extraneous bits in your 32 bit number. they won't get removed.
 

xox

Joined Sep 8, 2017
936
Lots of ways to do this, I prefer to break everything down into simple steps mainly so that when I go back to read the code much later it's easier to remember what I was thinking at the time. Not always the most efficient approach but sometimes makes things a little more manageable in the long run.

Code:
/*
   Warning: not tested(!!!), just a basic gist of the idea...
*/
/*
   Generic get/set bit functions...
   Note:
     - functions expect one-based (NOT zero-based!) indexes
     - "data" is passed and returned by VALUE (pointers not so useful here)
*/
unsigned long
   bit_get(unsigned long data, int index)
{
   const unsigned long
     one = 1;
   unsigned long
     bit = (one << (index - 1));
   return (data & bit) != 0;
}
unsigned long
   bit_set(unsigned long data, int index, int value)
{
   const unsigned long
     one = 1;
   unsigned long
     bit = (one << (index - 1)),
     inverse = ~bit;
   if(value != 0)
     data |= bit;
   else
     data &= inverse;
   return data;
}
/*
   Example:
*/
void
   readADS1231(void)
{
   uint32_t
     adcvalue = 0;
/*
   WAIT UNTIL DATA IS READY
*/
   while(digitalRead(D_OUT))
     continue;
/*
   Deserialize 24-bits of data
*/
   int
     counter = 24;
   while(counter-- != 0)
   {
     clock();  
   /*
     Shift previous bits towards MSB, then store next bit (0 or 1)
   */    
     adcvalue <<= 1;
     adcvalue |= digitalRead(D_OUT);
   }
   unsigned long
     sign = bit_get(adcvalue, 24);
/*
   Zero 24-bit sign
*/
   adcvalue = bit_set(adcvalue, 24, 0);
/*
   Restore sign bit
*/
   adcvalue = bit_set(adcvalue, 32, sign);
   digitalWrite(CLOCK, LOW);
}
 

WBahn

Joined Mar 31, 2012
32,836
You have a common misconception of how numbers are represented in a computer.
The stored number is in binary. There is no need to convert it into decimal. The number will appear in decimal on your display device according to the function that you choose to perform the display.

What you have is 24-bit binary. You need to store it in a 32-bit signed integer data type. In order to preserve the 2's complement representation you need to shift the number to the left by 8 bits.
You don't want to left-shift the number by 8 places. This merely multiplies the value by 256, possibly resulting in an overflow.

You want to sign extend the number.

if (value & 0x00800000) value |= 0xFF000000;

If value is a 32-bit signed int, then you can also do

value << 8;
value >> 8;

The left shift will multiply the value by 256 but, if overflow occurs, it will place the sign bit of the number into the sign bit of the variable. The right shift will then divide it by 256 while sign-extending the value.

There are other ways to do it. Which one works best will depend on compiler and hardware.
 

WBahn

Joined Mar 31, 2012
32,836
We don't know how you are interfacing to the ADS1231 but we will assume that you are reading the data serially into one port bit.

Declare a 32-bit data type:
uint32_t adcvalue;

Set to zero:
adcvalue = 0;

Read the data bit and loop through 24 times:
adcvalue = (adcvalue << 1) + data_bit;

When you have acquired 24 bits, make positive:
adcvalue = adcvalue + 0x00800000;
Edited: this should read
adcvalue = (adcvalue + 0x00800000) & 0x00FFFFFF;

Scale as you wish:
adcvalue = FULLSCALE * adcvalue / 0x01000000;
What if the value already WAS positive?

What if the advvalue was -1 (which is 0x00FFFFFF)?

Now you have

adcvalue = (0x00FFFFFF + 0x00800000) & 0x00FFFFFF;

adcvalue = 0x017FFFFF & 0x00FFFFFF;

adcvalue = 0x007FFFFF;

Which is NOT equal to 1.

All your code does is toggle the sign bit. But that does NOT produce the additive inverse in 2's complement.
 
Top