Shop OBEX P1 Docs P2 Docs Learn Events
Two's Compliment to Normal Dec — Parallax Forums

Two's Compliment to Normal Dec

TJHJTJHJ Posts: 243
edited 2008-08-13 22:21 in Propeller 1
So I am receiving a number that is stored in two's complement, with a signed 1st bit, 12bits of data, stored in a 16 bit register. How do I get this back to a normal dec number so I can use it, It is incrementing 15.625 uv per increment. I am thinking that if I can get this back to dec format, than I would be able to read the voltage value in thousandths of uv by simply multiplying by 15625. But I cant seem to get it into the propeller 32bit math system and get something that makes sense.

The value is stored in a register like so.
Sign, 12 bits, X, X, X for the 16 bit system.
Its received in two 8 bit registers.
S 7 Bits.
5 bits, 3 Dead spaces.

So how do I get this back to a normal number that can be used.

TJ

Comments

  • TimmooreTimmoore Posts: 1,031
    edited 2008-08-13 03:01
    assume 2 variables first and second

    concaternate the 2 getting rid of the 3 dead bits
    no := (first << 5) + (second >> 3)
    shift the sign bit to bit 31 and then sign extend back to bit 13
    no := (no << (32-13)) ~> (32-13)
  • AleAle Posts: 2,363
    edited 2008-08-13 06:35
    If you are doing it in assembler, I'd use this:



      mov two_s_complemented_variable,low_bits
      shl   high_bit,#8
      or    two_s_complemented_variable,high_bits
      shl   two_s_complemented_variable,#20
      abs  two_s_complemented_variable,two_s_complemented_variable
      shr  two_s_complemented_variable,#20
    
    



    You combine both parts, I assume they only contain 8 bit values, if not... you have to add some "and" guarding
    You shift left bit 12 to position 31 so it is used as sign and the instruction abs will take the absolute value, and a second shift this time right will reposition the significant digits where they have to be.

    If you know that _all_ your variables are two's complemented, you can instead of abs neg, but there is no time or size advantage in this case.

    Post Edited (Ale) : 8/13/2008 6:40:39 AM GMT
  • 4Alex4Alex Posts: 119
    edited 2008-08-13 14:27
    Hi TJ:

    15.625uV increments? Sounds like you're reading a fuel gauge chip. I've used successfully the DS2760 and here's the code (in C) to deal with 2's-complements and extract the current value:

    //msb: [noparse][[/noparse]SDDDDDDD] S=signed bit, D=data
    //lsb: [noparse][[/noparse]DDDDDXXX] D=data X=ignore
    write_byte (0x0E); //point to current register msb @ $0E
    msb == reab_byte(); //read msb from register
    lsb == reab_byte() & 0xF8; //read lsb and mask off lower 3 bits
    temp = msb<<8 + lsb; //merge values
    if ((msb & 0x80) == 0x80); //if sign bit is set
    temp = temp - 65536; //substract to get rid of 2's-complement
    //current equation: current = (Number_of_increments * uV) / Sense_resistor
    //(temp * 15.625 x 10^-6 Volts) / 20 x 10^-3 Ohms sense resistor = current in mA
    current = ((temp>>3) * 0.000015625) / 0.020;

    Hope this help. If you do use a fuel gauge, I've stopped using the Coulomb counting approach (as the 15.625 uV would suggest you're using) for the Open-Circuit Voltage (OCV) method, which is light-years simpler to implement in LiPo fuel-gauging as you don't need to know any of the battery parameters (try finding these half-a-dozen or so parameters for a LiPo bought at SparkFun and coming from China...). The chip I currently use is DS2786 (Digikey DS2786G+-ND). It's an I2C chip, requiring very minimal external parts, and a best-fit universal battery profile is already in the chip's eeprom. Only drawback is the TDFN-10 package, but it's easier to solder than it looks (dots of solder paste & frying pan method works very well, just make the landing pads somewhat longer).

    Cheers,

    Alex
  • TJHJTJHJ Posts: 243
    edited 2008-08-13 16:01
    Thanks all so far actually is a thermocouple type K reading im trying to get.
    Timmoore said...

    shift the sign bit to bit 31 and then sign extend back to bit 13
    no := (no << (32-13)) ~> (32-13)




    Does this work in a local variable? Arn't they word sized?

    4Alex said...

    temp = temp - 65536; //substract to get rid of 2's-complemen

    Is it really that simple all I need to do is subtract 65536, the maxium size for a word variable? If so wow I am way over thinking this.

    Thanks
    TJ
  • TimmooreTimmoore Posts: 1,031
    edited 2008-08-13 16:30
    locals are longs.
    The way to look at this is
    1. +ve numbers need to have 0s filling the bits you dont care about
    2. -ve numbers need to have 1s filling the bits you dont care about
    so no << (32-13) shifts your sign bit to bit 31 which is the real long sign bit.
    ~> (32-13) shifts them back copying bit 31 to the new bit 31. So this gives you the correct response in both cases. i.e. fill with 0 is +ve and fill with 1 if -ve
    Any other way to do the same thing works. so testing the sign bit then - 65535 from a +ve number that is less than 32767 will give the same effect.
    The other thing to look at are the spin operators ~ and ~~, ~~x will sign extend from bit 15, i.e. sign extend a work to a long. ~x will sign extend a byte to a long. ~~x is the same as x:= (x<<16)~>16 and ~x is the same as x:=(x<<24)~>24

    Post Edited (Timmoore) : 8/13/2008 4:55:23 PM GMT
  • TJHJTJHJ Posts: 243
    edited 2008-08-13 17:06
    Ok so working better, Now it is doing something odd. I am barely grasping the concept here, so if I am having a hardware or software issue is a mystery…
    I hooked up a pot to test it and see if it was ranging / working correctly. With the pot at 0, almost ground I get a range of -700 ish, it jumps pretty fast. Then as I move towards positive around 3600 it jumps to 4095. Any Ideas what I am looking at? A hardware or a decode issue?
    When the display is around 0, the pot is showing +8.1mv.
    ·
    I ended up having to use the ! to flip it to positive so it would still read a negative value, versus the abs command. Otherwise it would scale backwards.
    ·
    Here is the code I am using to read it.
    Var
    Byte MSB, LSB
     
    Pub GetVolt(Offset) | Temp
      temp := 0
      ow.reset                                              'Sorry Edit here, copy paste code reuse. 
      ow.writeByte(Match_net)   ' Send the match net command
      ow.writeAddress((p+(offset*8)))  ' Use one of the adreeses found earlier using OWseach. There is only 1 chip hooked up right now. 
      ow.writeByte(READ_Data)          ' Send read reg command
      ow.writeByte(Probereg)           ' Send the Reg adress we want to read
      MSB:= (ow.readByte )             ' Read the MSB reg we want. 
      
      ow.reset
      ow.writeByte(Match_net)
      ow.writeAddress((p+(offset*8)))
      ow.writeByte(READ_data)
      ow.writeByte(probeReg1)
      LSB := ow.readByte 
      ' Thanks Timmoore for the following. 
      temp := (((msb) << 5) + (lsb >> 3)) 
      'shift the sign bit to bit 31 and then sign extend back to bit 13
      temp := (temp << (32-13)) ~> (32-13)
      
      return !temp 
     
    

    edit* Test using sign extend.
    ' Thanks Timmoore for the following. 
      temp := (((~msb) << 5) + (lsb >> 3)) ' Also works using the sign extend operator but gives the same results. 
      
      
      return !temp  
    
    


    Post Edited (TJHJ) : 8/13/2008 10:16:47 PM GMT
  • TimmooreTimmoore Posts: 1,031
    edited 2008-08-13 17:20
    I haven't used this sensor before but a quick look at the datasheet suggests that a good test would be to not to send the temp convert command but still issue the read temperature command, you should get $0550 as the default temp. If you try this, this should show whether you are reading and converting the input data correctly.
  • 4Alex4Alex Posts: 119
    edited 2008-08-13 18:51
    Hi TJ,

    I might be completely wrong, so don't be offended if I am way out, but here's a few comments:

    1. If you have only one 1-wire chip on a network, you don't need to perform a match_rom ($55) each time.
    Just issue a reset cmd, followed by the skip_search ($CC) immediately followed by read_scratchpad ($BE).

    2. From the datasheet for the DS1822, the way to read the registers are reverse from what you do:
    The LSB seem to be first then MSB follows at the next onewire.read (page 11 of ds)

    Here's a way that should work:

    First, issue the conversion command sequence:

    onewire.reset 'reset command
    onewire.writeByte($CC) 'skip_rom command
    onewire.writeByte($44) 'convert temperature command

    'at this point, you MUST wait at least 750ms for the 12-bit resolution [noparse][[/noparse]Tconv]
    conversion to happen (see pages 4 & 20 of ds). In the posted code you don't seem
    to wait at all.

    then, issue the reading sequence:
    onewire.reset 'reset command
    onewire.writeByte($CC) 'skip_rom command
    onewire.writeByte($BE) 'read_scratchpad command

    The LSB is read first, followed by the MSB (page 4):

    lsb:= onewire.readByte ' read temperature LSB
    msb :=onewire.readByte ' read temperature MSB
    temp := (((~msb) << 5) + (lsb >> 3)) ' as from Timmoore

    I don't have the chip so I can't test it but this should work. Good luck.

    BTW, you mentioned the need to work with increments of 15.625 uV. I don't see where in your code
    you use it nor why you need it since the chip returns the temperature in increments of 0.0625 degree C
    (at 12-bit resolution) directly from the register you read. Try it with sample data shown at page 4: 85dec
    should read $550 (1360dec), which multiplied by 0.0625 (12-bit res) gives... 85 deg C. Also, I doubt it is
    your default temperature as suggested (it's pretty hot!): 85C is only at power-on; following a proper setup
    you should read room temperature, which should give you about $191 (25C, as per ds).

    Cheers,

    Alex
  • TJHJTJHJ Posts: 243
    edited 2008-08-13 19:46
    4Alex Thank you for the advice, but I am not reading the temperature register. I am sorry for not specifying completely.
    ·
    What makes this very confusing is the DS2762 without an internal sense resistor, The current register reads voltage in 15.625uv, Figure 4. on the datasheet, Stored in locations MSB = 0E(h) and LSB = 0F(h). This is the voltage applied to the SNS pins.
    ·
    If I can get this into a Dec format that each step is 15.625uv, I can use my lookup table that I made for type k, from 0deg C up to 1400Deg C. And is in thousandth of uv resolution, So then I can take the Decimal number multiply by 15625, run my lookup get the position in the table and· know Deg C of the thermocouple.
    ·
    Right now it is just a repeating loop to a VGA out, looking up each chip. Printing it to the screen and then waiting at the end to give time for the measurement to occur again.
    ·
    4Alex said...
    1. If you have only one 1-wire chip on a network, you don't need to perform a match_rom ($55) each time.
    Just issue a reset cmd, followed by the skip_search ($CC) immediately followed by read_scratchpad ($BE).
    ·
    Under the final setup, I plan to use each ds2762 on its own pin to allow me to physically know which sensor is where. So then I plan to use the skip Match Rom command. Right now I have them all tied on one wire, so I can get an idea if they are all reading the same then I know something is going my way.
  • 4Alex4Alex Posts: 119
    edited 2008-08-13 20:59
    Hi Tj,

    I am completely lost now:

    Not reading temperature? What all your postings are about then? Which chip do you really use? the DS1822 (with thermocouple type k) as indicated in your code snippet Posted Today 9:01 AM (GMT -7) or the DS2762 that you NEVER EVER indicated anywhere before on this thread (in which case you DO refer to a LiPo battery fuel-gauge as per my first reply to you)? Perhaps it's not just me that is lost.

    Also, why don't you try debugging 1 chip at a time? DS1822 is VERY simple, elementary even. Once mastered, you can use 1-wire protocols on more of the same chip in a network or more complex chips. DS2762 is a REAL pain to deal with if you don't have a clear idea of what you're doing. Unless you use this chip for the most basic function, it is really intended for fuel-gauge/Battery monitoring applications, in which case you will need to write and implement a *complex* piece of software on your processor-side. Believe me.

    As for the DS2762, the chip is intended for 1 LiPo battery. How many batteries are you monitoring? The chip offers the option of an internal sense resistor and an external sense resistor. Which option are you using? There's no thermocouple there to address...

    Finally, if you intend to use the DS2762 to monitor a LiPo battery in an application where you developped your own charger, be aware that you better double-check what you're doing as a mistake can lead the battery to explode. And its ugly.

    Cheers,

    Alex
  • TJHJTJHJ Posts: 243
    edited 2008-08-13 22:21
    Ok sorry, I reused the code from my initial experiment on the 1822,( forgot to take that comment out) and then have converted it to the ds2762. I am simply trying to make a spin object and recreate the thermocouple kit from parallax.


    http://www.parallax.com/Store/Sensors/TemperatureHumidity/tabid/174/CategoryID/49/List/0/Level/a/ProductID/96/Default.aspx?SortField=ProductName%2cProductName

    The only catch is the ds2762 is the replacement for the ds2760. Same chip basically. But yes it is a lipo charger chip, but it is also so perfect for reading a thermocouple. This is not really a Type K reading chip, but using the current input to sense voltage in the external resistor configuration, with the thermocouple acting as my resistor. The +-64mv range is ideal.

    I can get to any register I want, read the·Chip tempature just·fine, all through 1-wire. But when I read this voltage register, everything·gets screwy on me.·Sorry, I asumed my problem was in how I was dealing with the two's comp, and the MSB vs LSB. ·
    Hope this clears up what is going on here.
Sign In or Register to comment.