Shop OBEX P1 Docs P2 Docs Learn Events
Please help with multiplying irrational numbers. volt meter project! — Parallax Forums

Please help with multiplying irrational numbers. volt meter project!

Tony_tsiTony_tsi Posts: 98
edited 2012-04-09 14:26 in Propeller 1
I am using an eight bit a/d converter to check the voltage on a circuit. I am very close my calculations are just a little off and I do not know why. I am pretty sure that the problem is in line 56 of my code.
The a/d converter has 255 steps between ground and 5v, so to obtain the proper voltage I must divide 5 by 255 this gives me the amount of voltage that each step is worth. Now if I take the number coming from the a/d converter and multiply it by (5/255) then it should in theory give me the correct voltage reading. But I just can't get it to work this way.
If anyone has any suggestions for how I can fix this problem please help me out.

Comments

  • pedwardpedward Posts: 1,642
    edited 2012-04-09 11:59
    Have you debug printed all of the values you are getting at each step? The ADC reading, the volt_step, and volt_Calc?
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:05
    yes for volt_step i get 0.01960 (correct as as rounded irrational number)
    the ADC reading has a range of 0 to 255 (correct)
    The volt_ calc should be the voltage reading it is off by + 2
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2012-04-09 12:08
    In an 8-bit adc there are actually 256 steps... zero counts, so your number should be 0.01953125 volts per step.

    How far are you off? do you have a voltage divider on the front of the ADC or anything else that might affect the result?
  • pedwardpedward Posts: 1,642
    edited 2012-04-09 12:09
    2 counts or 2 volts? Can you provide actual sample values of these variables? I have a couple of ideas off the top of my head: signed value conversion, floating point rounding error.

    EDIT: Beau also touched on 256 and 255. Although 255 is the max count, and .0195 * 255 != 5 volts
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-04-09 12:11
    Without knowing to much about the float objects, I'd expect it to work with float-numbers. But you call the functions with 5, 255 ... which will be converted to integers by the compiler.
    So, even if the number has no fractionals you should call it with 5.0, 255.0 ....

    I'm also not sure wheather your binaryToDecimal is correct. I'd expect that the float objects provide some functions to convert an integer to a float.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 12:13
    When you use a floating point object, you want to make sure all the values you use with it are floating point.

    I don't see where the value "ADC_value" is every converted to a floating point number.

    What program is "ADC_Driver"?

    I don't see why you need the method "BinaryToDecimal"?

    Binary and decimal numbers are just to make it easier for us humans to read them. Normally they are stored the same in the uC's memory.

    I bet you don't need a floating point object at all. I think all your math could be done as pseudoreal (you might want to try "pseudo-real" and "pseudo real" with your search).
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:15
    I changed my code slightly so that i can view all three values at the same time. when ADC_value is 255 voltage_calc is 7.00649
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:17
    In an 8-bit adc there are actually 256 steps... zero counts, so your number should be 0.01953125 volts per step.

    How far are you off? do you have a voltage divider on the front of the ADC or anything else that might affect the result?

    this is much closer but still off now at ADC_value 255 the Volt_calc is 5.6
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-04-09 12:18
    Oh .. and by the way ... I don't know why you want to use floats at all?!

    Most ADC have a inaccuracy - for example 1 or 2 LSBs. This means that with 5V and 255 steps you have an inaccuracy of
    0,01960784313725490196078431372549 which means that it's worthless to show more than 3 digits.

    By simply using 500 as an equivalent of 5V you can use simple integer math to calculate your voltages.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:23
    MagIO2 wrote: »
    Oh .. and by the way ... I don't know why you want to use floats at all?!

    Most ADC have a inaccuracy - for example 1 or 2 LSBs. This means that with 5V and 255 steps you have an inaccuracy of
    0,01960784313725490196078431372549 which means that it's worthless to show more than 3 digits.

    By simply using 500 as an equivalent of 5V you can use simple integer math to calculate your voltages.

    I am new to programming most of the stuff you see in this code is copy, paste, modify. I only want to see 2 decimal places i just don't know how to do that yet.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:25
    Duane Degn wrote: »
    When you use a floating point object, you want to make sure all the values you use with it are floating point.

    I don't see where the value "ADC_value" is every converted to a floating point number.

    What program is "ADC_Driver"?

    I don't see why you need the method "BinaryToDecimal"?

    Binary and decimal numbers are just to make it easier for us humans to read them. Normally they are stored the same in the uC's memory.

    I bet you don't need a floating point object at all. I think all your math could be done as pseudoreal (you might want to try "pseudo-real" and "pseudo real" with your search).

    What is a floating point decimal? sorry I don't mean to ask dumb questions I am new to programming.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 12:28
    The thread linked to below talks about pseudo real numbers a bit.

    Post #4 is a method I wrote to output an integer with a decimal point to make it look like a floating point number had been used.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-04-09 12:33
    In an 8-bit adc there are actually 256 steps... zero counts, so your number should be 0.01953125 volts per step.
    Not quite. There are 256 values, but only 255 steps between 0 and 255. So 5.0 / 255.0 == .019607843 is correct. That's assuming, of course, that 255 actually corresponds to 5V and not the (non-existent) 256.

    -Phil
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 12:38
    WHY IS 5/255 = 0 I do not understand this! This is why I am tiring to use floating point numbers but I don't understand them either.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-04-09 12:46
    5/255 is integer division. It gets truncated to zero. You need to use floating point division to get a floating point constant: i.e. 5.0 / 255.0. Or, if done at run time, it would be f.fdiv(5.0, 255.0).

    -Phil
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 13:04
    The Prop chops off everything after the decimal point. So while 5/255 equals 0.0196 on a calculator, with the Prop, it equals 0.

    If you use 50,000 / 255 you'll get 196 with the Prop. This should be a usable number.

    You know 196 is really 0.0196 and you keep this in mind when you write the rest of your program.

    If 196 was stored in the variable "value", you could display it with a decimal point using the method (linked to earlier) "DecPoint" by calling it like this:
    DecPoint(value, 10000)
    

    You'd use the number "10000" since that what you multiplied "5" by to get "50000".

    There's more discussion about pseudo real numbers in that thread.

    Floating point numbers are stored differently in the Propeller's memory than the way normal integers are stored. You can't mix the two with math functions. You also can't use normal Spin math and comparisons with floating point numbers (if you want the answers to be meaningful).

    Edit: codeviper's calculator is a good example of using floating point numbers. He made the common mistake of mixing integer math operators with floating point operators in his expoent function "^". The rest of the functions work correctly.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 13:14
    The ADC_value and volt_step values are both correct!
    The problem has to be in line 57 or 61!
    Keep in mind volt_calc:= ACD_value*volt_step
    table of output:

    ADC_value volt_step volt calc
    0 0.01960784 0
    20 0.01960784 0
    40 0.01960784 0
    .................................................. .........
    50 0.01960784 0
    51 0.01960784 1.40
    .................................................. ............
    101 0.01960784 1.40
    102 0.01960784 2.8
    .................................................. ..........
    152 0.01960784 2.8
    143 0.01960784 4.2
    .................................................. .........
    203 0.01960784 4.2
    204 0.01960784 5.6
    ...........................................
    255 0.01960784 7.0
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 13:18
    Tony_tsi wrote: »
    The ADC_value and volt_step values are both correct!
    The problem has to be in line 57 or 61!
    Keep in mind volt_calc:= ACD_value*volt_step
    table of output:

    ADC_value volt_step volt calc
    0 0.01960784 0
    20 0.01960784 0
    40 0.01960784 0
    .................................................. .........
    50 0.01960784 0
    51 0.01960784 1.40
    .................................................. ............
    101 0.01960784 1.40
    102 0.01960784 2.8
    .................................................. ..........
    152 0.01960784 2.8
    143 0.01960784 4.2
    .................................................. .........
    203 0.01960784 4.2
    204 0.01960784 5.6
    ....................................
    255 0.1960784 7.0

    I have tried 3 times to space the numbers out and it keeps pushing them to the left in the table sorry!
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 13:35
    line 57 volt_Calc :=(fmath.Fmul(ADC_value,volt_step))
    This is my problem this only puts out an integer I need a number with 2 decimal places.
    how do i fix this?
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 13:43
    I think a lot of your difficulty is understanding the way numbers are stored in memory.

    Floating point numbers are stored differently the integers.

    While all numbers are stored as one and zeros, floating point numbers use the 32 bits differently (I don't really worry about it myself, I just know I can't mix integers and floating point numbers without converting one to the other first).


    Different Topic:

    Binary, hexadecimal and decimal numbers are just ways of displaying numbers for us humans. The Propeller stores them the same way (excluding floating point numbers).

    The method "BinaryToDecimal" copies a number one bit at a time and returns the copied number. Since it only copies eight bits, the only numbers it will change are numbers larger than 255.

    You could do the same thing with.
    value &= 255
    

    The above code will zero out all but the lowest 8 bits.

    Just to be sure I wrote a quick demo of "BinaryToDecimal".
    CON
      _CLKMODE = XTAL1 + PLL16X
      _CLKFREQ = 80_000_000
      _DebugBaud = 57_600
      
    OBJ
      Debug : "Parallax Serial Terminal"
      
    PUB Main | localIndex
      waitcnt(clkfreq * 3 + cnt)    ' time to open terminal window.
      
      Debug.Start(_DebugBaud)
      Debug.Clear
      repeat localIndex from 0 to 255
        Debug.Str(string(13, "localIndex before conversion = "))
        Debug.dec(localIndex)
        result := BinaryToDecimal(localIndex)
        Debug.Str(string(13, "localIndex after BinaryToDecimal conversion = "))
        Debug.dec(result)
      repeat
          
    PUB BinaryToDecimal(aBinVal) : rVal | multiplier, bitMask
       rVal := 0                                                                    'empty returned val
       multiplier := 1                                                              'start with multiplier of 1
       bitMask := 1                                                                 'start with bitMask of 1 (LSB)
      repeat 8                                                                      'repeat for all 8 bits
        if aBinVal & bitMask > 0                                                    'if bit at position of bitMask is 1
          rVal += multiplier                                                        'add multiplier to aVal
        multiplier *= 2                                                             'double value of multiplier
        bitMask := bitMask << 1                                                     'move bit in bitMask to left
      return rVal    
             
    

    Here's a portion of the output.
    localIndex before conversion = 0
    localIndex after BinaryToDecimal conversion = 0
    localIndex before conversion = 1
    localIndex after BinaryToDecimal conversion = 1
    localIndex before conversion = 2
    localIndex after BinaryToDecimal conversion = 2
    localIndex before conversion = 3
    localIndex after BinaryToDecimal conversion = 3
    localIndex before conversion = 4
    localIndex after BinaryToDecimal conversion = 4
    localIndex before conversion = 5
    localIndex after BinaryToDecimal conversion = 5
    localIndex before conversion = 6
    localIndex after BinaryToDecimal conversion = 6
    localIndex before conversion = 7
    localIndex after BinaryToDecimal conversion = 7
    localIndex before conversion = 8
    localIndex after BinaryToDecimal conversion = 8
    

    So you don't need the method. I don't think you need to "and" the value either.

    The main problem you're having is not keeping floating point numbers and integer numbers separate. One has to be converted to the other before you can do math with them.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 13:52
    Duane Degn wrote: »
    The Prop chops off everything after the decimal point. So while 5/255 equals 0.0196 on a calculator, with the Prop, it equals 0.

    If you use 50,000 / 255 you'll get 196 with the Prop. This should be a usable number.

    You know 196 is really 0.0196 and you keep this in mind when you write the rest of your program.

    If 196 was stored in the variable "value", you could display it with a decimal point using the method (linked to earlier) "DecPoint" by calling it like this:
    DecPoint(value, 10000)
    

    You'd use the number "10000" since that what you multiplied "5" by to get "50000".

    There's more discussion about pseudo real numbers in that thread.

    Floating point numbers are stored differently in the Propeller's memory than the way normal integers are stored. You can't mix the two with math functions. You also can't use normal Spin math and comparisons with floating point numbers (if you want the answers to be meaningful).

    Edit: codeviper's calculator is a good example of using floating point numbers. He made the common mistake of mixing integer math operators with floating point operators in his expoent function "^". The rest of the functions work correctly.

    Thanks this takes out a lot of stuff that i do not understand how to use and it works great! I have not figured out how to get a decimal point yet but i am still working on it.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 13:59
    value &= 255
    

    This is awesome!!
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 14:05
    Tony_tsi wrote: »
    value &= 255
    

    This is awesome!!

    I don't think you need it though.

    I don't have an LCD setup nor do I have the LCD object you're using (in the right place for the Prop Tool to find it) so I couldn't even compile the code below.

    I think the "Char" method is a private method in the LCD object you're using. You need to make it public by changing PRI to PUB.

    Here's the code:
    CON
      _clkmode = xtal1 + pll16x                                              ' use crystal x 16
      _xinfreq = 5_000_000                                                   ' 5 MHz cyrstal (sys clock = 80 MHz)
      adcCS_Pin = 13                                                         'pins used for ADC chip
      adcCLK_Pin = 15
      adcDO_Pin = 14
      INIT_OK = 0   
    OBJ
      lcd     : "LCD_16X2_8BIT" 
      ADC     : "ADC_Driver"
    VAR
      byte ADC_value  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>adc value in dec can range from 1 to 255
      long volt_calc  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>adc value multiplied by volt_step (volt step should = 5/255)
      long volt_step  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>should be equal to 5/255  this number repersents the voltage per 1 step of adc 
           
    PUB ini
      
      lcd.start                                                                     'start junk
      lcd.move(1,1)                                     
      lcd.str(string("kWH PROJECT"))
      lcd.move(1,2)
      lcd.str(string("WKU"))
      waitcnt(clkfreq*2 + cnt)
      volt_step := 50000 / 255
        
      main  
    pub main | rcLCD, rcADC
       
      lcd.clear                                                                     
      lcd.move(1,1)              
      lcd.str(string("volts"))
      rcADC := ADC.Init(adcCS_Pin, adcCLK_Pin, adcDO_Pin)
        
      if rcADC <> INIT_OK
        LCD.Str(string("ADC chip init err"))
        return
      repeat
        lcd.str(string("     "))                                
        LCD.move(10, 1)                                            
        ADC_value := ADC.GetConvertedVal                                            'get value from ADC (binary)
        volt_Calc := ADC_value * volt_step
        DecPoint(volt_Calc, 100000)                                     
        waitcnt(clkfreq/2 + cnt)                                                    'wait for 500ms
    PUB DecPoint(value, denominator) 
      if value < 0
        LCD.CHAR("-")
        -value
          
      if value => denominator
        result := value / denominator
        LCD.dec(result)
        value //= denominator     
      else    
        LCD.CHAR("0")
      LCD.CHAR(".")  
      repeat while denominator > 1
        denominator /= 10
        if value => denominator
          result := value / denominator
          LCD.dec(result)
          value //= denominator
        else
          LCD.CHAR("0")
    

    Let me know if if works.

    Edit: I see some of the comments wrapped around. You'll need to fix that. I'm editing this post to attach the Spin file. The file should now be attached. If the code doesn't work, use File\Achive\Project to zip all the objects together and post the zip file. This way I can be sure I'm using the same objects you're using.

    Edit (July 12, 2012): I fixed a bug the DecPoint method above. I did not fix the bug in the attached version of the code. Thank you Gunstar1 for pointing it out.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 14:22
    This one works. I also attached the objects needed to run it.
  • Tony_tsiTony_tsi Posts: 98
    edited 2012-04-09 14:26
    Duane Degn wrote: »
    I don't think you need it though.

    I don't have an LCD setup nor do I have the LCD object you're using (in the right place for the Prop Tool to find it) so I couldn't even compile the code below.

    I think the "Char" method is a private method in the LCD object you're using. You need to make it public by changing PRI to PUB.

    Here's the code:
    CON
      _clkmode = xtal1 + pll16x                                              ' use crystal x 16
      _xinfreq = 5_000_000                                                   ' 5 MHz cyrstal (sys clock = 80 MHz)
      adcCS_Pin = 13                                                         'pins used for ADC chip
      adcCLK_Pin = 15
      adcDO_Pin = 14
      INIT_OK = 0   
    OBJ
      lcd     : "LCD_16X2_8BIT" 
      ADC     : "ADC_Driver"
    VAR
      byte ADC_value  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>adc value in dec can range from 1 to 255
      long volt_calc  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>adc value multiplied by volt_step (volt step should = 5/255)
      long volt_step  '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>should be equal to 5/255  this number repersents the voltage per 1 step of adc 
           
    PUB ini
      
      lcd.start                                                                     'start junk
      lcd.move(1,1)                                     
      lcd.str(string("kWH PROJECT"))
      lcd.move(1,2)
      lcd.str(string("WKU"))
      waitcnt(clkfreq*2 + cnt)
      volt_step := 50000 / 255
        
      main  
    pub main | rcLCD, rcADC
       
      lcd.clear                                                                     
      lcd.move(1,1)              
      lcd.str(string("volts"))
      rcADC := ADC.Init(adcCS_Pin, adcCLK_Pin, adcDO_Pin)
        
      if rcADC <> INIT_OK
        LCD.Str(string("ADC chip init err"))
        return
      repeat
        lcd.str(string("     "))                                
        LCD.move(10, 1)                                            
        ADC_value := ADC.GetConvertedVal                                            'get value from ADC (binary)
        volt_Calc := ADC_value * volt_step
        DecPoint(volt_Calc, 100000)                                     
        waitcnt(clkfreq/2 + cnt)                                                    'wait for 500ms
    PUB DecPoint(value, denominator) 
      if value < 0
        LCD.CHAR("-")
        -value
          
      if value > denominator
        result := value / denominator
        LCD.dec(result)
        value //= denominator     
      else    
        LCD.CHAR("0")
      LCD.CHAR(".")  
      repeat while denominator > 1
        denominator /= 10
        if value > denominator
          result := value / denominator
          LCD.dec(result)
          value //= denominator
        else
          LCD.CHAR("0")
    

    Let me know if if works.

    Edit: I see some of the comments wrapped around. You'll need to fix that. I'm editing this post to attach the Spin file. The file should now be attached. If the code doesn't work, use File\Achive\Project to zip all the objects together and post the zip file. This way I can be sure I'm using the same objects you're using.



    This code displays 7 where it should display 5.
Sign In or Register to comment.