Shop OBEX P1 Docs P2 Docs Learn Events
Need some help understanding a counter — Parallax Forums

Need some help understanding a counter

Don MDon M Posts: 1,653
edited 2011-06-21 20:22 in Propeller 1
I have a variable named RevCount. I am able to call it and display it on my screen ok. When it counts the revolutions it increments by one just fine. The shaft revolution counter counts each turn of a shaft. Every shaft revolution moves a table by .007030371196429 inches.

1. My question is if I want it to increment my count display by that number with each revolution count how do I go about doing that?

2. I would like to have it as precise as possible and understand that the number above has quite a few decimal positions. How big a number can I use in the equations? I want the display resolution to be 4 decimal places.

I have kludged together some bits of code from the OBEX along with some of my own creation so please excuse my mess.

Here is some of the main code:
con


  _clkmode = xtal1 + pll16x         ' enable external clock and pll times 16
  _xinfreq = 5_000_000              ' set frequency to 5 MHZ


Encoder1pin   =  16                  ' shaft encoder - Propeller pin

  F1 = 1.0                          ' floating point 1
  F60000 = 60_000.0                 ' floating point 60,000 

obj

  kp   : "Keypad_encoder"                                       ' keypad driver object P0 - P7
  'term : "FullDuplexSerial"                                     ' for serial input & output monitoring
  'vfd  : "vfd_2x20_4bit_don"
  'vfd  : "LCD_16x2_4Bit_don"
  pst   : "Parallax Serial Terminal"
  
  mm    : "Motor_Minder"
  'lcd   : "serial_lcd"
  num   : "simple_numbers"
  F     : "FloatMath"
  FS    : "FloatString"
  

var

  long  period, revCount             ' motor period and revCount updated by Motor_Minder.spin
  long  Fwidth, Frpm                 ' floating point variables for display
  

pub main | Pressed_Key

   pst.start(115_200)                                            ' start terminal (use PST)
   pause(200)
   'vfd.start
   kp.start(4, 4, 0, 4, @table)                                  ' start keypad driver

   mm.start(Encoder1pin, @period, @revCount)
    
     
   
   'vfd.clear                                                    ' clear vfd screen
   'vfd.out($00) 
   pst.str(string("Table Controller", 13))
   pst.str(string("Version 1.xx"))
   'vfd.str(string("Position Controller"))
   pause(2000)
   'vfd.pos(0,2)
   pst.clear
   pst.position(0,0)
   pst.str(string("Position: "))
   pst.position(0,1)
   pst.str(string("Press PROG for Menu"))
   pause(1000)
   'vfd.pos(0,0)
   'vfd.str(string("Key pressed:        "))

   'repeat 7
   '  vfd.out($08)
   

   repeat

     Pressed_Key := kp.getkey 
     if Pressed_Key > 0
       pst.char(Pressed_Key)
       'vfd.tx(Pressed_Key)
       'vfd.out(Pressed_Key)
       'vfd.out($08)
       'vfd.out($08)
       pst.str(string(13))
     if Pressed_Key == "R"
       pst.str(string("PROG button was pressed", 13))
     if Pressed_Key == "S"
       pst.str(string("START button was pressed", 13))
     if Pressed_Key == "E"
       pst.str(string("SET button was pressed", 13))
     if Pressed_Key == "X"
       pst.str(string("STOP button was pressed", 13))

  
     pst.position(10,0)
      
     FS.setPrecision(8)
    
     Fwidth := F.FFloat(period)
     Fwidth := F.FDiv(F1, Fwidth)

     'Frpm := F.FMul(Fwidth, F60000)

     'lcd.str(FS.FloatToString(Frpm))
     'lcd.str(string(" RPM     "))
     'revCount := revCount + 0070 
     'lcd.gotoxy(0,1)
     pst.str(num.dec(revCount))      
   


pri Pause(Duration)

  waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
  return

     
dat     ' keypad translation table

table   byte  "1", "2", "3", "S"
        byte  "4", "5", "6", "R"
        byte  "7", "8", "9", "E"
        byte  "N", "0", "P", "X"

' Keypad layout
'
' 1  2  3  Start
' 4  5  6  Prog
' 7  8  9  Set
' -  0  +  Stop

Comments

  • kwinnkwinn Posts: 8,697
    edited 2011-06-20 19:48
    The problem with adding .007030371196429 to a "total" is that you loose some precision with every add, and as the "total" gets larger so does the loss of precision. It would be better to accumulate the shaft rotation count, convert it to floating point, and then multiply it by .007030371196429.
  • Don MDon M Posts: 1,653
    edited 2011-06-21 04:52
    kwinn- thanks for that. That seems like a very logical answer. Now to look up on converting numbers to floating point....
  • schillschill Posts: 741
    edited 2011-06-21 05:17
    Do you actually need the number to be floating point? If you keep all the math as integers it will be a faster and smaller program.

    If you only need the result in inches for the display, you can either display it in 10,000ths (equivalent of 4 decimal places) of an inch with no decimal or add the decimal in when you are displaying it.
  • prof_brainoprof_braino Posts: 4,313
    edited 2011-06-21 05:54
    I second shill, keep it integer internally and only go to float when you absolutely need to, at display time. Life is easier.
  • Don MDon M Posts: 1,653
    edited 2011-06-21 09:32
    Ok so I am changing gears a bit here to try and understand this concept.

    I have downloaded Jonny Macs 2 bit gray code demo. Here is a portion of his code for displaying the number:
    pub main | newlevel, oldlevel
    
      term.start(31, 30, %0000, 115_200)                            ' start terminal for test
      pause(2000)
      term.tx(CLS)
    
    '  level.init(0, false, 0, 100, 50)                              ' non-detent encoder on p0/p1
    '  level.init(0, true, 0, 100, 50)                               ' detented encoder on p0/p1
      level.init(16, false, 0, 1000, 50)                             ' non-detent encoder on p16/p17
    '  level.init(16, true, 0, 1000, 50)                              ' detented encoder on p16/p17
    
    
      pause(1)
      newlevel := level.read                                        ' read initial value
      
      repeat
        term.tx(HOME)                                               ' display it
        term.str(string("Encoder: "))
        term.dec(newlevel)
        term.tx(CLREOL)
    
        oldlevel := newlevel                                        ' setup to detect change
        repeat
          newlevel := level.read                                    ' poll encoder                                   
        until (newlevel <> oldlevel)                                '  until it changes 
    

    It works very well and displays changes very smoothly and quickly. If I add a line of code to add a multiplication factor to the result before it is displayed it becomes very clunky in displaying the number. It comes in chucks (for lack of a better way to explain it).

    Here is an example of what I added:
     repeat
        term.tx(HOME)                                               ' display it
        term.str(string("Encoder: "))
        newlevel := newlevel * 35515                                ' This is what I added as a multiplier
        term.dec(newlevel)
    

    Can someone explain why this is happening? Is there a better way to do this type of thing?

    Thanks for your help.
    Don
  • kuronekokuroneko Posts: 3,623
    edited 2011-06-21 17:19
    repeat
        term.tx(HOME)                                               ' display it
        term.str(string("Encoder: "))
        term.dec(newlevel [COLOR="red"]* 35515[/COLOR])
    
    Try this instead. In your example you modify newlevel which subsequently affects the old/new loop (chances are that it always ends up not equal immediately). If that doesn't solve your problem you'd have to elaborate on the clunky chunk bit.
  • Don MDon M Posts: 1,653
    edited 2011-06-21 17:37
    I moved oldlevel := newlevel up further in the loop right under the repeat statement and that cured the "clunkiness".

    My only issue remaining here is how to apply the multiplier. The multiplier factor is .003515185598214 inches / pulse. I want the display to read in inches from 0 to 5.9999 which would be roughly 0 to 1707 pulses for the 6" max.

    Attached please find my latest code.
    con
    
      _clkmode = xtal1 + pll16x
    ' _xinfreq =  5_000_000
      _clkfreq = 80_000_000
    
      MS_001   = _clkfreq / 1_000
    
    
    con
    
      #1, HOME, #8, BKSP, TAB, LF, CLREOL, CLRDN, CR, #16, CLS      ' PST formmatting control
    
    '  0.003515185598214                                            ' multiplier for inches / pulse
    
    obj
    
      term  : "fullduplexserialdp"                                  ' for terminal output
      level : "jm_grayenc2"                                         ' 2-bit, graycode encoder 
      kp    : "Keypad_encoder"                                      ' keypad driver object P0 - P7
      vfd   : "vfd_2x20_4bit_don"                                   ' VFD driver
    
    
    var
    
    
    pub main | newlevel, oldlevel, Pressed_Key
    
      term.start(31, 30, %0000, 115_200)                            ' start terminal for test
      pause(2000)
      term.tx(CLS)
    
    
      level.init(16, false, 0, 60000, 0)                             ' non-detent encoder on p16/p17
    
      vfd.start
      kp.start(4, 4, 0, 4, @table)                                   ' start keypad driver
    
      pause(1)
      newlevel := level.read                                        ' read initial value
    
    '  vfd.clear                                                    ' clear vfd screen
      vfd.out($00) 
      term.str(string("Table Controller", 13))
      term.str(string("Version 1.xx"))
      vfd.str(string("Table Controller"))
      pause(2000)
      vfd.pos(0,2)
      term.tx(CLS)
      'term.position(0,0)
      'term.str(string("Position: "))
      'pst.position(0,1)
      term.tx(LF)
      term.str(string("Press PROG for Menu", 13))
      pause(1000)
    '  vfd.pos(0,0)
    '  vfd.str(string("Key pressed:        "))
    
    '  repeat 7
    '    vfd.out($08)
      
      repeat
    
        'Pressed_Key := kp.getkey 
         'if Pressed_Key > 0
           'term.tx(Pressed_Key)
           'vfd.tx(Pressed_Key)
           'vfd.out(Pressed_Key)
           'vfd.out($08)
           'vfd.out($08)
           'term.str(string(13))
         'if Pressed_Key == "R"
           'term.str(string("PROG button was pressed", 13))
         'if Pressed_Key == "S"
           'term.str(string("START button was pressed", 13))
         'if Pressed_Key == "E"
           'term.str(string("SET button was pressed", 13))
         'if Pressed_Key == "X"
           'term.str(string("STOP button was pressed", 13))
    
      
         'pst.position(10,0)
    
    
      
        oldlevel := newlevel                                        ' setup to detect change 
        term.tx(HOME)                                               ' display it
        term.str(string("Position: "))
        'newlevel := newlevel * 3515                                 ' This is what I added as a multiplier
        'newlevel := newlevel / 10000
        term.decdp(newlevel, 4)
        term.tx(CLREOL)
        
    
        repeat
          newlevel := level.read                                    ' poll encoder
          Pressed_Key := kp.getkey 
          if Pressed_Key > 0
            term.tx(Pressed_Key)
            term.str(string(13))
                                             
        until (newlevel <> oldlevel)                                '  until it changes  
    
    
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
        
    
    dat     ' keypad translation table
    
    table   byte  "1", "2", "3", "S"
            byte  "4", "5", "6", "R"
            byte  "7", "8", "9", "E"
            byte  "N", "0", "P", "X"
    
    ' Keypad layout
    '
    ' 1  2  3  Start
    ' 4  5  6  Prog
    ' 7  8  9  Set
    ' -  0  +  Stop 
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-06-21 17:58
    Don M wrote: »
    My only issue remaining here is how to apply the multiplier. The multiplier factor is .003515185598214 inches / pulse. I want the display to read in inches from 0 to 5.9999 which would be roughly 0 to 1707 pulses for the 6" max.
    OK, assuming 1707 is the max value being returned from the encoder (instead of 60000) then this sequence should do the trick:
    newlevel *= 3515             ' 1707*3515 = 6000105
    newlevel /= 100              ' 6000105/100 = 60001 (integer)
    term.decdp(newlevel, 4)      ' "6.0001"
    
  • Don MDon M Posts: 1,653
    edited 2011-06-21 19:15
    kuroneko- you're a genius! I think it works the way I need it to but need to do some further testing. It does display from 0 to 6.0001 inches. I just need to confirm that it is incrementing (the display) properly in step with the encoder input and incrementing the "inches" also properly. I shall let you know....

    What I am using is a separate prop board that "simulates" the gray code as from the machine. It has 2 buttons- one for count up and the other count down. What I'll do is slow the pulses way down to watch the count and verify the accuracy.
  • Don MDon M Posts: 1,653
    edited 2011-06-21 19:29
    kuroneko- Thanks again! It does work. I applied a set number of pulses and did the math and the display shows the correct value.

    I hope that someday I can provide someone with such valuable help as you have for me. I'm marking this as solved.

    Don
  • kwinnkwinn Posts: 8,697
    edited 2011-06-21 20:22
    Schill and prof_braino are absolutely right that this would be simpler, faster, and smaller if coded in integer and converted to decimal inches afterwards. I thought you had already written the code in floating point and suggested a simple change to avoid confusing the issue.

    To do this using integers is relatively simple. First choose a power of 10 multiplier for 0.007030371196429. Since we are doing 32 bit math the largest signed number we can deal with is 2,147,483,647 (unless you want to go to double precision). If we multiply everything by 10^8 the table travel would be 600,000,000 units (deci-micro inches?) and the travel per revolution (0.007030371196429 x 10^8 rounded to nearest int) is 703037.

    Add 703037 to a long for each turn of the shaft for distance travelled.

    Convert the long to a decimal and insert a decimal point to the left of the 8th digit and you are ready to display it.

    Accuracy:

    853 x .7030371196 = 5.996906630188
    853 x .7030371 = 5.996906463
Sign In or Register to comment.