DrJeseuss Posted December 1, 2011 Posted December 1, 2011 (edited) This is an example I wrote up as I worked through things. I have a microcontroller that sends data over Serial connection to the PC. The script (using CommMg.dll) from Martin, puts the hex in $ComIn. For the example below, I've removed that to allow this example to run 'simulated' for anyone. Simply enter the hex into the $ComIn variable. the hex data comes in from a DS18B20 temperature sensor. I needed to be able to convert this raw data into Celcius and/or Farenheit, including negative values. The 18B20 sends negatives as a twos compliment making the MSB a 1 for negative. In a positive number, the MSB is a 0. If anyone has any shortcuts I could have used, I'd be glad to hear them. Otherwise, the code below is fully functional. expandcollapse popup#cs $ComIn can contain additional hex, only the first two bytes are parsed in this example. No error checking is done to verify this is hex, but in my application it's in from Serial so will always be hex Note the hex values are swapped on the sending (microcontroller) end to ease calculation. In the example the actual hex is '07 D0', not 'D0 07'. The 'real' data is in format LSB MSB, but we need this as MSB LSB to work with the script below The hex is from a DS18B20 temperature sensor. For testing here is a table of data and expected results TempC Binary Hex As entered in $ComIn +125 00000111 11010000 07 D0 D007 +85 00000101 01010000 05 50 5005 +25.0625 00000001 10010001 01 91 9101 +10.125 00000000 10100010 00 A2 A200 +0.5 00000000 00001000 00 08 0800 0 00000000 00000000 00 00 0000 -0.5 11111111 11111000 FF F8 F8FF -10.125 11111111 01011110 FF 5E 5EFF -25.0625 11111110 01101111 FE 6F 6FFE -55 11111100 10010000 FC 90 90FC #ce #include <String.au3> ; Needed only for _StringInsert to add a space to the binary output for visual clarity $ComIn = "0xD007aabbcc" ; Simulated Hex in from Serial port connection. aabbcc is to validate we only grabbed first 2 bytes and ignored the rest. ConsoleWrite("Hex in: " & $ComIn & @CRLF) ; Display raw $ComIn value $TempC = BinaryMid($ComIn, 1, 2) ; Store hex bytes for TempC $TempCache = $TempC ; For use in Binary generation below since BitShift is 'destructive' $Binary = "" For $loop = 1 To 16 ; Convert hex (two bytes) to Binary 1s and 0s $Binary = BitAND($TempCache, 1) & $Binary $TempCache = BitShift($TempCache, 1) Next ConsoleWrite("$Binary: " & _StringInsert($Binary," ",8) & @CRLF) ; View resulting Binary value (_StringInsert to add a space between bytes) $TempCSign = StringLeft($Binary,1) ; Get MSB (sign bit) If $TempCSign = 0 Then ; Positive, so convert to Dec $TempC = BitOr(Binary($TempC),0) ; Results in "proper" unmodified Decimal value [Unlike using 'Dec()'] ;Same result but MUCH longer approach below ;$TempC = (StringRight($Binary,1) * 1) + (StringMid($Binary,15,1) * 2) + (StringMid($Binary,14,1) * 4) + (StringMid($Binary,13,1) * 8) + (StringMid($Binary,12,1) * 16) + (StringMid($Binary,11,1) * 32) + (StringMid($Binary,10,1) * 64) + (StringMid($Binary,9,1) * 128) + (StringMid($Binary,8,1) * 256) + (StringMid($Binary,7,1) * 512) + (StringMid($Binary,6,1) * 1024) ElseIf $TempCSign = 1 Then ; Negative so... $TempC = "-" & BitXOR(Binary($TempC), Binary("0xffff")) +1 ; get 2s compliment (with leading '-' sign) EndIf $TempC = $TempC * 0.0625 ; Multiply reading by resolution for actual value *C $TempF = ($TempC * 1.8) + 32 ; Convert to *F ConsoleWrite("TempC: " & $TempC & " TempF: " & $TempF & @CRLF) ; Display result for compare to table above Edited December 1, 2011 by DrJeseuss
wraithdu Posted December 1, 2011 Posted December 1, 2011 I've got a couple general notes for you. 1) BinaryMid() returns a binary data type, so there's no reason to keep calling Binary($TempC). 2) $TempC = BitOr(Binary($TempC), 0) is a bit silly. Number($TempC) is all you need. 3) BitXOR(Binary($TempC), Binary("0xffff")) is just confusing. The Bit* functions work with numbers, not binary types. AutoIt does a lot of internal conversion for you, but you're asking for trouble down the road. Better to use BitXOR(Number($TempC), 0xFFFF).
funkey Posted December 1, 2011 Posted December 1, 2011 I thought this is a half precision float so I decided to port a function from C. But it is not the same. But here is is the function if someone needs it. expandcollapse popup#Include <WinAPI.au3> Local $x = _Short2HalfPrecisionFloat(0x5640) ConsoleWrite ($x & @CR) Local $x = _Short2HalfPrecisionFloat(0x5A40) ConsoleWrite ($x & @CR) Func _Short2HalfPrecisionFloat($half) Local $sign = BitAND(BitShift($half, 15), 0x0001) Local $exponent = BitAND(BitShift($half, 10), 0x001F) Local $fraction = BitAND($half, 0x03FF) ;ConsoleWrite(@CRLF & StringFormat("Sign: %i; Exponent: %i; Fraction: %i", $sign, $exponent, $fraction) & @CRLF) If $exponent = 0 Then If $fraction = 0 Then Return BitShift($sign, -31) Else While Not (BitAND($fraction, 0x0400)) $fraction = BitShift($fraction, -1) $exponent -= 1 WEnd $exponent += 1 $fraction = BitAND($fraction, BitNOT(0x0400)) EndIf ElseIf $exponent = 31 Then If $fraction = 0 Then Return BitOR(BitShift($sign, -31), 0x7F800000) Else Return BitOR(BitShift($sign, -31), 0x7F800000, BitShift($fraction, -13)) EndIf EndIf $exponent += 127 - 15 $fraction = BitShift($fraction, -13) Return StringFormat("%f", _WinAPI_IntToFloat(BitOR(BitShift($sign, -31), BitShift($exponent, -23), $fraction))) EndFunc Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
DrJeseuss Posted December 1, 2011 Author Posted December 1, 2011 (edited) I've got a couple general notes for you. 1) BinaryMid() returns a binary data type, so there's no reason to keep calling Binary($TempC). 2) $TempC = BitOr(Binary($TempC), 0) is a bit silly. Number($TempC) is all you need. 3) BitXOR(Binary($TempC), Binary("0xffff")) is just confusing. The Bit* functions work with numbers, not binary types. AutoIt does a lot of internal conversion for you, but you're asking for trouble down the road. Better to use BitXOR(Number($TempC), 0xFFFF). I've made a few changes to my script to simplify things as suggested by wraithdu. Thanks! This was my first go with this type of data and using the Bit* functions so I knew I had room to grow here. I also modified a bit so it's getting the SignBit from raw data. This will eliminate the 'need' to have the binary 1s and 0s, though I've left that bit in also for visual appeal. expandcollapse popup#cs $ComIn can contain additional hex, only the first two bytes are parsed in this example. No error checking is done to verify this is hex, but in my application it's in from Serial so will always be hex Note the hex values are swapped on the sending (microcontroller) end to ease calculation. In the example the actual hex is '07 D0', not 'D0 07'. The 'real' data is in format LSB MSB, but we need this as MSB LSB to work with the script below The hex is from a DS18B20 temperature sensor. For testing here is a table of data and expected results TempC Binary Hex As entered in $ComIn +125 00000111 11010000 07 D0 D007 +85 00000101 01010000 05 50 5005 +25.0625 00000001 10010001 01 91 9101 +10.125 00000000 10100010 00 A2 A200 +0.5 00000000 00001000 00 08 0800 0 00000000 00000000 00 00 0000 -0.5 11111111 11111000 FF F8 F8FF -10.125 11111111 01011110 FF 5E 5EFF -25.0625 11111110 01101111 FE 6F 6FFE -55 11111100 10010000 FC 90 90FC #ce #include <String.au3> ; Needed only for _StringInsert to add a space to the binary output for visual clarity $ComIn = "0x90FCaabbcc" ; Simulated Hex in from Serial port connection. aabbcc is to validate we only grabbed first 2 bytes and ignored the rest. ConsoleWrite("Hex in: " & $ComIn & @CRLF) ; Display raw $ComIn value $TempC = BinaryMid($ComIn, 1, 2) ; Store hex bytes for TempC $SignBit = BitShift($TempC, 15) #region ; This section produces the binary 1s and 0s for visualization $TempCache = $TempC ; 'Disposable' variable for use in Binary generation below $Binary = "" For $loop = 1 To 16 ; Convert hex (two bytes) to Binary 1s and 0s $Binary = BitAND($TempCache, 1) & $Binary $TempCache = BitShift($TempCache, 1) Next ConsoleWrite("$Binary: " & _StringInsert($Binary," ",8) & @CRLF) ; View resulting Binary value (_StringInsert to add a space between bytes) #endregion If $SignBit = 0 Then ; Positive, so convert to Dec $TempC = Number($TempC) ; Results in "proper" unmodified Decimal value [Unlike using 'Dec()'] ;Same result but MUCH longer approach below ;$TempC = (StringRight($Binary,1) * 1) + (StringMid($Binary,15,1) * 2) + (StringMid($Binary,14,1) * 4) + (StringMid($Binary,13,1) * 8) + (StringMid($Binary,12,1) * 16) + (StringMid($Binary,11,1) * 32) + (StringMid($Binary,10,1) * 64) + (StringMid($Binary,9,1) * 128) + (StringMid($Binary,8,1) * 256) + (StringMid($Binary,7,1) * 512) + (StringMid($Binary,6,1) * 1024) ElseIf $SignBit = 1 Then ; Negative so... $TempC = "-" & BitXOR(Number($TempC), 0xffff) +1 ; get 2s compliment (with leading '-' sign) EndIf $TempC = $TempC * 0.0625 ; Multiply reading by resolution for actual value *C $TempF = ($TempC * 1.8) + 32 ; Convert to *F ConsoleWrite("TempC: " & $TempC & " TempF: " & $TempF & @CRLF) ; Display result for compare to table above Edited December 1, 2011 by DrJeseuss
wraithdu Posted December 1, 2011 Posted December 1, 2011 (edited) I should have thought of this before, but you could also just test the sign bit like this: If (BitAND(Number($TempC), 0x8000) = 0x8000) Then Edited December 1, 2011 by wraithdu
DrJeseuss Posted December 2, 2011 Author Posted December 2, 2011 I should have thought of this before, but you could also just test the sign bit like this: If (BitAND(Number($TempC), 0x8000) = 0x8000) Then In my edited code, I'm using a bit different approach. Is there anything I'm missing or will mine do basically the same result? I see your method becomes part of the expression while mine currently uses a "flag" variable ($SignBit). I suppose I could eliminate the $SignBit by doing the following. I've tried it and it seems to work... and a bit less code than you suggested. If BitShift($TempC, 15) = 0 Then ; Positive, so convert to Dec $TempC = Number($TempC) ; Results in "proper" unmodified Decimal value [Unlike using 'Dec()'] ElseIf BitShift($TempC, 15) = 1 Then ; Negative so... $TempC = "-" & BitXOR(Number($TempC), 0xffff) +1 ; get 2s compliment (with leading '-' sign) EndIf I'm only just beginning to grasp the power of these Bit* functions and binary math. I may have questions for you down the road. Thanks again for the extra set of eyes.
DrJeseuss Posted December 2, 2011 Author Posted December 2, 2011 The more I think about this the more I wonder if I've reinvented the wheel. Maybe I've missed a simple function that will do the same. Is there a simper way to take my hex in it's 'natural' form of LSB MSB (0x07D0) to get 125, or (0xFE6F) to get -25.0625, etc (per the table in my example)?
wraithdu Posted December 2, 2011 Posted December 2, 2011 (edited) I think you've simplified it as much as possible. I personally wouldn't use the BitShift function to test for the sign bit like that, just because you're dealing with a 16 bit value. The Bit* functions work with 32-bit numbers, and there isn't any documentation as to how those upper 16 bits are padded when feeding a 16-bit number to BitShift. I would assume 0's, but I can't say that is true in all cases. There's also the issue of propagation of the sign bit with BitShift and 32-bit numbers. For example $n = 0x80000000 ConsoleWrite(Hex(BitShift($n, 16)) & @CRLF) ; output = FFFF8000 Anyway, that's just me being weird. Edited December 2, 2011 by wraithdu
DrJeseuss Posted December 2, 2011 Author Posted December 2, 2011 I think you've simplified it as much as possible. I personally wouldn't use the BitShift function to test for the sign bit like that, just because you're dealing with a 16 bit value. The Bit* functions work with 32-bit numbers, and there isn't any documentation as to how those upper 16 bits are padded when feeding a 16-bit number to BitShift. I would assume 0's, but I can't say that is true in all cases. From my testing and trying to break this I've seen that the padding is in fact 0's, though it may not always be true. I've yet to see an exception though so far. There's also the issue of propagation of the sign bit with BitShift and 32-bit numbers. For example $n = 0x80000000 ConsoleWrite(Hex(BitShift($n, 16)) & @CRLF) ; output = FFFF8000 Anyway, that's just me being weird. Can you explain what 'propogation of the sign bit' means? I'm not entirely clear on hat's happening in your example. It does appear that I need to ensure only proper byte counts are used for input to this script.
wraithdu Posted December 2, 2011 Posted December 2, 2011 As with your data, the MSB is the sign bit for signed 32-bit integers, ie 0x80000000 = 10000000 00000000 00000000 00000000 ^ If you BitShift this 16 bits to the right as I did above, AutoIt returns 0xFFFF8000 = 11111111 11111111 10000000 00000000 The sign bit is propagated down the line.
DrJeseuss Posted December 3, 2011 Author Posted December 3, 2011 As with your data, the MSB is the sign bit for signed 32-bit integers, ie0x80000000 =10000000 00000000 00000000 00000000^If you BitShift this 16 bits to the right as I did above, AutoIt returns0xFFFF8000 =11111111 11111111 10000000 00000000The sign bit is propagated down the line.I think I understand now... despite using a 16-bit hex, autoit (and BitShift) are still treating this as a 32-bit, padding as needed to make the shift. As such, I may have issues if I BitShift PAST my 16 bits... is this correct. So, in short, if I'm careful to stay in bounds of my 16 bits (in this example) then I should not enter this padded region and therefore my data would remain accurate, right?
czardas Posted December 3, 2011 Posted December 3, 2011 The 32 bit integer will always be interpreted as having the MSB as the first bit. If you wish to redefine this, then I suggest you set the MSB to zero and only work within the confines of the other 31 bits. operator64 ArrayWorkshop
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now