debouncing switches with a timer interrupt?


Closed Thread
Results 1 to 28 of 28
  1. #1
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42

    Default debouncing switches with a timer interrupt?

    hi everyone,
    im experimenting with the "menu builder" application on a 18f2431 and im having a bit of a problem. it inserts a lot of "pause 200" commands, to debounce the switches, which is causing havoc on my real time timer on an lcd. if i remove the pauses, there's no debounce, and one push of a button cycles through alot of menus. i tried the portb interrupt on change, and im getting the same results. i've read some rumours that debouncing can be done using a timer interrupt. is it possible? im using 4 buttons: up, down, enter, exit. any help/idea on pseudocode will really be appreciated; portb interrupt, timer interrupt, software debounce, etc
    NAG CON WIFE!
    WIFE VAR MOOD

  2. #2
    Join Date
    Nov 2005
    Location
    Perth, Australia
    Posts
    429


    Did you find this post helpful? Yes | No

    Default

    If you can change the hardware, one option would be to use capacitors across the switches. Software denounce would no longer be required.

    Otherwise you do do this:

    port b change interrupt: detects a button press and takes action, an the end of the interrupt it sets a timer so that it will overflow in say 50ms, and then disables the port b interrupt.

    when the timer interrupt goes off 50ms later it simply needs to re-enable the port b interrupt and stop the timer.
    "I think fish is nice, but then I think that rain is wet, so who am I to judge?" - Douglas Adams

  3. #3
    Join Date
    Sep 2005
    Location
    Campbell, CA
    Posts
    1,107


    Did you find this post helpful? Yes | No

    Default

    Could it be that you don't need a debouncer at all?

    If you are polling the pin that is connected the switch every program loop, and the loop is longer than the switch bounce time, you don't need a debounce, since it will see the transition, and the "bounces" will be over before you check the switch again.
    Charles Linquist

  4. #4
    Join Date
    Mar 2003
    Location
    Commerce Michigan USA
    Posts
    1,166


    Did you find this post helpful? Yes | No

    Default

    Charles, I do just exactly that. In my interrupt routine. I copy th state of my button and add it to a shifted result. When the button is connected to ground with a pullup to VDD, the value after the 8 cycle (interrupt) debounce is equal to zero. When the button is released the value after the 8 cycle (interrupt) debounce is equal to 255. I use a 10 millisecond interrupt timer so the debounce time is 80 milliseconds. That way all the main has to do is interrogate debounce variable for it's value.

    Dave Purola,
    N8NTA

  5. #5
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    if i remove the pauses, there's no debounce, and one push of a button cycles through alot of menus.
    I would apply a flag condition to control the key state.

    Code:
    False        CON  0
    True         CON  1
    KEYPRESS  VAR portXx
    KeyFlag      VAR BIT
    Counter      VAR WORD
    
    LOOP:
    
    PAUSE 1
    
    IF KEYPRESS = False then
    
    IF KeyFlag = False Then
    Counter = Counter + 1
    KeyFlag = True
    ENDIF
    
    ELSE
    KeyFlag = False
    ENDIF
    .
    .
    .
    .
    .
    GOTO LOOP
    In the above snippet every time you press the key "KEYPRESS" the word variable "Counter" will increment of one unit, no matter how long you keep the key down. If you remove the flag control using pauses, it will be rather complicate to reach the same results.



    Al.
    Last edited by aratti; - 25th July 2010 at 16:12.
    All progress began with an idea

  6. #6
    Join Date
    Nov 2005
    Location
    Perth, Australia
    Posts
    429


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by aratti View Post
    I would apply a flag condition to control the key state.
    This would still require some kind of debouncing. The logic state can "bounce" a number of times when pressing a button, so if it toggles back and forth a few times it could still advance the counter more than once for a single key press if the loop is quick enough.
    "I think fish is nice, but then I think that rain is wet, so who am I to judge?" - Douglas Adams

  7. #7
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    This would still require some kind of debouncing. The logic state can "bounce" a number of times when pressing a button
    Contact's debouncing should be solved with proper RC filter! If proper filtering is not possible an additional IF/THEN statement should reduce greatly the debouncing effect.

    Code:
    False        CON  0
    True         CON  1
    KEYPRESS  VAR portXx
    KeyFlag      VAR BIT
    Counter      VAR WORD
    
    LOOP:
    
    PAUSE 1
    
    IF KEYPRESS = False then
    
    IF KeyFlag = False Then
    Counter = Counter + 1
    KeyFlag = True
    ENDIF
    
    ELSE
    KeyFlag = False
    IF  KEYPRESS = False THEN  KeyFlag = True    ' re-checks the key logic state
    ENDIF
    .
    .
    .
    .
    .
    GOTO LOOP
    Al.
    Last edited by aratti; - 26th July 2010 at 00:01.
    All progress began with an idea

  8. #8
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    thanks guys.

    i tried the capacitor method, and that did not work well. still skipping menu items.

    im using proteus simulation, and i tried every value of cap, 1000uf to 0.000001 uf., and everything in between.

    ill try integrate the software flag idea into the problem, and hope that it fixes it. thanks
    NAG CON WIFE!
    WIFE VAR MOOD

  9. #9
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default still not functioning

    hi all
    i tried to implement the flag idea, but still the same result. menu item cycles through 3 to 6 items with one push of a button. here's the code snippet
    Code:
    RB_MainMenuLoop:
    ' BTN_PLUS is the Next Choice button
        PAUSE 1
          IF BTN_PLUS=0 THEN
          ;PAUSE 200
          IF FLAG_PLUS = 0 THEN
          bMenuPos=bMenuPos+1
          FLAG_PLUS = 1
          ELSE 
          FLAG_PLUS = 0
          IF BTN_PLUS = 0 THEN FLAG_PLUS = 1
          ;ENDIF
          IF bMenuPos>22 THEN
             bMenuPos=1
          ENDIF
          ENDIF
          Goto DisplayMainMenuLoop:
       ENDIF
    im beginning to think that debouncing is not the problem. is there any other way to skin this cat?
    NAG CON WIFE!
    WIFE VAR MOOD

  10. #10
    Join Date
    May 2008
    Location
    Italy
    Posts
    825


    Did you find this post helpful? Yes | No

    Default

    Counter Var Byte

    RB_MainMenuLoop:
    ' BTN_PLUS is the Next Choice button

    PAUSE 1

    IF BTN_PLUS=0 THEN

    IF FLAG_PLUS = 0 THEN
    bMenuPos = bMenuPos + 1
    FLAG_PLUS = 1
    Counter = 0
    ENDIF

    ELSE
    Pause = 10
    Counter = Counter + 1
    IF Counter > 50 THEN FLAG_PLUS = 0 : Counter = 0
    ENDIF

    IF bMenuPos>22 THEN
    bMenuPos=1
    ENDIF

    Goto DisplayMainMenuLoop:

    ================================================== ===============

    Try this snippet and see how it works. You can tune the PAUSE 10 and Counter > 5, to better values to suite your need.

    Al.
    Last edited by aratti; - 30th July 2010 at 08:48.
    All progress began with an idea

  11. #11
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,614


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by DDDvvv View Post
    hi all
    i tried to implement the flag idea, but still the same result.
    Hi,

    You should understand debouncing NEED time, where ever this time comes from ( capacitor, timer ...)

    the idea of debouncing is :

    " Does the electrical state of pin still changes afer X milliseconds ??? "

    so, you can't get a correct answer before X milliseconds !!! - Obvious -

    But 200 ms is debouncing time for really poor quality switches, 20 ms can be obtained easily.

    note also switches " bounce " when closing its contact ... not when opening it !!!

    so, ... is it here THE good idea ???

    Further, May I tell you a real time timer might not be disturbed by a keypress ... - or your program is " not so good " ...

    the delay needed by the RTC to be updated is so far smaller than a key press action ...

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  12. #12
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Consider using a switch state latch and relatively simple logic to sample your switches and detect the "new press" state while ignoring the other switch states. Here are the switch states;

    Code:
    swnew swold  switch state
      1     0    a "new press"
      1     1    still pressed
      0     1    a "new release"
      0     0    still released
    Sample the switches at a decent debounce interval (16 to 25 msecs) either in your main program loop or in a timer based interrupt service routine. You only need a few lines of code to implement the switch state logic for multiple switches (perhaps one of the PBP gurus can interpret the C code? Sorry!);

    Code:
    //  swnew  ____---____-----_____   new switch sample
    //  swold  _____---____-----____   switch state latch
    //  delta  ____-__-___-____-____   changes, press or release
    //  newhi  ____-______-_________   filter out release bits
    //
    
    while(1)                  //
    {                         //
      delay_ms(24)            // 24-msec debounce interval
      swnew = ~porta;         // sample active-lo switches
      swnew &= 0x0F;          // on RA3..RA0 pins
      swnew ^= swold;         // changes, press or release
      swold ^= swnew;         // update switch state latch
      swnew &= swold;         // filter out "new release" bits
      if(swnew)               // if any "new press" bits
      { beep();               // task a "new press" beep
      }
      
      if(swnew.0)             // if RB0 "new press"
      {                       //
      }                       //
    
      if(swnew.1)             // if RB1 "new press"
      {                       //
      }                       //
    }
    If you implement the method in an ISR you'll need to inclusive-or "swnew" with a "flags" register for your main program.

    Kind regards, Mike
    Last edited by Mike, K8LH; - 31st July 2010 at 01:57.

  13. #13
    Join Date
    Sep 2005
    Location
    Campbell, CA
    Posts
    1,107


    Did you find this post helpful? Yes | No

    Default

    The code below was written to use a momentary pushbutton switch as an ON/OFF control.

    It is written to be used in a Timer interrupt routine.

    PowerOnDelayTimer is the number of interrupts that the button must be held before turning ON the unit.

    PowerOFFDelayTimer is the number of interrupts that the button must be held before turning OFF the unit

    SwitchUpDelayTimer is the number of interrupts that must expire before the button can be pushed again (useful when you don't want someone rapidly turning ON and OFF a piece of hardware).




    Code:
                IF !EnableSw THEN     ; Aliased pin, goes low when button pushed.
                     
                     IF SwitchFlag THEN ; Switch has been pushed, but prevously released.
                                                   
                       IF PowerSwitchCounter < 250 THEN 
                               PowerSwitchCounter = PowerSwitchCounter + 1  ; don't let it overflow
                       ENDIF 
                            
                                                  
                       IF PowerONstate = 0 THEN  ; Currently OFF
                           If PowerSwitchCounter >= PowerOnDelayTimer then           ; Turn on or off
                              Gosub PowerOn
                              SwitchFlag = 0
                              SwitchUpCounter = 0
                           ENDIF 
                        ELSE      ; Currently OFF
                           If PowerSwitchCounter >= PowerOffDelayTimer then  
                             GOSUB PowerOff
                              SwitchFlag = 0
                              SwitchUpCounter = 0
                          ENDIF
                       ENDIF
           
                    ENDIF
          
                ELSE
                    IF SwitchUpCounter < 250 THEN 
                          SwitchUpCounter = SwitchUpCounter + 1
                    ENDIF      
                    IF SwitchUpCounter > SwitchUpDelayTimer THEN 
                         SwitchFlag = 1          ; Switch has been released
                         PowerSwitchCounter = 0 
                    ENDIF      
               ENDIF
    Charles Linquist

  14. #14
    Join Date
    Sep 2005
    Location
    Campbell, CA
    Posts
    1,107


    Did you find this post helpful? Yes | No

    Default

    I should state that SwitchFlag must be initialized to a non-zero value at the top of the program.
    Charles Linquist

  15. #15
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    I'd like to supplement Charles' fine example with an interrupt driven version of the "parallel switch state logic" example I posted earlier.

    This example uses a 1-msec timer driven interrupt "heart beat" which is perfect for toggling a piezo speaker at 500-Hz and it uses a simple counter to produce a 32-msec debounce/sample interval.

    Using a momentary switch to emulate a "toggle" switch (press to toggle from on-to-off or from off-to-on) can be accomplished directly by the driver by exclusive-or'ing the debounced "new press" bits with the "flags" switch flag bits variable. Main should test the switch flag bit and then clear it for "momentary" switches or simply test the switch flag bit for an emulated "toggle" switch.

    Code:
    void interrupt()                // 1-msec Timer 2 interrupts
    { 
      pir1.TMR2IF = 0;              // clear TMR2 interrupt flag
    //
    //  beep task (32-msec 500-Hz tone using 1-msec interrupts)
    //
      if(beep)                      // if beep task running
      { spkrpin ^= 1;               // toggle speaker pin and
        beep--;                     // decrement beep msec counter
      }
    //
    //  switch state management (multi-switch "parallel" logic)
    //
      if(--dbcount == 0)            // if 32-msec debounce interval
      { dbcount = 32;               // reset debounce timer and
        swnew = ~porta;             // sample active low switches
        swnew &= 0x0F;              // on RA3..RA0 pins
        swnew ^= swold;             // changes, press or release
        swold ^= swnew;             // update switch state latch
        swnew &= swold;             // filter out "new release" bits
        flags ^= swnew;             // toggle flag bits for 'main'
        if(swnew)                   // if any "new press" bits
          beep = 32;                // task a "new press" beep
      }
    }
    Code:
    //  in "main"
    
      while(flags.0)                // while sw0 (RA0) "on" (toggle), do this block
      {                             // until new sw0 press toggles flags.0 to "off"
        if(flags.1)                 // if sw1 (RA1) "new press" (momentary)
        { flags.1 = 0;              // turn off flag and
          ...                       // do something
        }
      }
    Last edited by Mike, K8LH; - 1st August 2010 at 01:04.

  16. #16
    Join Date
    Jul 2003
    Posts
    2,405


    Did you find this post helpful? Yes | No

    Default

    Can you post the code you're having problems with? I suspect there's a very simple solution.
    Regards,

    -Bruce
    tech at rentron.com
    http://www.rentron.com

  17. #17
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    thanks for all the replies guys. ive been away for the weekend, with no computer access. here's the code, fresh from the menubuilder application.

    its a gadget that im making, to keep track/records of my workout sessions. it will also have a realtime clock/stopwatch, all displayed on a 20x2 lcd with 4 menu buttons (next, previous, up down.)

    even with the "pause 200" debouncing, (default) everytime i try to cycle through menu items, with the push of a button, it skips three to six items. (same goes when you try to change a variable) at first, i thought that proteus simulation on my pentium 4 was slow (always bitching about "simulation not running in real time because of excessive cpu load" ) so i transferred the simulation to my intel i7 laptop, and proteus is not complaining any more. still having the same skipping items problem.

    ill try mr Arrati's snipet again, but meanwhile, here's the code.

    Code:
            clear
    define OSC 16                                ;40 MHZ HS CRYSTAL
    OSCCON = %01110000
    ADCON0= %00000000                         ;PORT A ALL DIGITAL       
    ANSEL0= %00000000                         ;PORT A ALL DIGITAL
    TRISA = %00000000                         ;
    TRISB = %11111100                         ;
    TRISC = %10111000                         ;
                                                   ;
    LATA = %00000000                          ;
    LATB = %00000000                          ;
    LATC = %00000000                          ;
    
    
    
    'VARIABLE DECLARATION
    bMenuPos    var Byte     'Main menu position
    bSubMenuPos var Byte     'Sub menu position
    BTN_NEXT    var PORTB.4  'Next button
    BTN_PREV    var PORTB.5  'Previous button
    BTN_PLUS    var PORTB.6  'Plus button
    BTN_MINUS   var PORTB.7  'Minus button
    SQUATS_WT  VAR  Byte
    SQUATS_RP  VAR  Byte
    PULLDOWN_WT  VAR  Byte
    PULLDOWN_RP  VAR  Byte
    LATRAISE_WT  VAR  Byte
    LATRAISE_RP  VAR  Byte
    OVHEADPRESS_WT  VAR  Byte
    OVHEADPRESS_RP  VAR  Byte
    PREACHERCURL_WT  VAR  Byte
    PREACHERCURL_RP  VAR  Byte
    PUSHDOWN_WT  VAR  Byte
    PUSHDOWN_RP  VAR  Byte
    PECDECK_WT  VAR  Byte
    PECDECK_RP  VAR  Byte
    CABLESHRUG_WT  VAR  Byte
    CABLESHRUG_RP  VAR  Byte
    CALFRAISES_WT  VAR  Byte
    CALFRAISES_RP  VAR  Byte
    DBWRISTCURL_WT  VAR  Byte
    DBWRISTCURL_RP  VAR  Byte
    DECLINESITUP_WT  VAR  Byte
    DECLINESITUP_RP  VAR  Byte
    'END OF VARIABLE DECLARATION
    
    
    
    DEFINE LCD_DREG PORTA
    DEFINE LCD_DBIT 0
    DEFINE LCD_BITS 4
    DEFINE LCD_RSREG PORTC
    DEFINE LCD_RSBIT 0
    DEFINE LCD_EREG PORTC
    DEFINE LCD_EBIT 1
    
        
    PAUSE 500
    
    LCDOUT $FE, $01
    
    LCDOUT $FE, $80, " FITNESS GYM ASSIST"
    LCDOUT $FE, $C0, " COPYRIGHT DAVID M"
    
    PAUSE 1000
    
    MAIN
       
    GOSUB DisplayMainMenu
    GOTO MAIN
    
    
    'SUB ROUTINES
    
    '******************************************************
    'ROUTINE DisplayMainMenu
    'This routine handles the display of the main menu choices
    'All you need is to call from your main program this routine,
    'and have the button ports setup,
    '******************************************************
    DisplayMainMenu:
       bMenuPos=1   'This is the default menu option
    DisplayMainMenuLoop:
       IF bMenuPos=1 THEN
          LCDOUT $FE,1,"SQUATS WT"
       ENDIF
       IF bMenuPos=2 THEN
          LCDOUT $FE,1,"SQUATS RP"
       ENDIF
       IF bMenuPos=3 THEN
          LCDOUT $FE,1,"PULLDOWN WT"
       ENDIF
       IF bMenuPos=4 THEN
          LCDOUT $FE,1,"PULLDOWN RP"
       ENDIF
       IF bMenuPos=5 THEN
          LCDOUT $FE,1,"LAT RAISE WT"
       ENDIF
       IF bMenuPos=6 THEN
          LCDOUT $FE,1,"LAT RAISE RP"
       ENDIF
       IF bMenuPos=7 THEN
          LCDOUT $FE,1,"OVHEAD PRESS WT"
       ENDIF
       IF bMenuPos=8 THEN
          LCDOUT $FE,1,"OVHEAD PRESS RP"
       ENDIF
       IF bMenuPos=9 THEN
          LCDOUT $FE,1,"PREACHER CURL WT"
       ENDIF
       IF bMenuPos=10 THEN
          LCDOUT $FE,1,"PREACHER CURL RP"
       ENDIF
       IF bMenuPos=11 THEN
          LCDOUT $FE,1,"PUSHDOWN WT"
       ENDIF
       IF bMenuPos=12 THEN
          LCDOUT $FE,1,"PUSHDOWN RP"
       ENDIF
       IF bMenuPos=13 THEN
          LCDOUT $FE,1,"PEC DECK WT"
       ENDIF
       IF bMenuPos=14 THEN
          LCDOUT $FE,1,"PEC DECK RP"
       ENDIF
       IF bMenuPos=15 THEN
          LCDOUT $FE,1,"CABLE SHRUG WT"
       ENDIF
       IF bMenuPos=16 THEN
          LCDOUT $FE,1,"CABLE SHRUG RP"
       ENDIF
       IF bMenuPos=17 THEN
          LCDOUT $FE,1,"CALF RAISES WT"
       ENDIF
       IF bMenuPos=18 THEN
          LCDOUT $FE,1,"CALF RAISES RP"
       ENDIF
       IF bMenuPos=19 THEN
          LCDOUT $FE,1,"DBWRIST CURL WT"
       ENDIF
       IF bMenuPos=20 THEN
          LCDOUT $FE,1,"DBWRIST CURL RP"
       ENDIF
       IF bMenuPos=21 THEN
          LCDOUT $FE,1,"DECLINE SITUP WT"
       ENDIF
       IF bMenuPos=22 THEN
          LCDOUT $FE,1,"DECLINE SITUP RP"
       ENDIF
    RB_MainMenuLoop:
    ' BTN_PLUS is the Next Choice button
       IF BTN_PLUS=0 THEN
          PAUSE 200
          bMenuPos=bMenuPos+1
          IF bMenuPos>22 THEN
             bMenuPos=1
          ENDIF
          Goto DisplayMainMenuLoop:
       ENDIF
    ' BTN_PREV is the Exit button
       IF BTN_PREV=0 THEN
          PAUSE 200
          RETURN
       ENDIF
    ' BTN_NEXT is the Goto SubMenu Choice button
       IF BTN_NEXT=0 THEN
          PAUSE 200
          GoSub DisplaySubMenu
          Goto DisplayMainMenuLoop
       ENDIF
    Goto RB_MainMenuLoop:
    '******************************************************
    'ROUTINE DisplaySubMenu
    'This routine handles the display of the sub menu choices
    '******************************************************
    DisplaySubMenu:
       bSubMenuPos=1   'This is the default menu option
    DisplaySubMenuLoop:
       IF bMenuPos=1 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"SQUATS WT ADJ"
       ENDIF
       IF bMenuPos=2 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"SQUATS RP ADJ"
       ENDIF
       IF bMenuPos=3 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PULLDOWN WT ADJ"
       ENDIF
       IF bMenuPos=4 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PULLDOWN RP ADJ"
       ENDIF
       IF bMenuPos=5 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"LAT RAISE WT ADJ"
       ENDIF
       IF bMenuPos=6 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"LAT RAISE RP ADJ"
       ENDIF
       IF bMenuPos=7 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"OVHEAD PRESS WT"
       ENDIF
       IF bMenuPos=8 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"OVHEAD PRESS RP ADJ"
       ENDIF
       IF bMenuPos=9 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PREACHER CURL WT ADJ"
       ENDIF
       IF bMenuPos=10 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PREACHER CURL RP ADJ"
       ENDIF
       IF bMenuPos=11 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PUSHDOWN WT ADJ"
       ENDIF
       IF bMenuPos=12 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PUSHDOWN RP ADJ"
       ENDIF
       IF bMenuPos=13 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PEC DECK WT ADJ"
       ENDIF
       IF bMenuPos=14 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"PEC DECK RP ADJ"
       ENDIF
       IF bMenuPos=15 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"CABLE SHRUG WT ADJ"
       ENDIF
       IF bMenuPos=16 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"CABLE SHRUG RP ADJ"
       ENDIF
       IF bMenuPos=17 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"CALF RAISES WT ADJ"
       ENDIF
       IF bMenuPos=18 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"CALF RAISES RP ADJ"
       ENDIF
       IF bMenuPos=19 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"DBWRIST CURL WT ADJ"
       ENDIF
       IF bMenuPos=20 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"DBWRIST CURL RP ADJ"
       ENDIF
       IF bMenuPos=21 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"DECLINE SITUP WT ADJ"
       ENDIF
       IF bMenuPos=22 AND bSubMenuPos=1 THEN
          LCDOUT $FE,1,"DECLINE SITUP RP ADJ"
       ENDIF
    RB_SUBMenuLoop:
    ' BTN_PLUS is the Next Choice button
       IF BTN_PLUS=0 THEN
          PAUSE 200
          bSubMenuPos=bSubMenuPos+1
          IF bMenuPos=1 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=2 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=3 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=4 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=5 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=6 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=7 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=8 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=9 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=10 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=11 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=12 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=13 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=14 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=15 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=16 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=17 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=18 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=19 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=20 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=21 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          IF bMenuPos=22 AND bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          GoTo DisplaySubMenuLoop
       ENDIF
    ' BTN_PREV is the Exit button
       IF BTN_PREV=0 THEN
          PAUSE 200
          RETURN
       ENDIF
    ' BTN_NEXT is the Goto SubMenu Choice button
       IF BTN_NEXT=0 THEN
          PAUSE 200
          IF bMenuPos=1 AND bSubMenuPos=1 THEN
             GoSub SUB_1_1
          ENDIF
          IF bMenuPos=2 AND bSubMenuPos=1 THEN
             GoSub SUB_2_1
          ENDIF
          IF bMenuPos=3 AND bSubMenuPos=1 THEN
             GoSub SUB_3_1
          ENDIF
          IF bMenuPos=4 AND bSubMenuPos=1 THEN
             GoSub SUB_4_1
          ENDIF
          IF bMenuPos=5 AND bSubMenuPos=1 THEN
             GoSub SUB_5_1
          ENDIF
          IF bMenuPos=6 AND bSubMenuPos=1 THEN
             GoSub SUB_6_1
          ENDIF
          IF bMenuPos=7 AND bSubMenuPos=1 THEN
             GoSub SUB_7_1
          ENDIF
          IF bMenuPos=8 AND bSubMenuPos=1 THEN
             GoSub SUB_8_1
          ENDIF
          IF bMenuPos=9 AND bSubMenuPos=1 THEN
             GoSub SUB_9_1
          ENDIF
          IF bMenuPos=10 AND bSubMenuPos=1 THEN
             GoSub SUB_10_1
          ENDIF
          IF bMenuPos=11 AND bSubMenuPos=1 THEN
             GoSub SUB_11_1
          ENDIF
          IF bMenuPos=12 AND bSubMenuPos=1 THEN
             GoSub SUB_12_1
          ENDIF
          IF bMenuPos=13 AND bSubMenuPos=1 THEN
             GoSub SUB_13_1
          ENDIF
          IF bMenuPos=14 AND bSubMenuPos=1 THEN
             GoSub SUB_14_1
          ENDIF
          IF bMenuPos=15 AND bSubMenuPos=1 THEN
             GoSub SUB_15_1
          ENDIF
          IF bMenuPos=16 AND bSubMenuPos=1 THEN
             GoSub SUB_16_1
          ENDIF
          IF bMenuPos=17 AND bSubMenuPos=1 THEN
             GoSub SUB_17_1
          ENDIF
          IF bMenuPos=18 AND bSubMenuPos=1 THEN
             GoSub SUB_18_1
          ENDIF
          IF bMenuPos=19 AND bSubMenuPos=1 THEN
             GoSub SUB_19_1
          ENDIF
          IF bMenuPos=20 AND bSubMenuPos=1 THEN
             GoSub SUB_20_1
          ENDIF
          IF bMenuPos=21 AND bSubMenuPos=1 THEN
             GoSub SUB_21_1
          ENDIF
          IF bMenuPos=22 AND bSubMenuPos=1 THEN
             GoSub SUB_22_1
          ENDIF
          GoTo DisplaySubMenuLoop
       ENDIF
    GoTo RB_SUBMenuLoop
    '*************** SUB SUB_1_1*********
    SUB_1_1:
    'First read the variables from eprom
       READ 1,SQUATS_WT
    SUB_1_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC SQUATS_WT
    SUB_1_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF SQUATS_WT>=255 THEN
             SQUATS_WT=0
          ELSE
             SQUATS_WT=SQUATS_WT+1
          ENDIF
          GoTo SUB_1_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF SQUATS_WT<=0 THEN
             SQUATS_WT=255
          ELSE
             SQUATS_WT=SQUATS_WT-1
          ENDIF
          GoTo SUB_1_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 1,SQUATS_WT
          RETURN
       ENDIF
    GoTo SUB_1_1MLoop
    RETURN
    '*************** SUB SUB_2_1*********
    SUB_2_1:
    'First read the variables from eprom
       READ 2,SQUATS_RP
    SUB_2_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC SQUATS_RP
    SUB_2_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF SQUATS_RP>=255 THEN
             SQUATS_RP=0
          ELSE
             SQUATS_RP=SQUATS_RP+1
          ENDIF
          GoTo SUB_2_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF SQUATS_RP<=0 THEN
             SQUATS_RP=255
          ELSE
             SQUATS_RP=SQUATS_RP-1
          ENDIF
          GoTo SUB_2_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 2,SQUATS_RP
          RETURN
       ENDIF
    GoTo SUB_2_1MLoop
    RETURN
    '*************** SUB SUB_3_1*********
    SUB_3_1:
    'First read the variables from eprom
       READ 3,PULLDOWN_WT
    SUB_3_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PULLDOWN_WT
    SUB_3_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PULLDOWN_WT>=255 THEN
             PULLDOWN_WT=0
          ELSE
             PULLDOWN_WT=PULLDOWN_WT+1
          ENDIF
          GoTo SUB_3_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PULLDOWN_WT<=0 THEN
             PULLDOWN_WT=255
          ELSE
             PULLDOWN_WT=PULLDOWN_WT-1
          ENDIF
          GoTo SUB_3_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 3,PULLDOWN_WT
          RETURN
       ENDIF
    GoTo SUB_3_1MLoop
    RETURN
    '*************** SUB SUB_4_1*********
    SUB_4_1:
    'First read the variables from eprom
       READ 4,PULLDOWN_RP
    SUB_4_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PULLDOWN_RP
    SUB_4_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PULLDOWN_RP>=255 THEN
             PULLDOWN_RP=0
          ELSE
             PULLDOWN_RP=PULLDOWN_RP+1
          ENDIF
          GoTo SUB_4_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PULLDOWN_RP<=0 THEN
             PULLDOWN_RP=255
          ELSE
             PULLDOWN_RP=PULLDOWN_RP-1
          ENDIF
          GoTo SUB_4_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 4,PULLDOWN_RP
          RETURN
       ENDIF
    GoTo SUB_4_1MLoop
    RETURN
    '*************** SUB SUB_5_1*********
    SUB_5_1:
    'First read the variables from eprom
       READ 5,LATRAISE_WT
    SUB_5_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC LATRAISE_WT
    SUB_5_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF LATRAISE_WT>=255 THEN
             LATRAISE_WT=0
          ELSE
             LATRAISE_WT=LATRAISE_WT+1
          ENDIF
          GoTo SUB_5_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF LATRAISE_WT<=0 THEN
             LATRAISE_WT=255
          ELSE
             LATRAISE_WT=LATRAISE_WT-1
          ENDIF
          GoTo SUB_5_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 5,LATRAISE_WT
          RETURN
       ENDIF
    GoTo SUB_5_1MLoop
    RETURN
    '*************** SUB SUB_6_1*********
    SUB_6_1:
    'First read the variables from eprom
       READ 6,LATRAISE_RP
    SUB_6_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC LATRAISE_RP
    SUB_6_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF LATRAISE_RP>=255 THEN
             LATRAISE_RP=0
          ELSE
             LATRAISE_RP=LATRAISE_RP+1
          ENDIF
          GoTo SUB_6_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF LATRAISE_RP<=0 THEN
             LATRAISE_RP=255
          ELSE
             LATRAISE_RP=LATRAISE_RP-1
          ENDIF
          GoTo SUB_6_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 6,LATRAISE_RP
          RETURN
       ENDIF
    GoTo SUB_6_1MLoop
    RETURN
    '*************** SUB SUB_7_1*********
    SUB_7_1:
    'First read the variables from eprom
       READ 7,OVHEADPRESS_WT
    SUB_7_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC OVHEADPRESS_WT
    SUB_7_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF OVHEADPRESS_WT>=255 THEN
             OVHEADPRESS_WT=0
          ELSE
             OVHEADPRESS_WT=OVHEADPRESS_WT+1
          ENDIF
          GoTo SUB_7_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF OVHEADPRESS_WT<=0 THEN
             OVHEADPRESS_WT=255
          ELSE
             OVHEADPRESS_WT=OVHEADPRESS_WT-1
          ENDIF
          GoTo SUB_7_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 7,OVHEADPRESS_WT
          RETURN
       ENDIF
    GoTo SUB_7_1MLoop
    RETURN
    '*************** SUB SUB_8_1*********
    SUB_8_1:
    'First read the variables from eprom
       READ 8,OVHEADPRESS_RP
    SUB_8_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC OVHEADPRESS_RP
    SUB_8_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF OVHEADPRESS_RP>=255 THEN
             OVHEADPRESS_RP=0
          ELSE
             OVHEADPRESS_RP=OVHEADPRESS_RP+1
          ENDIF
          GoTo SUB_8_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF OVHEADPRESS_RP<=0 THEN
             OVHEADPRESS_RP=255
          ELSE
             OVHEADPRESS_RP=OVHEADPRESS_RP-1
          ENDIF
          GoTo SUB_8_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 8,OVHEADPRESS_RP
          RETURN
       ENDIF
    GoTo SUB_8_1MLoop
    RETURN
    '*************** SUB SUB_9_1*********
    SUB_9_1:
    'First read the variables from eprom
       READ 9,PREACHERCURL_WT
    SUB_9_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PREACHERCURL_WT
    SUB_9_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PREACHERCURL_WT>=255 THEN
             PREACHERCURL_WT=0
          ELSE
             PREACHERCURL_WT=PREACHERCURL_WT+1
          ENDIF
          GoTo SUB_9_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PREACHERCURL_WT<=0 THEN
             PREACHERCURL_WT=255
          ELSE
             PREACHERCURL_WT=PREACHERCURL_WT-1
          ENDIF
          GoTo SUB_9_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 9,PREACHERCURL_WT
          RETURN
       ENDIF
    GoTo SUB_9_1MLoop
    RETURN
    '*************** SUB SUB_10_1*********
    SUB_10_1:
    'First read the variables from eprom
       READ 10,PREACHERCURL_RP
    SUB_10_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PREACHERCURL_RP
    SUB_10_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PREACHERCURL_RP>=255 THEN
             PREACHERCURL_RP=0
          ELSE
             PREACHERCURL_RP=PREACHERCURL_RP+1
          ENDIF
          GoTo SUB_10_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PREACHERCURL_RP<=0 THEN
             PREACHERCURL_RP=255
          ELSE
             PREACHERCURL_RP=PREACHERCURL_RP-1
          ENDIF
          GoTo SUB_10_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 10,PREACHERCURL_RP
          RETURN
       ENDIF
    GoTo SUB_10_1MLoop
    RETURN
    '*************** SUB SUB_11_1*********
    SUB_11_1:
    'First read the variables from eprom
       READ 11,PUSHDOWN_WT
    SUB_11_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PUSHDOWN_WT
    SUB_11_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PUSHDOWN_WT>=255 THEN
             PUSHDOWN_WT=0
          ELSE
             PUSHDOWN_WT=PUSHDOWN_WT+1
          ENDIF
          GoTo SUB_11_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PUSHDOWN_WT<=0 THEN
             PUSHDOWN_WT=255
          ELSE
             PUSHDOWN_WT=PUSHDOWN_WT-1
          ENDIF
          GoTo SUB_11_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 11,PUSHDOWN_WT
          RETURN
       ENDIF
    GoTo SUB_11_1MLoop
    RETURN
    '*************** SUB SUB_12_1*********
    SUB_12_1:
    'First read the variables from eprom
       READ 12,PUSHDOWN_RP
    SUB_12_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PUSHDOWN_RP
    SUB_12_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PUSHDOWN_RP>=255 THEN
             PUSHDOWN_RP=0
          ELSE
             PUSHDOWN_RP=PUSHDOWN_RP+1
          ENDIF
          GoTo SUB_12_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PUSHDOWN_RP<=0 THEN
             PUSHDOWN_RP=255
          ELSE
             PUSHDOWN_RP=PUSHDOWN_RP-1
          ENDIF
          GoTo SUB_12_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 12,PUSHDOWN_RP
          RETURN
       ENDIF
    GoTo SUB_12_1MLoop
    RETURN
    '*************** SUB SUB_13_1*********
    SUB_13_1:
    'First read the variables from eprom
       READ 13,PECDECK_WT
    SUB_13_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PECDECK_WT
    SUB_13_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PECDECK_WT>=255 THEN
             PECDECK_WT=0
          ELSE
             PECDECK_WT=PECDECK_WT+1
          ENDIF
          GoTo SUB_13_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PECDECK_WT<=0 THEN
             PECDECK_WT=255
          ELSE
             PECDECK_WT=PECDECK_WT-1
          ENDIF
          GoTo SUB_13_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 13,PECDECK_WT
          RETURN
       ENDIF
    GoTo SUB_13_1MLoop
    RETURN
    '*************** SUB SUB_14_1*********
    SUB_14_1:
    'First read the variables from eprom
       READ 14,PECDECK_RP
    SUB_14_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC PECDECK_RP
    SUB_14_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF PECDECK_RP>=255 THEN
             PECDECK_RP=0
          ELSE
             PECDECK_RP=PECDECK_RP+1
          ENDIF
          GoTo SUB_14_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF PECDECK_RP<=0 THEN
             PECDECK_RP=255
          ELSE
             PECDECK_RP=PECDECK_RP-1
          ENDIF
          GoTo SUB_14_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 14,PECDECK_RP
          RETURN
       ENDIF
    GoTo SUB_14_1MLoop
    RETURN
    '*************** SUB SUB_15_1*********
    SUB_15_1:
    'First read the variables from eprom
       READ 15,CABLESHRUG_WT
    SUB_15_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC CABLESHRUG_WT
    SUB_15_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF CABLESHRUG_WT>=255 THEN
             CABLESHRUG_WT=0
          ELSE
             CABLESHRUG_WT=CABLESHRUG_WT+1
          ENDIF
          GoTo SUB_15_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF CABLESHRUG_WT<=0 THEN
             CABLESHRUG_WT=255
          ELSE
             CABLESHRUG_WT=CABLESHRUG_WT-1
          ENDIF
          GoTo SUB_15_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 15,CABLESHRUG_WT
          RETURN
       ENDIF
    GoTo SUB_15_1MLoop
    RETURN
    '*************** SUB SUB_16_1*********
    SUB_16_1:
    'First read the variables from eprom
       READ 16,CABLESHRUG_RP
    SUB_16_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC CABLESHRUG_RP
    SUB_16_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF CABLESHRUG_RP>=255 THEN
             CABLESHRUG_RP=0
          ELSE
             CABLESHRUG_RP=CABLESHRUG_RP+1
          ENDIF
          GoTo SUB_16_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF CABLESHRUG_RP<=0 THEN
             CABLESHRUG_RP=255
          ELSE
             CABLESHRUG_RP=CABLESHRUG_RP-1
          ENDIF
          GoTo SUB_16_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 16,CABLESHRUG_RP
          RETURN
       ENDIF
    GoTo SUB_16_1MLoop
    RETURN
    '*************** SUB SUB_17_1*********
    SUB_17_1:
    'First read the variables from eprom
       READ 17,CALFRAISES_WT
    SUB_17_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC CALFRAISES_WT
    SUB_17_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF CALFRAISES_WT>=255 THEN
             CALFRAISES_WT=0
          ELSE
             CALFRAISES_WT=CALFRAISES_WT+1
          ENDIF
          GoTo SUB_17_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF CALFRAISES_WT<=0 THEN
             CALFRAISES_WT=255
          ELSE
             CALFRAISES_WT=CALFRAISES_WT-1
          ENDIF
          GoTo SUB_17_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 17,CALFRAISES_WT
          RETURN
       ENDIF
    GoTo SUB_17_1MLoop
    RETURN
    '*************** SUB SUB_18_1*********
    SUB_18_1:
    'First read the variables from eprom
       READ 18,CALFRAISES_RP
    SUB_18_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC CALFRAISES_RP
    SUB_18_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF CALFRAISES_RP>=255 THEN
             CALFRAISES_RP=0
          ELSE
             CALFRAISES_RP=CALFRAISES_RP+1
          ENDIF
          GoTo SUB_18_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF CALFRAISES_RP<=0 THEN
             CALFRAISES_RP=255
          ELSE
             CALFRAISES_RP=CALFRAISES_RP-1
          ENDIF
          GoTo SUB_18_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 18,CALFRAISES_RP
          RETURN
       ENDIF
    GoTo SUB_18_1MLoop
    RETURN
    '*************** SUB SUB_19_1*********
    SUB_19_1:
    'First read the variables from eprom
       READ 19,DBWRISTCURL_WT
    SUB_19_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC DBWRISTCURL_WT
    SUB_19_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF DBWRISTCURL_WT>=255 THEN
             DBWRISTCURL_WT=0
          ELSE
             DBWRISTCURL_WT=DBWRISTCURL_WT+1
          ENDIF
          GoTo SUB_19_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF DBWRISTCURL_WT<=0 THEN
             DBWRISTCURL_WT=255
          ELSE
             DBWRISTCURL_WT=DBWRISTCURL_WT-1
          ENDIF
          GoTo SUB_19_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 19,DBWRISTCURL_WT
          RETURN
       ENDIF
    GoTo SUB_19_1MLoop
    RETURN
    '*************** SUB SUB_20_1*********
    SUB_20_1:
    'First read the variables from eprom
       READ 20,DBWRISTCURL_RP
    SUB_20_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC DBWRISTCURL_RP
    SUB_20_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF DBWRISTCURL_RP>=255 THEN
             DBWRISTCURL_RP=0
          ELSE
             DBWRISTCURL_RP=DBWRISTCURL_RP+1
          ENDIF
          GoTo SUB_20_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF DBWRISTCURL_RP<=0 THEN
             DBWRISTCURL_RP=255
          ELSE
             DBWRISTCURL_RP=DBWRISTCURL_RP-1
          ENDIF
          GoTo SUB_20_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 20,DBWRISTCURL_RP
          RETURN
       ENDIF
    GoTo SUB_20_1MLoop
    RETURN
    '*************** SUB SUB_21_1*********
    SUB_21_1:
    'First read the variables from eprom
       READ 21,DECLINESITUP_WT
    SUB_21_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC DECLINESITUP_WT
    SUB_21_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF DECLINESITUP_WT>=255 THEN
             DECLINESITUP_WT=0
          ELSE
             DECLINESITUP_WT=DECLINESITUP_WT+1
          ENDIF
          GoTo SUB_21_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF DECLINESITUP_WT<=0 THEN
             DECLINESITUP_WT=255
          ELSE
             DECLINESITUP_WT=DECLINESITUP_WT-1
          ENDIF
          GoTo SUB_21_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 21,DECLINESITUP_WT
          RETURN
       ENDIF
    GoTo SUB_21_1MLoop
    RETURN
    '*************** SUB SUB_22_1*********
    SUB_22_1:
    'First read the variables from eprom
       READ 22,DECLINESITUP_RP
    SUB_22_1MDLoop:
       LCDOUT $FE,192,"                ",$FE,192,DEC DECLINESITUP_RP
    SUB_22_1MLoop:
       IF BTN_PLUS=0 THEN
          PAUSE 100
          IF DECLINESITUP_RP>=255 THEN
             DECLINESITUP_RP=0
          ELSE
             DECLINESITUP_RP=DECLINESITUP_RP+1
          ENDIF
          GoTo SUB_22_1MDLoop
       ENDIF
       IF BTN_MINUS=0 THEN
          PAUSE 100
          IF DECLINESITUP_RP<=0 THEN
             DECLINESITUP_RP=255
          ELSE
             DECLINESITUP_RP=DECLINESITUP_RP-1
          ENDIF
          GoTo SUB_22_1MDLoop
       ENDIF
       IF BTN_PREV=0 THEN
          PAUSE 100
          WRITE 22,DECLINESITUP_RP
          RETURN
       ENDIF
    GoTo SUB_22_1MLoop
    RETURN
    NAG CON WIFE!
    WIFE VAR MOOD

  18. #18
    Join Date
    Jul 2003
    Posts
    2,405


    Did you find this post helpful? Yes | No

    Default

    Instead of a pause after any button press try using WHILE WEND to wait for it to be released.

    Code:
    IF BTN_PLUS=0 THEN
        WHILE BTN_PLUS = 0
        WEND
    Also - you have a ton of RETURN statements after GOTOs' so it never lands on these. I haven't followed your code all the way through, but if it needs to land on the returns, that could be another problem.

    See if the attached works without skipping menu options.
    Attached Files Attached Files
    Last edited by Bruce; - 1st August 2010 at 20:55. Reason: attachment
    Regards,

    -Bruce
    tech at rentron.com
    http://www.rentron.com

  19. #19
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    That's the easy fix but I cringe every time someone suggests waiting for a switch release.

    Since you'll probably be using interrupts eventually to support an RTC or Timer function, I would suggest delegating switch debounce/management to the ISR. Then you're working with fully debounced real-time "new press" flag bits. Your program would need the ISR and a couple subtle changes;

    Code:
    SWNEW       var Byte     '
    SWOLD       var Byte     ' switch state latch
    FLAGS       var Byte     ' switch flag bits
    BTN_NEXT    var FLAGS.4  'Next button
    BTN_PREV    var FLAGS.5  'Previous button
    BTN_PLUS    var FLAGS.6  'Plus button
    BTN_MINUS   var FLAGS.7  'Minus button
    Changes to your main program include testing for a '1' instead of a '0' and clearing the flag bit when you use it.

    Code:
    RB_SUBMenuLoop:
    ' BTN_PLUS is the Next Choice button
       IF BTN_PLUS=1 THEN
          BTN_PLUS=0
          ...
       ENDIF
    ' BTN_PREV is the Exit button
       IF BTN_PREV=1 THEN
          BTN_PREV=0
          RETURN
       ENDIF
    ' BTN_NEXT is the Goto SubMenu Choice button
       IF BTN_NEXT=1 THEN
          BTN_NEXT=0
          ...
       ENDIF
    GoTo RB_SUBMenuLoop
    If you're interested, you might consider trying out a non-interrupt version by adding a sample/debounce subroutine to your program;

    Code:
    '
    '  I need help with PBP commands for a "delay" and for "compliment"
    '
    SUB_SWITCHES                    '
      delay_ms(16)                  ' 16-msec debounce interval  ??? how
      SWNEW = ~PORTB                ' sample active lo switches  ??? how
      SWNEW = SWNEW AND 0xF0        ' on RB7..RB4 pins
      SWNEW = SWNEW XOR SWOLD       ' changes, press or release
      SWOLD = SWOLD XOR SWNEW       ' update switch state latch
      SWNEW = SWNEW AND SWOLD       ' filter out "new release" bits
      FLAGS = FLAGS XOR SWNEW       ' toggle switch flag bits
    RETURN
    Then you would need to call the subroutine at the top of each of your switch test loops (forgive me for shortening your program and for any PBP syntax errors);

    Code:
    RB_SUBMenuLoop:
      GoSub SUB_SWITCHES                    ' sample switches
    ' BTN_PLUS is the Next Choice button
       IF BTN_PLUS=1 THEN
          BTN_PLUS=0
          bSubMenuPos=bSubMenuPos+1
          IF bSubMenuPos>1 THEN
             bSubMenuPos=1
          ENDIF
          GoTo DisplaySubMenuLoop
       ENDIF
    ' BTN_PREV is the Exit button
       IF BTN_PREV=1 THEN
          BTN_PREV=0
          RETURN
       ENDIF
    ' BTN_NEXT is the Goto SubMenu Choice button
       IF BTN_NEXT=1 THEN
          BTN_NEXT=0
          IF bSubMenuPos=1 THEN
             GoSub SUB_ADJ
          ENDIF
          GoTo DisplaySubMenuLoop
       ENDIF
    GoTo RB_SUBMenuLoop
    
    '----------------------------------------------------------------
    '
    '   adjust EEPROM value indexed by 'bMenuPos' variable (1..22)
    '
    '   1 SQUATS_WT          9 PREACHER CURL WT  17 CALF RAISES WT
    '   2 SQUATS_RP         10 PREACHER CURL RP  18 CALF RAISES RP
    '   3 PULLDOWN WT       11 PUSHDOWN WT       19 DBWRIST CURL WT
    '   4 PULLDOWN RP       12 PUSHDOWN RP       20 DBWRIST CURL RP
    '   5 LAT RAISE WT      13 PEC DECK WT       21 DECLINE SITUP WT
    '   6 LAT RAISE RP      14 PEC DECK RP       22 DECLINE SITUP RP
    '   7 OVHEAD PRESS WT   15 CABLE SHRUG WT
    '   8 OVHEAD PRESS RP   16 CABLE SHRUG RP
    '
    SUB_ADJ:                        '
      READ bMenuPos,COUNTER         ' read EEPROM location 1..22
    SUB_ADJ_1MDLoop:                '
      LCDOUT $FE,192,"                "
      LCDOUT $FE,192,DEC COUNTER    '
    SUB_ADJ_1MLoop:                 '
      GoSub SUB_SWITCHES            ' sample switches
      IF BTN_PLUS = 1 THEN          ' if BTN_PLUS press
        BTN_PLUS = 0                ' clear switch flag
        IF COUNTER>=255 THEN        '
          COUNTER=0                 '
        ELSE                        '
          COUNTER=COUNTER+1         '
        ENDIF                       '
        GoTo SUB_ADJ_1MDLoop        ' refresh displayed count
      ENDIF                         '
      IF BTN_MINUS=1 THEN           ' if BTN_MINUS press
        BTN_MINUS=0                 ' clear switch flag
        IF COUNTER<=0 THEN          '
          COUNTER=255               '
        ELSE                        '
          COUNTER=COUNTER-1         '
        ENDIF                       '
        GoTo SUB_ADJ_1MDLoop        ' refresh displayed count
      ENDIF                         '
      IF BTN_PREV=1 THEN            ' if BTN_PREV press
        BTN_PREV=0                  ' clear switch flag
        WRITE bMenuPos,COUNTER      ' update EEPROM
        RETURN                      ' exit
      ENDIF                         '
      GoTo SUB_X_1MLoop             ' loop (for key press)
    RETURN                          '
    Have fun. Cheerful regards, Mike
    Last edited by Mike, K8LH; - 1st August 2010 at 22:16.

  20. #20
    Join Date
    Jul 2003
    Posts
    2,405


    Did you find this post helpful? Yes | No

    Default

    Mike,

    I would suggest delegating switch debounce/management to the ISR
    With his switch inputs on RB7-RB4, how would you go about that without waiting for a switch release, and then clearing the int-on-change flag bit?

    Wouldn't releasing the switch re-trigger the interrupt?
    Regards,

    -Bruce
    tech at rentron.com
    http://www.rentron.com

  21. #21
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Bruce View Post
    Mike,

    With his switch inputs on RB7-RB4, how would you go about that without waiting for a switch release, and then clearing the int-on-change flag bit?

    Wouldn't releasing the switch re-trigger the interrupt?
    Oh no, I wouldn't use IOC interrupts in this application Bruce. Bouncing would be problematic. I would use periodic timer interrupts and poll the switches as in the example in post #15. And since OP is going to include RTC or Stopwatch functionality, periodic timer interrupts make sense.

  22. #22
    Join Date
    Jul 2003
    Posts
    2,405


    Did you find this post helpful? Yes | No

    Default

    Oh! I see what you meant now. Good idea.

    Newer types like the 16F193x series IOC are a lot easier to use. These don't require reading the port to clear IOC flag bits, and they can be set to trigger on any edge..
    Regards,

    -Bruce
    tech at rentron.com
    http://www.rentron.com

  23. #23
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default yes, success at last.

    thanks guys, all this ideas work! now, all i have to do is implement the rtc and beeper, which are ready to be merged with this code.

    once again, thanks.

    ps; not that im complaining, but is there any other menu builder app in picbasic?
    NAG CON WIFE!
    WIFE VAR MOOD

  24. #24
    Join Date
    Aug 2010
    Posts
    4


    Did you find this post helpful? Yes | No

    Default antirrebotes

    yo he implementado esto y funciona correctamente
    Attached Files Attached Files

  25. #25
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    hi, again, im trying this piece of code, from a previous post
    Code:
    RB_SUBMenuLoop:
    ' BTN_PLUS is the Next Choice button
       IF BTN_PLUS=1 THEN
          BTN_PLUS=0
          ...
       ENDIF
    ' BTN_PREV is the Exit button
       IF BTN_PREV=1 THEN
          BTN_PREV=0
          RETURN
       ENDIF
    ' BTN_NEXT is the Goto SubMenu Choice button
       IF BTN_NEXT=1 THEN
          BTN_NEXT=0
          ...
       ENDIF
    GoTo RB_SUBMenuLoop
    then i have this code scanning port b, through a 1ms interrupt:
    Code:
    Changed = PortB ^ OldPort
             IF Changed.4 = 1 THEN     ; Pick your pin
             OldPort = PortB
             BTN_PLUS = 1
             ENDIF
    this code is incrementing one at a time, which is what i want, but the only problem is that it is incrementing on a rising and falling edge of a button push. so if i push the btn_plus, bmenupos increments by 1, and if i release it, bmenupos increments again.

    i want it to increment only on the rising edge, not on the falling edge. any ideas?
    NAG CON WIFE!
    WIFE VAR MOOD

  26. #26
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    When you exclusive-or "new" and "old" a resulting 1 bit indicates a change of state (either a new hi or new press, or a new lo or new release). You need to filter these "change" bits. If you AND the "change" bit with the "old" bit you filter out the "new press" bits and you're left with "new release" bits. If you AND the "change" bit with the "new" bit you filter out the "new release" bits and you're left with the "new press" bits.

    Hope this helps.

    Cheerful regards, Mike

    Code:
    '
    '  swnew  ____-----_____-----_____   new switch sample (positive logic)
    '  swold  _____-----_____-----____   switch state latch
    '  delta  ____-____-____-____-____   changes, press or release
    '  newhi  ____-_________-_________   new press bits
    '  newlo  _________-_________-____   new release bits
    '
        swnew = PortB                  ' sample active hi switches
        delta = swnew ^ swold          ' changes, press or release
        newhi = delta & swnew          ' get "new press" bits
        newlo = delta & swold          ' get "new release" bits
        swold = swnew                  ' update switch state latch
        IF newhi.4 = 1 THEN            ' 
          BTN_PLUS = 1                 '
        ENDIF

  27. #27
    Join Date
    Dec 2009
    Location
    Kalamazoo
    Posts
    42


    Did you find this post helpful? Yes | No

    Default

    that is just great. thanks for pointing me to the right direction, Mr Mike. its all fixed now.

    cheers
    NAG CON WIFE!
    WIFE VAR MOOD

  28. #28
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by DDDvvv View Post
    that is just great. thanks for pointing me to the right direction, Mr Mike. its all fixed now.

    cheers
    You're very welcome Sir.

    Please note that the previous example can be optimized a bit. Since you're using the "new press" state as the action state in your program you don't need the "newlo" variable and its assignment statement. You can also retask the "swnew" variable and eliminate the "newhi" variable. Some XOR trickery is used to update the "swold" switch state latch to the new PortB pattern.

    Code:
    '
    '  swnew  ____-----_____-----_____   new switch sample (positive logic)
    '  swold  _____-----_____-----____   switch state latch
    '  swnew  ____-____-____-____-____   changes, press or release
    '  swnew  ____-_________-_________   new press bits
    '
        swnew = PortB                  ' sample active hi switches
        swnew = swnew ^ swold          ' changes, press or release
        swold = swold ^ swnew          ' update switch state latch (swold = new PortB)
        swnew = swnew & swold          ' get "new press" bits
        IF swnew.4 = 1 THEN            ' if RB4 "new press"
          BTN_PLUS = 1                 ' set flag for main
        ENDIF
    With your 1-msec interrupt intervals you might consider adding a piezo speaker and code for "new press" beeps;
    Code:
    '
    '  beep task (32-msec 500-Hz beep using 1-msec interrupts)
    '
        IF beepctr > 0 THEN            ' if beep task running
          PortB = PortB ^ spkrpinmask  ' toggle piezo speaker pin
          beepctr = beepctr - 1        ' decrement beep msec counter
        ENDIF
    '
    '  swnew  ____-----_____-----_____   new switch sample (positive logic)
    '  swold  _____-----_____-----____   switch state latch
    '  swnew  ____-____-____-____-____   changes, press or release
    '  swnew  ____-_________-_________   new press bits
    '
        swnew = PortB                  ' sample active hi switches
        swnew = swnew ^ swold          ' changes, press or release
        swold = swold ^ swnew          ' update switch state latch (swold = new PortB)
        swnew = swnew & swold          ' get "new press" bits
        IF swnew.4 = 1 THEN            ' if RB4 "new press"
          BTN_PLUS = 1                 ' set flag for main and
          beepctr = 32                 ' task a new press beep
        ENDIF
    If you experience bounce problems with a 1-msec sampling interval you might consider adding a counter and executing the switch sample code at some interval between 16 and 32 msecs.

    BTW, assembly language "parallel switch state logic" is incredibly efficient (6 or 8 words);

    Code:
    ;
    ;  wreg  ___-----_____-----____   new switch sample (positive logic)
    ; swold  ____-----_____-----___   switch state latch
    ;  wreg  ___-____-____-____-___   changes, press or release
    ;  wreg  ___-_________-________   filter out new release bits
    ; flags  ___-----------________   toggle sw flags for main
    ;
            comf    PORTB,W         ; sample active lo switches
    ;       movf    PORTB,W         ; sample active hi switches
            andlw   b'11110000'     ; on RB7..RB4 pins
            xorwf   swold,W         ; changes, press or release
            xorwf   swold,F         ; update switch state latch
            andwf   swold,W         ; filter out "new release" bits
            skpz                    ; new press? no, skip, else
            bsf     beepctr,5       ; task a "new press" beep
            xorwf   flags,F         ; toggle sw flags for main

    Cheerful regards, Mike

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts