Shop OBEX P1 Docs P2 Docs Learn Events
Debouncing QuickStart touch pads — Parallax Forums

Debouncing QuickStart touch pads

Ron CzapalaRon Czapala Posts: 2,418
edited 2014-03-13 05:16 in Propeller 1
I have looked at many debouncing forum threads (google search) and tried several approaches including those from Beau Schwabe, JonnyMac, Duane Degn, etc
but I still get multiple hits almost every time.

Someone mentioned bitwise OR in debouncing but I could not find any posted code.

If anyone has a "foolproof" approach, I would love to see the code!

Thanks,
Ron

Comments

  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 11:24
    jazzed wrote: »

    Thanks jazzed,

    But that was one of first ones I tried but I just did it again and got 25 lines of ("Cogs used: 5") in the terminal window with a quick touch.

    My program just counts available unused cogs when pad one is touched.
  • kwinnkwinn Posts: 8,697
    edited 2014-03-10 11:59
    Don't know if it will work with the QS touch pads but I loop until the same key has been detected several times in a row. For spin 8 times seems to work, and for PASM either 80K times or 8 times at 1 mS intervals.
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 12:57
    kwinn wrote: »
    Don't know if it will work with the QS touch pads but I loop until the same key has been detected several times in a row. For spin 8 times seems to work, and for PASM either 80K times or 8 times at 1 mS intervals.

    I think that's what the Touch Buttons PASM/Spin code does - looks like it checks 31 times
    CON
      SAMPLES       = 32            ' Require 32 high redings to return true
    
    VAR  
      long  Results
    
    PUB State | Accumulator
      Accumulator := Results        ' Sample multiple times and return true
      repeat constant(SAMPLES - 1)  '  if every sample was highw
        Accumulator &= Results
      return Accumulator
    
  • edited 2014-03-10 13:06
    I go on the assumption that a normally open push button won't push itself down so, if it indicates down, it's because the user pressed it down. At that point you can go ahead and do the action. The debounce routine then comes AFTER the action.
    loop
      wait for button down
      toggle led_out pin
      waitcnt( ( clkfreq >> 4 ) + cnt )  '1/16 second delay / stabilize
      wait for button up
      waitcnt( ( clkfreq >> 4 ) + cnt ) '1/16 second delay / debounce
    

    This approach gives good response. If your action takes long enough you can eliminate the first 1/16 second delay / stabilize pause. Some switches seem to bounce around for ages before they stabilize so the 1/16 second period may have to be lengthened.

    Sandy
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 15:56
    I'm sure there are better ways but this one works for me.
    'CON { QS_ButtonScan.spin }
    '    { ButtonStart - responsible for the touch pad buttons }
    VAR                  
      LONG Buttons
      LONG ButtonCog
      LONG Stack[ 16 ]                 ' define my stack
    
    OBJ util:"QS_Utilities"
    
    PUB start( ButtonAdr )                  ' start this in a new cog
      Buttons := ButtonAdr                  ' save status
      if ButtonCog
        cogstop(ButtonCog-1)                ' no cog available
      ButtonCog := cognew(ButtonScan, @Stack) + 1
      
    PUB ButtonScan | B, B1                   ' local variables
      dira [0..7] := %11111111               ' all pad pins outputs    
      outa [0..7] := %11111111               ' all pad pins high   
      'main loop - scan the buttons   
      Repeat                                 ' loop forever
         Repeat B from 0 to 7                ' QS LEDS are pins 0-7
           dira [B] := 1                     ' make pin an output 
           dira [B] := 0                     ' make pin an input
           util.WaitMS(4)                    ' short delay for some decay
           B1 := ina[B]                      ' read the pad
           if B1 == 0                        ' 0 here means pressed
              BYTE[Buttons] |= |< B          ' set bit if pressed   
           else                    
              BYTE[Buttons] &= !|< B         ' clear bit if not
    


    demo program to test touchpads
    CON
    { LED PingPong for the QuickStart board }    
      _CLKMODE=XTAL2 
      _xinfreq = 5_000_000
      Right      = 1                ' Direction
      Left       = 2                ' Direction
      ButtonR    = 0                ' Right Button pin
      ButtonL    = 7                ' Left Button Pin
      
    OBJ Tpad: "QS_Buttons"       'this loads the Touchpad driver as alias Touch.
    
    VAR
      LONG MS001
      BYTE buttons
      BYTE PointsRight, PointsLeft,First, Last  
      BYTE BallSpeed, Direction
    
    PUB SetUp
      Tpad.start( @buttons )        ' start touchpad driver
      MS001 := CLKFREQ / 1_000      ' define 1 millisec 
      outa[ 16..23 ] := $00         ' all LED pins off    '
      dira[ 16..23 ] := $FF         ' all LED pins outputs   '
      BallSpeed := 150 
      Direction := Left
      PingPong                      
                                                                  '      
    PUB PingPong
     ClearAll
     Repeat
        BallMove
        If Direction == Right                    ' to the Right
           if byte[Buttons AND %00000001] == 1   ' hit it? bit 0
              ChangeDirection                    ' Oh Yea!
           else                                  
              ScoreRight                         ' missed it by that much..
        elseif Direction == Left                 ' Not right must be left 
           if byte[Buttons AND %10000000] == 1   ' hit it?  bit 7
              changeDirection                    ' yup!
           else                                                        
              ScoreLeft                          'aced him again!
     
    Pri ScoreRight
        PointsRight++
        ShowScores
        WaitMS(1000)
        
    Pri ScoreLeft
        PointsLeft++
        ShowScores
        WaitMS(1000)
        
    Pri ShowScores  | X
        ' do we have a winner? 
     if PointsRight == 7         ' I won!
         Repeat 20
            outa [16..23] := %11111111   
            waitMS(50)
            outa [16..23] := %00000000
            waitMS(50)
         ClearAll
         WaitMS(2000)             ' wait a sec before serving again
         changeDirection          ' change Server
     elseif PointsLeft == 7      ', no, I won! 
         Repeat 20
              outa [16..23] := %11111111
              waitMS(50)
              outa [16..23] := %00000000
              waitMS(50)
         ClearAll
         waitMS(2000)              ' wait a sec before serving again
         ChangeDirection           ' change server
     elseif 1 
       ' no winner - show the scores - keep playing
        If PointsRight >0
           Repeat 10
              Repeat x from 23-(PointsRight-1) to 23
                outa[x] ~~
              waitMS(50)
              outa [16..23] := %00000000
              waitMS(50)
        If PointsLeft >0
           repeat 10
              Repeat x from 16 to 16+(PointsLeft-1)
               outa[x] ~~            
              waitMS(50)
              outa [16..23] := %00000000
              waitMS(50)   
       
    Pri ClearAll
        PointsLeft  := 0
        PointsRight := 0
    
    PUB ChangeDirection
        If Direction == Right
           Direction := Left
        else
           Direction := Right   
    
    PUB BallMove | LED
        ' sets direction in terms of LED pin numbers
        If Direction == Left
           First:= 16
           Last := 23
        else
           First:=23
           Last :=16 
        ' now actually move the "ball"
        repeat  Led FROM First to Last
           outa [LED] := 1
           WaitMS(BallSpeed)
           outa [LED] := 0
           WaitMS(100)
    
    PUB WaitMS(W)                      'wait for W milliseconds
      WaitCNT ((W*MS001)+cnt)
    
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 16:47
    cavelamb wrote: »
    I'm sure there are better ways but this one works for me.
    ...

    Thanks cavelamb. I can sort of get it to work if I stick a 20ms wait in my loop but I guess what I'm trying to come up with is a routine that gives me a single hit when a pad is touched.

    I guess it needs to constantly compare the button states for a fixed period and only return a non-zero byte if button state bits are unchanged for the entire period but where it won't miss a button push (if that makes sense).

    Sort of works
      repeat
        outa[23..16] := Buttons
        if byte[Buttons AND %00000001] == 1          ' hit it? bit 0
          CountCogs
          WaitMS(20)                  
    
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 17:00
    Oh.

    I thought the same thing at first, but it's a bit more involved that that.

    Touchpad aren't switches.

    The trouble you are experiencing is not "bounce".

    It works like this:
    First the touchpads are "charged up" but writing 1s to the pins as outputs.
    Then the outputs are turned around to inputs.
    Next, a short delay (word from our sponsor) to let that charge decay off a bit.
    NOW we finally read the pin and check to see if it's touched or not.
    Lastly, if the pad returns 0 (pressed) that bit is set (1) in the return byte.
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 17:02
    I don't have code working for the followiing, but I'm thinking on it
    for a project ...

    I'd like to detect:
    Short press
    Long press
    Double tap
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 17:07
    cavelamb wrote: »
    Oh.

    I thought the same thing at first, but it's a bit more involved that that.

    Touchpad aren't switches.

    The trouble you are experiencing is not "bounce".

    It works like this:
    First the touchpads are "charged up" but writing 1s to the pins as outputs.
    Then the outputs are turned around to inputs.
    Next, a short delay (word from our sponsor) to let that charge decay off a bit.
    NOW we finally read the pin and check to see if it's touched or not.
    Lastly, if the pad returns 0 (pressed) that bit is set (1) in the return byte.

    I guess calling it bounce is technically not correct but the behavior is somewhat similar.

    What I'm looking for is (do I dare say this on a Propeller forum?:smile:) something like a hardware interrupt.
    Something that doesn't depend on long waits or give multiple hits for one brief touch.
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 17:10
    I wonder about using the WAITPEQ instruction - I've never messed with that before...

    Hmmmm - that can be problematic I see:

    http://forums.parallax.com/showthread.php/126265-WAITPEQ-with-timeout

    http://forums.parallax.com/showthread.php/99748-Forcing-exit-of-waitpeq-if-no-match-occurs
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 17:33
    I guess calling it bounce is technically not correct but the behavior is somewhat similar.

    What I'm looking for is (do I dare say this on a Propeller forum?:smile:) something like a hardware interrupt.
    Something that doesn't depend on long waits or give multiple hits for one brief touch.

    Thought that for a while too. But the Prop is "different".

    That's what multiple cogs are for.
    The touchpad routine could easily be started in another cog and left running.
    Then all we would have to do is read the button byte and take action.
    Small tight loop...

    Try that drive I posted.
    Shouldn't give you multiple hits.
    It runs pretty smooth for me.
  • pjvpjv Posts: 1,903
    edited 2014-03-10 17:34
    Cavelamb,

    Its actually quite a bit more complicated than what you said.

    Due to the conductivity of your finger, which can vary enormously, and the pressure of your !push! the decay time can vary greatly. Then, to top it all off, your finger also introduces a large 60 Hz AC signal into the node of unknown proportions.

    So to read the pads reliably, with various users and humidity conditions, requires a somewhat complicated algorithm. I am developing an adaptive approach, but its not done yet.

    Simple approaches such as timing may appear to work, but when users or conditions change, the results will vary. Hard long presses may give passable results, depending on your needs.

    Cheers,

    Peter (pjv)
    cavelamb wrote: »
    Oh.

    I thought the same thing at first, but it's a bit more involved that that.

    Touchpad aren't switches.

    The trouble you are experiencing is not "bounce".

    It works like this:
    First the touchpads are "charged up" but writing 1s to the pins as outputs.
    Then the outputs are turned around to inputs.
    Next, a short delay (word from our sponsor) to let that charge decay off a bit.
    NOW we finally read the pin and check to see if it's touched or not.
    Lastly, if the pad returns 0 (pressed) that bit is set (1) in the return byte.
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 17:40
    pjv wrote: »
    Cavelamb,

    Its actually quite a bit more complicated than what you said.

    Due to the conductivity of your finger, which can vary enormously, and the pressure of your !push! the decay time can vary greatly. Then, to top it all off, your finger also introduces a large 60 Hz AC signal into the node of unknown proportions.

    So to read the pads reliably, with various users and humidity conditions, requires a somewhat complicated algorithm. I am developing an adaptive approach, but its not done yet.

    Simple approaches such as timing may appear to work, but when users or conditions change, the results will vary. Hard long presses may give passable results, depending on your needs.

    Cheers,

    Peter (pjv)


    Yes, I know that, p.
    But the issue at hand is getting the guy "adapted" to the Propeller.

    This set up has given me good service so far, but you are probably right that someone else probably has a
    different "touch".
    I'd be really interested in in seeing your driver when you get it working.

    break

    Ron, if it's really important, you can add real switches to the pad pins and read them
    like real (bouncy) switches.
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 17:43
    cavelamb wrote: »
    Thought that for a while too. But the Prop is "different".

    That's what multiple cogs are for.
    The touchpad routine could easily be started in another cog and left running.
    Then all we would have to do is read the button byte and take action.
    Small tight loop...

    Try that drive I posted.
    Shouldn't give you multiple hits.
    It runs pretty smooth for me.

    Here is the code I used and it does give multiple hits - especially if I remove the 20ms wait in the repeat loop.
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 17:49
    That looks like exactly the same routine, Ron.
    repeat
        outa[23..16] := Buttons
        if byte[Buttons AND 000001] == 1          ' hit it? bit 0
          CountCogs
          WaitMS(20)
    

    How fast can you get your finger off of the pad?

    Looks like you need to FLAG that a toughpad has happened,
    and wait for it to be released before repeating the loop?
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 17:53
    cavelamb wrote: »
    ...
    Ron, if it's really important, you can add real switches to the pad pins and read them
    like real (bouncy) switches.

    I considered using real buttons
    I'm really just messing around and trying to familiarize myself with the QuickStart.

    I would probably use real buttons if I wanted a more reliable trigger...

    Thanks,
    - Ron
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 17:55
    cavelamb wrote: »
    That looks like exactly the same routine, Ron.
    repeat
        outa[23..16] := Buttons
        if byte[Buttons AND 000001] == 1          ' hit it? bit 0
          CountCogs
          WaitMS(20)
    

    How fast can you get your finger off of the pad?

    Looks like you need to FLAG that a toughpad has happened,
    and wait for it to be released before repeating the loop?

    I need to play around with it some more - maybe I can figure out a way to do what I want.
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 18:48
    Well, the way your code reads right now you have 20 ms to get off of the pad....

    or it repeats
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-10 19:06
    cavelamb wrote: »
    Well, the way your code reads right now you have 20 ms to get off of the pad....

    or it repeats

    Yeah - got to be a better way to approach this.
    I thought it might be neat to use the QuickStart (and it's touch pads) to set the date and time on a ChronoDot (DS3231 Real Time Clock) module.
    Using the pads to advance the hour/min/sec etc would tricky unless I can get a more controlled button action.

    http://forums.parallax.com/showthread.php/137879-Chrono-dot-interface?p=1073753&viewfull=1#post1073753
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 19:37
    [B]' untested[/B]
     repeat     outa[23..16] := Buttons     
       if byte[Buttons AND 000001] == 1 ' hit it? bit 0
          [B]repeat while byte[Buttons AND 000001] == 1[/B]    ' wait for release
          CountCogs     WaitMS(20)         ' when key press terminate
    

    or - there's that LONG PRESS thing that would be real handy here for auto-increment

    something like?
     repeat     
     outa[23..16] := Buttons     
       if byte[Buttons AND 000001] == 1 ' hit it? bit 0
          tp_start := cnt 
          repeat while byte[Buttons AND 000001] == 1    ' wait for release
          tp_end := cnt
          tp_time := tp_end - tp_start   ' but doesn't allow for cnt rollover
    
          if tp_time >= tP_long then
             'auto-bump the time
             'and accellerate?  - longer you hold it, the faster it goes
            else
              ' just inc/dec by onzies
        '
    
  • AlarusAlarus Posts: 48
    edited 2014-03-10 21:43
    Hi all.

    Maybe this will help.:smile:

    Alarus.
  • cavelambcavelamb Posts: 720
    edited 2014-03-10 22:19
    Nice.
    Thanks
  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2014-03-12 17:18
    A revelation of sorts. Not looking at this for a couple of days and then rethinking it, the answer was obvious and I can't believe I tried to make a simple problem so complicated.

    DUH!

    Saving the buttons state byte into a variable (e.g. prevState) and only triggering the desired routine when the state changes pretty much eliminates the repeat action - as long as you make a firm, quick touch to the pad.

    Holding your finger on the pad does not constitute a change either until you let go.

    If you brush the pad or tentatively make contact you can get a repeat.
    VAR
      byte btnState, prevState
    
      Buttons.start(_CLKFREQ / 100)                         ' Launch the touch buttons driver sampling 100 times a second
    
      dira[23..16]~~                                                  ' Set the LEDs as outputs
      repeat
        btnState := Buttons.State
        outa[23..16] := btnState  'Buttons.State '          ' Light the LEDs when touching the corresponding buttons
        if btnState <> prevState                                  ' has button state changed?
          prevState := btnState
          bitStatus := btnState & (|< 0)                        ' get bit 0 
          if bitStatus                                                   ' if high 
            CountCogs 
    
    
  • cavelambcavelamb Posts: 720
    edited 2014-03-12 20:37
    I usually find things like that right after I post them. :(

    Try that driver from Alarus.
    It's small and tight and works right nice.
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2014-03-12 23:45
    It nice to see there is a good piece of code to resolve this. The other alternative is to use a 'one shot' chip between the button and the Propeller.

    Try the CD4047 if you really want to add hardware. But I'd prefer to not do so.
  • cavelambcavelamb Posts: 720
    edited 2014-03-13 05:16
    It nice to see there is a good piece of code to resolve this. The other alternative is to use a 'one shot' chip between the button and the Propeller.

    Try the CD4047 if you really want to add hardware. But I'd prefer to not do so.

    That's kinda what he did, but in software...
Sign In or Register to comment.