Shop OBEX P1 Docs P2 Docs Learn Events
My Menu Object update speed is inconsistent — Parallax Forums

My Menu Object update speed is inconsistent

garyggaryg Posts: 420
edited 2016-02-14 05:34 in General Discussion
Hi
I wrote an Object in which a menu is a forever loop.
Pressing a button#4 advances the menu selection.
When the proper menu is displayed on the LCD I tell the program to go to the selected menu using button#1.

I have 2 issues that are somewhat troubling to me.
Issue #1:
On power up of the Prop-Mini the proper menu screen is displayed.
Pressing and releasing Button#4 advances the menu to the next selection 1, 2, or 3.
If I keep pressing and releasing Button#4 about 10 or so times, 1sec on 1 sec off,
LCD display does not respond until I wait about 5 or 10 seconds.
Issue #2:
I'm thinking that maybe I do not have my Return to the Menue at the end of each minor method written correctly.
The structure of " return (Main_Menue) " is the only thing I tried to get back to the main menue after exiting the methods.

I'll try to display my code here:

'Menu_Driven_Object_GG-2016.spin

{{ THIS WORKS!!
   BUT:
   When using BUTN4 to advance the menue selection more than about two iterations, the button
   response starts slowing down.             
   Something is taking longer per iteration of the menue selections on BUTN4.

  I believe I have an error in my structure that I cannot find.
  More than likely, It's something I don't understand.
}}

{{  This is a simple Menu selection Object.
    On microcontroller boot-up the LCD is set to the menu display.
    Pressing BUTN4 will advance the selections to different Menu selections.
    After the proper Menu item is selected and showing on the display.
    Pressing BUTN1 will send the code to the proper method 1, 2, or 3.

    After the method is completed.
    Pressing BUTN1 should take the program back to the menue selection.
}}


CON
' Set Processor Speed @ 80mhz
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

' Lcd Pin Assignment
  LCD_Pin = 18
' Global Pin Assignments for Pushbuttons
' Pushbuttons start at the bottom of the board.                           
  BUTN1 = 8
  BUTN2 = 9
  BUTN3 = 10
  BUTN4 = 11
   
VAR
 
  Long Flag1           'Flag for BUTN1
  Long Flag2           'Flag for BUTN2 
  Long Flag3           'Flag for BUTN3                       
  Long Flag4           'Flag for BUTN4                      
  Long MENUE            'Variable used to reach the proper menu selection, 1,2 or 3.

  Long mask          'Used to set up the Waitpne instruction in MainMenue method
   

OBJ
  ' FullDuplexSerial will be used to drive the Parallax 2 line LCD display
  
  LCD : "FullDuplexSerial"
                    
PUB  Main 

waitcnt(clkfreq * 3 + cnt)             ' 3sec Delay Before Starting   

  Flag1:=0           'Flag for BUTN1
  Flag2:=0           'Flag for BUTN2 
  Flag3:=0           'Flag for BUTN3                       
  Flag4:=0           'Flag for BUTN4         
  MENUE:=1           'Sets MENUE variable to 1 which is the lowest menu number for this situation.

     'Make BUTN1 through BUTN4 an input. 
dira[BUTN1] := 0
dira[BUTN2] := 0                              '
dira[BUTN3] := 0
dira[BUTN4] := 0

  mask := (|<BUTN1) | (|<BUTN4)  'OR the two button masks together into a single value.  Used in the MainMenue method.      

  LCD.start(LCD_Pin, LCD_Pin, %1000, 9_600)  'This is setting for LCD display.
  waitcnt(clkfreq / 10 + cnt)                ' Pause for 100ms to initialize the LCD display.  

MAIN_MENUE                                   ' Calls the next method
                                     
pub MAIN_MENUE

'Set up and display initial MENUE LCD view.
  LCD.tx(12)                            ' Clear the LCD display
  waitcnt (60_000 + cnt)                ' Pause for 5ms time for LCD display to clear
  
  LCD.str(string("MENU"))               ' line 0 printed to LCD                                      
  LCD.tx(13)                            ' Line feed
  LCD.str(string("BUTN4 = Select"))     ' line 1 printed to LCD
  LCD.tx(212)                           ' Set quarter note
  LCD.tx(220)                           ' A tone             

 repeat

            Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button BUTN1 is NOT pressed . 
            If INA[BUTN1]==0                       'BUTN1 must be released to enable BUTN1 again.
               Flag1:=0                            'Flag1 says that the BUTN1 has been released. 
            If INA[BUTN4]==0                       'BUTN4 must be released to enable BUTN4 again.
               Flag4:=0                            'Flag4 says that the BUTN4 has been released.
                            
            waitpne(  0 , mask , 0 )       'wait for either BUTN1 or BUTN4 to be set. mask is defined in the VAR section.      
 
         {{  waitpne(0,(|<BUTN1) | (|<BUTN4), 0)    'This waits until either butn1 or butn4 have been pressed before moving on.
                                                    'It appears that this statement is quite a bit slower than waitpne( 0, mask, 0)}}

            IF INA[BUTN4]==1 and Flag4==0

                   Flag4:=1

                   MENUE:= MENUE+1

                    IF MENUE>3
                       MENUE:=1         

       if Flag4==1   
          Case MENUE
            1 : '1 = Print to LCD, the 1st Menu 

                LCD.TX(12)                          'Clear the LCD screen.
                waitcnt(clkfreq / 200 + cnt)        'Wait 5ms for screen to clear.  This is 1 / 0.005 seconds  
                LCD.str(String("To 1st Menu?"))             
                                               
            2 : '2 = Print to LCD, the 2nd Menu
                                                        

                LCD.TX(12)                          'Clear the LCD screen.
                waitcnt(clkfreq / 200 + cnt)        'Wait 5ms for screen to clear.  This is 1 / 0.005 seconds  
                LCD.str(String("To 2nd Menu?"))      
                      
            3 : '3 = Print to LCD, the 3rd Menu

                LCD.TX(12)                          'Clear the LCD screen.
                waitcnt(clkfreq / 200 + cnt)        'Wait 5ms for screen to clear.  This is 1 / 0.005 seconds  
                LCD.str(String("To 3rd Menu?"))      

        Flag4:=2                                    'Flag4:=2 is used so the Case MENU does not keep repeating.
                                                    'This stops the MENUE from updating if BUTN4 is staying pressed.
   
       If INA[BUTN1]==1 and Flag1==0

                    Flag1:=1            

           Case MENUE
              1 : FirstMenue        'This is the method that drives the stepper motor to each gear position.

              2 : SecondMenue      'This allows you to jog the stepper motor to the correct position for derailure position
                                   'Then it saves the current StepCount to the Gear1 through Gear8 variables
              3 : ThirdMenue       'This needs to be written in order to save the Gear1 through Gear8 data to EEPROM 


pub FirstMenue

                LCD.TX(128)                         'Move cursor to Line0 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 0
                LCD.TX(128)                         'Move cursor to Line0 position 0  
                waitcnt(clkfreq / 200 + cnt)        'Wait 5ms for screen to clear.  This is 1 / 0.005 seconds
                LCD.str(String("In FirstMenue !!")) 'This proves I'm in the FirstMenue method.
                LCD.TX(148)                         'Move cursor to Line1 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 1                
                waitcnt(clkfreq / 200 + cnt)        'Wait 5ms for screen to clear.  This is 1 / 0.005 seconds
                LCD.TX(148)                         'Move cursor to Line1 position 0               
                LCD.str(String("Exit = BUTN1"))     'Instruction to leave this method

  Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button BUTN1 is NOT pressed .  
       '  Waitpeq (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button BUTN1 is pressed and held.  
 repeat
   If INA[BUTN1]==1
     return (MAIN_MENUE)
                      
pub SecondMenue                               

                LCD.TX(128)                         'Move cursor to Line0 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 0
                LCD.TX(128)                         'Move cursor to Line0 position 0  
                LCD.str(String("In SecondMenue !")) 'This proves I'm in the FirstMenue method.
                LCD.TX(148)                         'Move cursor to Line1 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 1                
                LCD.TX(148)                         'Move cursor to Line1 position 0               
                LCD.str(String("Exit = BUTN1"))     'Instruction to leave this method

  Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is NOT pressed .  
'  Waitpeq (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is pressed and held.  

 repeat
   If INA[BUTN1]==1
     return (MAIN_MENUE)       
     
                          
pub ThirdMenue

                LCD.TX(128)                         'Move cursor to Line0 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 0
                LCD.TX(128)                         'Move cursor to Line0 position 0  
                LCD.str(String("In ThirdMenue !!")) 'This proves I'm in the FirstMenue method.
                LCD.TX(148)                         'Move cursor to Line1 position 0
                LCD.str(String("                ")) 'Print 16 spaces to clear line 1                
                LCD.TX(148)                         'Move cursor to Line1 position 0               
                LCD.str(String("Exit = BUTN1"))     'Instruction to leave this method 

  Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is NOT pressed .  
'  Waitpeq (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is pressed and held.  

 repeat
   If INA[BUTN1]==1
     return (MAIN_MENUE)





The following should be the Archive file including the Full Duplex Serial that is needed.


Menu_Driven_Object_GG-2016%20-%20Archive%20.zip


When I'm using my Prop Mini and 2x16 Parallax LCD display set to 9600 baud, the Object starts working in
the manner I wanted.
I plan on using this in a larger project, but need to be sure it will work in a trouble free manner before attempting
to use it in my larger project.

Any words of wisdom, tips, would be greatly appreciated.

Garyg

Comments

  • You have this code in your menu functions:
      Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is NOT pressed
    
     repeat
       If INA[BUTN1]==1
         return (MAIN_MENUE)
    

    That seems very strange - You don't need to do the return in a while loop. I actually don't know how the prop is going to deal with that, because it looks like that "return" function is trying to call your MAIN_MENUE method and returning the result.

    What you probably want instead is the line you've commented out when you wait for the button to be pressed again.
      Waitpne (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is NOT pressed .  
      Waitpeq (|<BUTN1, |<BUTN1, 0)          'Pause program execution until button ina[8] is pressed and held.
    

    Is there a reason you changed that?

    Also, I noticed this comment in your code:
    '   waitpne(0,(|<BUTN1) | (|<BUTN4), 0)    'This waits until either butn1 or butn4 have been pressed before moving on.
                                               'It appears that this statement is quite a bit slower than waitpne( 0, mask, 0)
    

    That's true, but you can make it the same speed by doing this:
     waitpne(0, constant((|<BUTN1) | (|<BUTN4)), 0)    'This waits until either butn1 or butn4 have been pressed before moving on.
    

    constant() tells the compiler to evaluate what's in the braces and use that result, instead of computing it at runtime. Your original version would be performing two shifts and ORing the result together to produce the value.

    I don't know if the weird repeat thing is the cause of your issues, but I'd start with that. It might also be worth outputting some "markers" to the debug terminal as you're going through your loops so you can see the code paths you're taking. If the code isn't doing what you expect, outputting where you are and what your current flag variables are might help you track down the issue.
  • Hi Jason
    I agree with your opinion that my code saying:
    repeat
    If INA[BUTN1]==1
    return (MAIN_MENUE)

    looks very strange.

    The "repeat
    If INA[BUTN1]==1
    return (MAIN_MENUE)" actually does send the program back to the start of the MAIN_MENUE method.

    If I just say MAIN_MENUE the program returns to the beginning of MAIN_MENUE and
    all works ok.

    I've been told that returning to a method by again calling that method is generally a very bad idea.

    What I think may be happening is that when I exit FirstMenue via return statement or by just letting the FirstMenue
    method run until it's end,
    the program may be returning to the CASE which was the last statement causing FirstMenue to run.
    Since the MENUE variable had not changed, program goes immediately back to the FirstMenue method.

    Does that make any sense???

    Your comments in your last post agree 100% with what the Propeller Manual says.
    I think I just cannot figure out how to get back to the start of MAIN_MENUE Method.

    I'll be trying your other suggestions also, but it's getting very late. Sometimes things make more sense in the Morning.

    Thanks
    Garyg



    ..
  • kwinnkwinn Posts: 8,697
    No need to put in return statements. Pub and Pri blocks automatically return when the last statement in the block is executed. A typical program would look like the following based on the code you posted.

  • Hi
    I edited my 1st post to get the code to display correctly the way it was written.
    I found the clue to do this in Kwinn's spin attachment.

    That should make things a bit easier to understand.

    I put Kwinn's flow example as comments into my Object.
    That helped and I was able to eliminate my peculiar return statements.
    BUT

    What happens,
    when I run the updated object,
    It starts out correctly I select the method I want to go to.
    Push BUTN1 and program goes there.
    I push BUTN1 again to exit and hopefully go back to Main_Menue
    The LCD does not update the text in this part of the method.
    pub MAIN_MENUE
    
    'Set up and display initial MENUE LCD view.
      LCD.tx(12)                            ' Clear the LCD display
      waitcnt (60_000 + cnt)                ' Pause for 5ms time for LCD display to clear
      
      LCD.str(string("MENU"))               ' line 0 printed to LCD                                      
      LCD.tx(13)                            ' Line feed
      LCD.str(string("BUTN4 = Select"))     ' line 1 printed to LCD
      LCD.tx(212)                           ' Set quarter note
      LCD.tx(220)                           ' A tone             
    

    I feel very close to getting this working.
    I've tried a number of different things to get this to display again after leaving my
    FirstMenue through ThirdMenue

    I really don't understand why this text part of the code is being skipped.

    Thanks to both of you guys
    you are being very patient and helpful.
    Garyg
  • I believe I've resolved my updating problem.
    I did not have any way to exit the loop in MAIN_MENUE.
    I used Jason and Kwinns explanations to resolve my issues.
    The Object now runs the way I intended from the start.

    I'll be using this REV2 Object and lessons learned in a step motor control object I'm working on.

    Menu_Driven_Object_GG-2016_REV2%20-%20Archive%20%20%5BDate%202016.02.14%20%20Time%2019.31%5D.zip

    Thanks again for all your help on this
    Comments and words of wisdom are always welcome

    Garyg
  • kwinnkwinn Posts: 8,697
    Hi Gary

    I was making some changes to an old project that has a menu system similar to what you are working on and thought it might be of help. I copied the relevant parts into the code you posted and used your variable names as much as possible. Had to test it with PST (no lcd around). Hope it is of some use to you. If you decide to use it and have questions I will be happy to help.
  • Thanks Kwinn
    While my Menue Object is working pretty good,
    Yours appears to be more elegant.
    I'll need to play with it for a while to be sure I understand exactly how it works.
    and

    Thanks Again for all your help on my Object Menu project.
    Garyg
  • I thought it might be interesting to see how I do button presses. I use the modulus operator to step through menu options. In my case I'm only using two options but if I had four options I would use "//4".
    CON
    
        _clkmode = xtal1 + pll16x   
        _xinfreq = 5_000_000
    
      SwitchPin    = 24
      DebounceTime = 80_000_000/20  '50ms at 80Mhz
    
    VAR
      
       byte mode_select
       long debounce, pin, pinflag
    
    OBJ
    
      pst : "Parallax Serial Terminal"
    
    PUB PressButton  
       pst.Start(115_200)
       waitcnt(clkfreq*2+cnt)
       dira[22]~~
       outa[22]~
       dira[SwitchPin] := 0
          
       repeat
         pin := ina[SwitchPin]                'read the pin
         if pin == 1                          'is the pin high?
           waitcnt(DebounceTime+cnt)          'wait
           if pin == 1                        'is the pin still high?
             repeat until ina[SwitchPin] == 0             
             mode_select := ++pinflag//2      'change the mode     
             outa[22] := mode_select             'visual way to see mode_select 
         pst.Str(String(pst#NL, "mode_select = "))
         pst.Dec(mode_select)
         waitcnt(clkfreq/2+cnt)
    
  • garyggaryg Posts: 420
    edited 2016-02-20 02:44
    Hi again.
    Thanks for you button press example.
    I'll try this out to see if I can understand it.
    Things like Modulus operators and statements like mode_select := ++pinflag//2 'change the mode
    are difficult to wrap my head around.

    For now, I need to change gears so to speak.
    I changed my original menu selection routine based on information from this message thread.
    I included the menu selection in my project and things are working good.
    I'll attempt to attach a photo of my test bed and the end result of my spin code.

    My next spin adventure will be concerning saving to eeprom.
    I found a few threads that might get me going on that.
    I need to do lots of reading to begin to understand what to do in the eeprom area.
    More than likely, I'll be starting another thread on the EEprom thing.

    Anyway, thanks again to everyone.
    Garyg

    IMG_2431-Trimmed.jpg


    1stTry_LinearCable-Actuator_LCDgearSettingREV9D%20-%20Archive%20%20%5BDate%202016.02.19%20%20Time%2020.16%5D.zip
  • kwinnkwinn Posts: 8,697
    @garyg

    You're welcome. If you want I could add some more comments and re-post it.
  • garyggaryg Posts: 420
    edited 2016-02-20 03:17
    Kwinn
    If you are willing I would appreciate it greatly.
    Whenever I start on a project I always review my past message threads.
    The ability to review my past message threads on this forum always helps with my projects.

    garyg
  • kwinnkwinn Posts: 8,697
    edited 2016-02-20 06:25
    Added a few comments to the program. Be aware of the following:

    I have been using PST for testing, no buttons or LCD handy. To test the get_buttons code you must press the PC's 1, 2, or 4 key 3 times to get to the appropriate sub menu. Once you switch to using the pushbuttons this will not be needed.

    To switch to the buttons comment out the “ temp1 := LCD.rx “ line and uncomment the “temp1 := ina[butn1..butn4]” and “repeat until ina[butn1..butn4] == 0” lines. Change the “ repeat while loops < 2 ” from 2 to 8.

    The get_buttons routine take care of debouncing the buttons and waiting for them to be released so no other button code should be needed elsewhere. A structure similar to that in the “pub MAIN_MENUE” in each of the sub menus would be a good start for that code.

    PS, you may have to add in some of the LCD control code. I am guessing that writing two lines of 20 characters every time will work ok, but I could be wrong.
  • garyggaryg Posts: 420
    edited 2016-02-20 22:17
    Thanks again Kwinn
    I've added your "Be aware of the following" info directly into
    the LCD menu.spin file.
    I now have a Kwinn subdirectory in my Propeller and Stamp projects directory.
    I'm really not sure why I stopped using PST.
    I think it had to do with the way PST worked differently from the Stamp programming software.
    It appears that most people who post on the forum use PST when doing propeller things.
    I will start using it again when reviewing and testing things related to LCD menu.spin.

    Garyg
  • kwinnkwinn Posts: 8,697
    No problem. Always happy to share code when I can. That little snippet of code in get_buttons is something I use in one form or another any time I have to deal with buttons or switches. Works really well with a case statement for selecting the function to perform. It can be used to read multiple simultaneous button presses so an enable and direction button both have to be pressed for manual motion.
Sign In or Register to comment.