# 2's compliment to binary

Discussion in 'General Electronics Chat' started by kevinnas, Sep 27, 2017.

1. ### kevinnas Thread Starter Member

Jul 31, 2017
73
3
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:
{
//24-->21 bits
clock ();
clock ();
clock ();
.... etc

File size:
911.8 KB
Views:
1
2. ### MrChips Moderator

Oct 2, 2009
19,114
6,145
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.

3. ### kevinnas Thread Starter Member

Jul 31, 2017
73
3
Understood.
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?

4. ### KeepItSimpleStupid AAC Fanatic!

Mar 4, 2014
3,544
669
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
--------------------s
s is the sign bit.

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

Last edited: Sep 27, 2017
kevinnas likes this.
5. ### MrChips Moderator

Oct 2, 2009
19,114
6,145
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.

kevinnas likes this.
6. ### kevinnas Thread Starter Member

Jul 31, 2017
73
3
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?

unsigned long bit24;
long X;

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

//24-->1
clock ();
clock ();
// Bit 24 all the way down to bit 1:
//
clock ();
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
{
}
else // sign bit is = 0
{
}
//
digitalWrite(CLOCK, LOW);
}

7. ### MrChips Moderator

Oct 2, 2009
19,114
6,145
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:

Set to zero:

Read the data bit and loop through 24 times:

When you have acquired 24 bits, make positive:

Scale as you wish:

xox, kevinnas and cmartinez like this.
8. ### dl324 AAC Fanatic!

Mar 30, 2015
8,724
2,107
Code (Text):
3. unsigned long bit24;
4. long X;
5.
8.     X = 0;
9.     bit24 = 0x800000; // sign bit of the 24 bit adcvalue - bit 24 ( or 23rd bit)
11.
13.     //24-->1
14.     clock ();
16.     clock ();
18.     // Bit 24 all the way down to bit 1:
19.     //
20.     clock ();
22.     clock ();//TO PULL DATA READY TO HIGH STATE --PAGE No.14 ON ADS1231 DATASHEET
23.
24.     // Sign Extending adcvalue (24 bit) to 32 bit
25.     X = bit24 & adcvalue; // zero-ing the rest of the adcvalue bits except the sign bit.
26.     if(X==bit24) // if sign bit is = 1
27.     {
29.     }
30.     else        // sign bit is = 0
31.     {
33.     }
34.     //
35.     digitalWrite(CLOCK, LOW);
36. }

Last edited: Sep 27, 2017
kevinnas likes this.
9. ### kevinnas Thread Starter Member

Jul 31, 2017
73
3
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?:

10. ### kevinnas Thread Starter Member

Jul 31, 2017
73
3
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?

Last edited: Sep 28, 2017
11. ### MrChips Moderator

Oct 2, 2009
19,114
6,145
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:

12. ### KeepItSimpleStupid AAC Fanatic!

Mar 4, 2014
3,544
669
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

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.

13. ### xox Member

Sep 8, 2017
351
74
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 (Text):
1.
2. /*
3.    Warning: not tested(!!!), just a basic gist of the idea...
4. */
5. /*
6.    Generic get/set bit functions...
7.    Note:
8.      - functions expect one-based (NOT zero-based!) indexes
9.      - "data" is passed and returned by VALUE (pointers not so useful here)
10. */
11. unsigned long
12.    bit_get(unsigned long data, int index)
13. {
14.    const unsigned long
15.      one = 1;
16.    unsigned long
17.      bit = (one << (index - 1));
18.    return (data & bit) != 0;
19. }
20. unsigned long
21.    bit_set(unsigned long data, int index, int value)
22. {
23.    const unsigned long
24.      one = 1;
25.    unsigned long
26.      bit = (one << (index - 1)),
27.      inverse = ~bit;
28.    if(value != 0)
29.      data |= bit;
30.    else
31.      data &= inverse;
32.    return data;
33. }
34. /*
35.    Example:
36. */
37. void
39. {
40.    uint32_t
42. /*
43.    WAIT UNTIL DATA IS READY
44. */
46.      continue;
47. /*
48.    Deserialize 24-bits of data
49. */
50.    int
51.      counter = 24;
52.    while(counter-- != 0)
53.    {
54.      clock();
55.    /*
56.      Shift previous bits towards MSB, then store next bit (0 or 1)
57.    */
60.    }
61.    unsigned long
63. /*
64.    Zero 24-bit sign
65. */
67. /*
68.    Restore sign bit
69. */
71.    digitalWrite(CLOCK, LOW);
72. }
73.

14. ### WBahn Moderator

Mar 31, 2012
24,555
7,691
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.

15. ### WBahn Moderator

Mar 31, 2012
24,555
7,691
What if the value already WAS positive?

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

Now you have

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