Shop OBEX P1 Docs P2 Docs Learn Events
Problem with string manipulation — Parallax Forums

Problem with string manipulation

kolyurkolyur Posts: 43
edited 2011-12-20 20:29 in Propeller 1
I'm trying to write a function that will take a signed integer (maximum 5 digits) and convert it to a string of the form XX.XXX with optional sign display. This string will then be displayed on a VFD. I know my VFD implementation is correct because if I use the Numbers or Simple_Numbers object to create the string, it displays correctly. However, the following code seems like it should work but it just shows garbled characters on the VFD.
PUB PosToString(value,signdisp) | idx, divisor

  bytefill(stringvar, " ", 8)                  'Clear array
  stringvar[7] := 0                            'Add zero terminator
  
  if signdisp == 2 AND value => 0              'Determine sign character
    stringvar[0] := "+"
  elseif signdisp > 0 AND value < 0
    stringvar[0] := "-"
  else
    stringvar[0] := " "
  
  value := ||(-99_999 #> value <#= 99_999)     'Constrain value to 5 digits, remove sign
  
  divisor := 10_000
  repeat idx from 1 to 5
    stringvar[idx] := value / divisor + "0"
    value //= divisor
    divisor /= 10

  repeat idx from 5 to 3                       'Shift digits and add decimal point
    stringvar[idx+1] := stringvar[idx]
  stringvar[3] := "."

  if stringvar[1] == "0"                       'Remove leading zero
    stringvar[1] := " "

stringvar is a global array with 8 elements. Can anyone see any glaring errors? I compared it to the string object in Simple_Numbers and the procedure seems very similar. One thing that's still tripping me up with Spin is knowing when I need to use the pointer @ symbol in front of a variable. I'm using @stringvar[0] as the argument in my VFD send string function, as I want to specify the address of the first character (I think). I'd appreciate any suggestions!

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2011-12-20 05:20
    This works for me. How do you call (and display) it when it goes wrong?
  • kolyurkolyur Posts: 43
    edited 2011-12-20 05:44
    The VFD is asynchronous serial. My VFD object uses the "tx" method of the Full Duplex Serial object as so:
    PUB strsend(stringptr)
      repeat strsize(stringptr)
        ser.tx(byte[stringptr++])
    

    Strsend is called with argument @stringvar[0] as I mentioned. I can display strings on the VFD all day long using this method (including those generated by Numbers.spin), but my PosToString method just shows garbage characters. Unfortunately I don't remember exactly what characters are shown and I don't have access to the controller at the moment. I'll fire it up tonight and see if I can figure out what it's showing. Thanks for your help.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-12-20 06:01
    You need to use an @ in the first statement of the method. The line should be bytefill(@stringvar, " ", 8). "stringvar" is the name of the array. The address of the array is "@stringvar". bytefill requires the address of the buffer, and not the name of the buffer. A variable name used by itself gives the same result as adding a zero index on it. That is, stringvar has the same value as stringvar[0]. @stringvar is the same address as @stringvar[0].
  • kuronekokuroneko Posts: 3,623
    edited 2011-12-20 06:04
    Dave Hein wrote: »
    You need to use an @ in the first statement of the method. The line should be bytefill(@stringvar, " ", 8).
    Well spotted! This (as it is) usually works once and then never again ...
  • kolyurkolyur Posts: 43
    edited 2011-12-20 08:08
    Dave Hein wrote: »
    You need to use an @ in the first statement of the method. The line should be bytefill(@stringvar, " ", 8). "stringvar" is the name of the array. The address of the array is "@stringvar". bytefill requires the address of the buffer, and not the name of the buffer. A variable name used by itself gives the same result as adding a zero index on it. That is, stringvar has the same value as stringvar[0]. @stringvar is the same address as @stringvar[0].
    Thanks for the clarification. I think I might have already made the bytefill correction you mentioned in one of my many attempts to get this to work.

    If memory serves me correct, the output I am getting on the VFD with this code is a single ASCII character $9D (at least, that's what it looks like in my ASCII chart) and the rest spaces, regardless of the input value. So I admit that "garbage characters" plural was a bit of an exaggeration--there's only one.

    I will get more information tonight and post back.
  • JonnyMacJonnyMac Posts: 9,198
    edited 2011-12-20 09:01
    As a "let's write a bit of code while the coffee kicks in" exercise I wrote this variant. Tests with PST show that it works. This version wants you to pass a pointer to the string buffer (array of bytes) that you will ultimately copy to your VFD.
    pub val2display(val, showsign, spntr) | divisor, idx
    
      ' show sign if value is negative, or positive and showsign is true
    
      if (val < 0)
        byte[spntr++] := "-"                                        ' always show negative sign
      else
        if (showsign)                                               ' selective display for positives
          byte[spntr++] := "+"
        else
          byte[spntr++] := " " 
    
      val := ||(-99_999 #> val <# 99_999)                           ' constrain to 5 digits
    
      divisor := 10_000                                             ' set intial divisor
      
      repeat idx from 0 to 5                                        ' 5 digits plus decimal
        if (idx == 2)                                               ' if decimal position
          byte[spntr++] := "."                                      '  put .
        else                                                        ' else
          result := val / divisor                                   '  extract digit from val
          if ((idx == 0) and (result == 0))                         '  check for leading zero 
            byte[spntr++] := " "                                    '   no lz
          else
            byte[spntr++] := result + "0"                           '   print digit
          val //= divisor                                           '  update val
          divisor /= 10                                             '  update divisor
    
      byte[spntr] := 0                                              ' terminate string
    


    It does the same thing you're doing, just in a single loop. Another way to skin the cat as it were....

    You can move a value to the buffer like this:
    val2display(someVar, true, @buffer)
    


    ... where buffer is a byte array big enough to hold the string (plus one for the trailing zero). After that, you should be able to use the .str() method in FDS:
    vfd.str(@buffer)
    
  • kolyurkolyur Posts: 43
    edited 2011-12-20 09:43
    After reading your initial reply my only question was about how to reserve room for the resultant string. I see you answered it in your edit before I even had a chance to ask. Nice!

    Thanks for the code, I can't wait to try it out.
  • JonnyMacJonnyMac Posts: 9,198
    edited 2011-12-20 09:54
    Yes, when one refers to a "string" in the Propeller world, what one really means is a byte array that is large enough to hold all desired characters, plus one for the trailing zero (that is used by the .str method).

    By modifying your method to accept a string pointer, you have a bit more flexibility: you could, for example, prepare two strings (requires two buffers) and via your HMI switch between them without having to re-run the conversion method.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-20 10:50
    Awhile back, I rewrote Simple_Numbers as Simple_Numbers_plus (attached). It does two things that might be useful to the OP:
    1. It includes a decf method that converts an integer to a fixed-point string.

    2. It uses a FIFO buffer to retain multiple string results at once.

    I haven't submitted it to the OBEX, though, because there is still one thing I want to modify. The "fixed-width" decimal-to-string routines are not really fixed-width, since they add a character position for negative numbers to accommodate the sign. This causes column misalignment in mixed-sign displays. I'd like to change that behavior, but haven't gotten around to it yet.

    -Phil
  • kolyurkolyur Posts: 43
    edited 2011-12-20 20:29
    Update: JonnyMac's code worked great, as did mine once I made some corrections to the pointer usage. I was so convinced that the error was related to how I was generating the string, that I didn't examine how I was actually passing it to the VFD object. Thanks for everyone's help... I now have a much better understanding of the function of @ (and what can happen when you leave it out).
Sign In or Register to comment.