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:
    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
     
  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
    Hello. Thank you for your reply.
    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
    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: 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
    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);
    }
     
  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:
    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;
     
    xox, kevinnas and cmartinez like this.
  8. dl324

    AAC Fanatic!

    Mar 30, 2015
    8,724
    2,107
    Your code will be more readable if you use a code box; slightly more readable in your case...
    Code (Text):
    1. long adcvalue;
    2. uint32_t adcvalue_32bit;
    3. unsigned long bit24;
    4. long X;
    5.  
    6. void readADS1231(void) {
    7.     adcvalue = 0;
    8.     X = 0;
    9.     bit24 = 0x800000; // sign bit of the 24 bit adcvalue - bit 24 ( or 23rd bit)
    10.     adcvalue_32bit = 0;
    11.  
    12.     while(digitalRead(D_OUT));//WAIT UNTIL DATA IS READY
    13.     //24-->1
    14.     clock ();
    15.     if(digitalRead(D_OUT)){adcvalue=adcvalue+0x800000;} //24
    16.     clock ();
    17.     if(digitalRead(D_OUT)){adcvalue=adcvalue+0x400000;}//23
    18.     // Bit 24 all the way down to bit 1:
    19.     //
    20.     clock ();
    21.     if(digitalRead(D_OUT)){adcvalue=adcvalue+0x1;}//1
    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.     {
    28.       adcvalue_32bit = adcvalue+0xFF000000; // sign extend adc value to 32 bit
    29.     }
    30.     else        // sign bit is = 0
    31.     {
    32.       adcvalue_32bit = adcvalue+0x00000000; // sign extend adc value to 32 bit
    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?
    e.g scaled32bitadcval = adcvalue_32bit + 0xFF800000
     
    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:

    adcvalue = (adcvalue + 0x00800000) & 0x00FFFFFF;
     
  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
    38.    readADS1231(void)
    39. {
    40.    uint32_t
    41.      adcvalue = 0;
    42. /*
    43.    WAIT UNTIL DATA IS READY
    44. */
    45.    while(digitalRead(D_OUT))
    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.    */    
    58.      adcvalue <<= 1;
    59.      adcvalue |= digitalRead(D_OUT);
    60.    }
    61.    unsigned long
    62.      sign = bit_get(adcvalue, 24);
    63. /*
    64.    Zero 24-bit sign
    65. */
    66.    adcvalue = bit_set(adcvalue, 24, 0);
    67. /*
    68.    Restore sign bit
    69. */
    70.    adcvalue = bit_set(adcvalue, 32, sign);
    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;

    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.
     
Loading...