Shop OBEX P1 Docs P2 Docs Learn Events
Need to display a float < 1 to LCD, Help Needed, using SPIN — Parallax Forums

Need to display a float &lt; 1 to LCD, Help Needed, using SPIN

DRMorrisonDRMorrison Posts: 81
edited 2017-09-15 00:43 in Propeller 1
I'm trying to display a decimal number on a serial LCD, but can't quite get the sequence. I have a stepper that moves 2.5 microns per step,
and would like this displayed on the LCD during the stepping. I have something that works, but it seems like a kludge to me.
The value always starts at zero, and then increases by the 2.5 microns (0.0025) each step. I'm having a time figuring a way to display the
fractional decimal number on the LCD as it increases each step. I've multiplied the number of steps by 25, and then using a the case command,
pad the resultant value with zeros, like this...
 if Zcnt =< 799
  Zcnt += 1
  case Zcnt
   0..7 :
    tx(148)
    str(string("-0.00"))
   8..79 :
    tx(148)
    str(string("-0.0"))
   80..799 :
    tx(148)
    str(string("-0."))
  Disp
Disp is a sub that prints the the decimal value to the LCD just following the text printed above. If the count gets to 800, the number becomes > 1, and another routine
prints "-1.xxx", where the xxx is the fractional part determined by the steps.
I've looked into F32 and StringtoChar, but I just can't figure how to use them. I've looked for samples, all I need is one, and then I could figure
how to get this done. I'll keep trying to figure this out on my own, but any and all help will be greatly appreciated.
Daniel

Comments

  • your case command is really not the right way to do this

    Taking your steps and multiplying by 25 is correct. now you have your microns in a 32bit integer long. say Zcnt

    What you need to do is to first separate the whole part from the fractional part

    so take another variable 'Zcnt2' to help you
    we still need the case, but just one time
    Zcnt2 := Zcnt/1000 'Zcnt2 now contains just everything left of your decimal point. divided by 1000 and truncated, is just a long integer
    Tx(148)
    Tx("-")
    Dec(Zcnt2) 
    Tx(".")
    Zcnt2 := Zcnt - (Zcnt2 *1000) ' Zcnt2 now contains just the fractural part
    case Zcnt2
            0..9:
                  Tx("0")
                  Tx("0")
            10..99:
                  Tx("0")
            other: 
    
    Dec(Zcnt2) 
    
    and done!
    
    ' out of fullduplexserial
    PUB Dec(value) | i, x
    {{
       Transmit the ASCII string equivalent of a decimal value
    
       Parameters: dec = the numeric value to be transmitted
       return:     none
    
       example usage: serial.Dec(-1_234_567_890)
    
       expected outcome of example usage call: Will print the string "-1234567890" to a listening terminal.
    }}
    
      x := value == NEGX                                    'Check for max negative
      if value < 0
        value := ||(value+x)                                'If negative, make positive; adjust for max negative
        Tx("-")                                             'and output sign
    
      i := 1_000_000_000                                    'Initialize divisor
    
      repeat 10                                             'Loop for 10 digits
        if value => i                                                               
          Tx(value / i + "0" + x*(i == 1))                  'If non-zero digit, output digit; adjust for max negative
          value //= i                                       'and digit from value
          result~~                                          'flag non-zero found
        elseif result or i == 1
          Tx("0")                                           'If zero digit (or only digit) output it
        i /= 10                                             'Update divisor
    
    

    Enjoy!

    Mike
  • Mike,
    That sort of works, but 20 iterations, or steps in my case, should result in a value of .05. But this ends up giving me .5--off by a factor of 10.
    I've been trying to do this with Float32 and FloatString, but can't get the string to print to the LCD. This is a small test app. I think that I'm not
    getting something correct with the string pointer of the returned value from FloatString obj.
    CON zSTEP = 0.0025
    VAR long Zcnt, zStep
    Zcnt := 0.0
    
    repeat 20
      waitcnt(clkfreq/10 + cnt)
      Zcnt += FltMth.FAdd(Zcnt, zSTEP)
      tx(148)
      FltStr.FloatToString(Zcnt)
      str(string(@Zcnt))
    
  • I can't see the problem. You take the number of steps, multiply by 25 and then when you print the 32-bit integer you simply scale to 4 decimal places. Isn't that correct? Of course you can trim trailing zeros easily enough too.

    Here is a quick test in Tachyon:
    ..  pub .mm ( steps -- )         25 * 0 4 .DP PRINT" mm"  ; 
    ..  20 .mm 0.0500mm ok.  
    

    and again:
    ..  5 BY 10 FOR CR I . TAB I .mm NEXT 
    0       0.0000mm
    5       0.0125mm
    10      0.0250mm
    15      0.0375mm
    20      0.0500mm
    25      0.0625mm
    30      0.0750mm
    35      0.0875mm
    40      0.1000mm
    45      0.1125mm ok
    
  • msrobotsmsrobots Posts: 3,704
    edited 2017-09-15 04:58
    OK I did just 3 places right of the decimal not four
    Zcnt2 := Zcnt/10_000 'Zcnt2 now contains just everything left of your decimal point. divided by 10000 and truncated, is just a long integer
    Tx(148)
    Tx("-")
    Dec(Zcnt2) 
    Tx(".")
    Zcnt2 := Zcnt - (Zcnt2 *10_000) ' Zcnt2 now contains just the fractural part
    case Zcnt2
            0..9:
                  Tx("0")
                  Tx("0")
                  Tx("0")
            10..99:
                  Tx("0")
                  Tx("0")
            100..999:
                  Tx("0")
            other: 
    
    Dec(Zcnt2) 
    

    and done!
  • Sorry Peter, I don't know what Tachyon is. I barely know Spin. It looks compact, though.

    mike, this works!

    Is there a way to do this using the Float32 and the FloatString objects?
    Just curious.

    Daniel
  • I think you need to do
    CON zSTEP = Float(0.0025)
    VAR long Zcnt, zStep
    Zcnt := Float(0.0)
    
    ...
    
  • The reason I show you how it's done in Tachyon is that I can, and quickly check the results too. However the principle is the same.

    One thing I notice is that you trim trailing zeros but on LCD displays sometimes it's better to have fixed length fields for numbers otherwise the reading could jump about. It's easy enough to do this in Spin with just a few lines without complicating it with Floating point though.
  • This is being used on a precision diamond saw that I've built, and I need to convert the steps to an value that I can see on the LCD.
    Each step is 0.0025mm, so after 200 steps the display will show -0.5000. 400 steps is 1mm. This is the reading of the
    z-axis, which moves down, hence the neg. number. It always starts from zero at the beginning of a cut cycle. The saw will cut
    no more than 1-2 mm in depth.
    I Would like to have this read an encoder and display the position like a DRO, but for now this will work. As long as I keep count
    of the steps, I can display the value (position) on the LCD.
    Now I need to get this to work in the other direction.

    Thanks for your help, everyone. Daniel
  • This code should do the job with alignment like Peter suggests.
    con
      _clkmode = xtal1 + pll16x
      _clkfreq = 80_000_000
    
    obj
    comms:  "fullduplexserial"
    
    pub main|x,total,q
    
        comms.start(31,30,0,115200)
        
        repeat while ina[31]
        
        repeat x from -5 to 5
        
          total := x * 25
          comms.dec(x)
          comms.tx(9)
    
          if total < 0
             comms.tx("-")
          else
             comms.tx("+")
               
          q := || total / 10_000    'leftside
          pad(q,2," ")
          comms.tx(".")
    
          q := || total // 10_000   'right side
          pad(q,4,"0")
          comms.str(string(" mm"))
          comms.tx(13)
    
    pri pad(value,digits,char)
    
      if digits > 3 and value < 1_000
         comms.tx(char)
      if digits > 2 and value < 100
         comms.tx(char)
      if digits > 1 and value < 10
         comms.tx(char)
      comms.dec(value)   
    
    Output sample
    -5      - 0.0125 mm
    -4      - 0.0100 mm
    -3      - 0.0075 mm
    -2      - 0.0050 mm
    -1      - 0.0025 mm
    0       + 0.0000 mm
    1       + 0.0025 mm
    2       + 0.0050 mm
    3       + 0.0075 mm
    4       + 0.0100 mm
    5       + 0.0125 mm
    


  • ozpropdev, I like this a lot. Works great and allows me to go in both directions. This is being used as a quasi-DRO on an axis
    that is moved with a stepper motor. I've tweeked things around a little to fit into my current program.
    Thanks a million!
    Daniel
  • Here's a simple frequency counter that uses a float to change frequency to a different scale factor. (Just a float to a string example)
  • I could not make out how that code converted the float to string and then printed it. If someone could give a simple
    step-by-step of one example, I could then use that in my code. I've never used floats up to this point, so I'm not sure
    of the proper sytax. And converting and then printing is beyond me at this point.
    1. create necessary variables
    2. do math on variables
    3. convert variables to string
    4. print string to LCD

    Daniel
  • You can use the spin file I posted. Substitute X1A:=Frequency with X1A:=55 or 333 or any number. You will see the results on the LCD after FloatHz..
    It will multiply X1A by the G constant up in CON 2.1. Or change it to 1.0 or 4.4 etc. don't forget the decimal or it doesn't work.
    I don't know your skill level maybe your a pro, semipro, beginner, etc. Your going to need the FloatMath and Floatstring object's for it to work.
  • If you have a two line LCD you'll have to tweek the con to say 2 lines and change the Y line number in the display.
  • Here is another perhaps simpler example that may help.
    {
      Float Values Display Demo
    
      The code below uses the FloatToString routine from the "FloatString" library.
    
      NOTE: The FloatToString routine only deals with one float-to-string conversion at a time.
            This means that string pointer that is returned by the routine will always point to
            the last converted float value, so you should display/use the string immediately before
            converting a new float value.
    }
    
    
    CON
      _clkmode = xtal1 + pll16x                     'Standard clock mode * crystal frequency = 80 MHz
      _xinfreq = 5_000_000
    
      numPI = 3.14159                               'Float constant.
      num2PI = float(2) * numPI                     'Float constant.
    
       
    VAR
      long X, Y, Z
    
       
    OBJ
     PST :  "Parallax Serial Terminal"              'Serial Terminal
       F :  "FloatMath"                             'Float math library.
      FS :  "FloatString"                           'Float string library.
    
     
    PUB Main
    
      pst.Start(115_200)                            'Start Serial Terminal routine.
      repeat
        repeat X from 1 to 9 
          waitcnt( 80_000_000 + cnt )               'Wait for 1 second.
          Y := F.FFloat(X)                          'Get float(X) value.
          Z := F.FMul(numPI, Y)                     'Multiply two float values.
          
          PST.str(FS.FloatToString(numPI))          'Display string of float value.
          PST.str(string(" * "))
          PST.dec(X)
          PST.str(string(" = "))
          PST.str(FS.FloatToString(Z))              'Display string of float value.
          PST.str(string(13))
    

    Sample output:
    3.14159 * 1 = 3.14159
    3.14159 * 2 = 6.28318
    3.14159 * 3 = 9.424769
    3.14159 * 4 = 12.56636
    3.14159 * 5 = 15.70795
    3.14159 * 6 = 18.84954
    3.14159 * 7 = 21.99113
    3.14159 * 8 = 25.13272
    3.14159 * 9 = 28.27431
    
  • Francis, that is what I needed. I can follow that one example, and should be able to make that work. I'm using
    the serial 4-line LCD from Parallax, but that shouldn't matter.
    Will using Float32 work in place of FloatMath?

    Daniel
  • DRMorrisonDRMorrison Posts: 81
    edited 2017-09-17 02:19
    I've tried the above code, but I can't get this line to work:

    str(string(FS.FloatToString(numPI))) 'Display string of float value

    The str(string( ) is used by the Parallax serial LCD to print to
    the LCD, and works great when printing text like this:
    str(string("Hello")) But the above does not work.
    All of the values are coming back from FloatMath as 0!
    Daniel
  • DRMorrison wrote: »
    I've tried the above code, but I can't get this line to work:

    str(string(FS.FloatToString(numPI))) 'Display string of float value

    The str(string( ) is used by the Parallax serial LCD to print to
    the LCD, and works great when printing text like this:
    str(string("Hello")) But the above does not work.
    All of the values are coming back from FloatMath as 0!
    Daniel

    Remove 'string()', you don't need to use that since the FloatToString routine already returns a zero delimited string pointer. The statement should look like:
    str(FS.FloatToString(numPI))
    

    You only need to use the 'string()' routine when you need to create/define a zero delimited string.
  • DRMorrisonDRMorrison Posts: 81
    edited 2017-09-17 05:45
    If I run the following code with the line in question commented out, the LCD shows " * 0 = 0" But if I include the line the
    LCD displays nothing. I've put lines of code to look at the numbers by printing them at the bottom of the LCD, and they
    always come out as zeros.
    Daniel
    CON      {{Testing LCD}}
    
     _clkmode = xtal1 + pll16x   '80 MHz clk
     _xinfreq = 5_000_000
     Baud = 19_200               'baud rate for LCD comm.
     LCD = 0                 'LCD comm pin
     numPI = 3.14159                               'Float constant.
    
    VAR
     long xx, yy, zz
    
    OBJ
     F :  "FloatMath"                             'Float math library.
     F32: "Float32"
     FS : "FloatString"                           'Float string library.
    
    PUB Main
    
     ' ----------------- lcd set up-----------------
     dira[LCD] ~~
     outa[LCD] ~~
     F32.start
     waitcnt(clkfreq/5 + cnt)
     tx(22)              'LCD ON, cursor OFF & NO Blink
     tx(12)              'Form Feed
    
     yy := F.FFloat(xx)                       
     zz := F.FMul(numPI, yy)                  
     tx(128)
     'str(FS.FloatToString(numPI))          
     str(string(" * "))
     dec(xx)
     str(string(" = "))
     str(FS.FloatToString(zz))
    
    Also, I'm including a simple LCD driver in the file, it's not shown here. It includes 4 methods: Dec(Value), DecF(value,divider,places), str(stringptr) and Tx(Tx_byte)
  • I still don't understand why you're even messing with floats.
    If you're keeping track of the number of steps already, then just use that as you did earlier in the thread.

    If it's the method I'm thinking of, using DecF(Zcnt * -25,20000,5) shows -1.00000 when Zcnt is 800.
  • I want to use this in another project, and have tried in the past to get it to work, but have never been
    able to do so. It seems that everyone else can get it to work, so I figure I too should be able to make
    it work. I'm kind of stubborn that way.
    Daniel
  • DRMorrisonDRMorrison Posts: 81
    edited 2017-09-17 21:53
    ChrisGadd, I am using the DecF() because it is a cleaner solution.
    I would still like to know how to use the FloatString object to
    print to the LCD.
    This is what I ended up using. Very elegant. Works in both directions, and have added an Up & Down
    button to move the stepper, and the DRO reads correctly.
    PRI Disp
    tx(148)   
      if Zcnt => 0
       tx("+")  
      DecF((Zcnt*25), 10000, 4)
      str(string(" mm"))
    

    Thanks to everyone for your help, Daniel
Sign In or Register to comment.