Shop OBEX P1 Docs P2 Docs Learn Events
Need to debounce the counter input - New user programming in C needing help — Parallax Forums

Need to debounce the counter input - New user programming in C needing help

SparkoSparko Posts: 8
edited 2015-04-14 15:28 in Propeller 1
I now have a counter that works. I monitor PIN0 , that I have held high using a 100K resistor between Vin and Pin0. When I ground Pin0 it realizes a negative going edge and counts. The only trouble is that multiple counts always occur.

The normal pulse to be counted is low for 100ms.

I have put in a loop that checks for a change in the pulse condition after 50ms and this accomplishes the debounce fine but ties up the main program for the 50ms.

Please give me advice on the best way to debounce input for an event counter like in this kind of application.

Thanks in advance.

Sparko

Comments

  • prof_brainoprof_braino Posts: 4,313
    edited 2015-04-13 20:39
    which counter command are you using? can we see the code?
  • jmgjmg Posts: 15,173
    edited 2015-04-13 21:09
    Sparko wrote: »
    Please give me advice on the best way to debounce input for an event counter like in this kind of application.

    The best way is with an External logic gate Schmitt + RC filter ( 1G17 etc) - HW sets the filter time constants, and no SW fixups are needed.

    A SW patch approach would be to limit the count rate to (eg) +1 every 100ms, and for that you just check the Counter at some convenient polling rate, and ensure over 100ms it is forced to either +0 or +1
    Effectively the Counter HW is used as an edge(s) detector, and you advance in SW at some slower rate.
  • LeonLeon Posts: 7,620
    edited 2015-04-14 00:57
    This is the classic work on switch debouncing: http://www.eng.utah.edu/~cs5780/debouncing.pdf
  • SparkoSparko Posts: 8
    edited 2015-04-14 06:26
    I certainly appreciate the feedback. The choices are hardware or software.

    This device is being developed to count coin pulses from various already installed coin drops. Bounce is anticipated but due to the variety the exact duration is unknown. However not more than about 10ms.

    Leon - Thanks I will consider RC circuit in the article first.

    jmg - Thanks - Am I correct in thinking that running the counter and SW debounce in a separate cog allows the main program to proceed at will (asynchronous from the counter) and read the count and zero the count any time it is convenient? If this is the case is there any advantage to using a counter in the edge detecting mode at all?

    professor - Thanks Here are the sections of code involved below. Am I correct that if I put this in a cog independent of the main the main can proceed and check and zero the count at any time that is convenient? Am I correct in thinking there is no advantage to using a counter at all? If there is an advantage I could I use the debounce part of this code to feed the neg edge detecting counter?

    ...................................

    #include "simpletools.h" // Include simpletools

    int main() // main function

    {
    int buttonchange = 0;
    int pulses = 0; // initialize pulse counter
    while(1) // Endless loop
    {
    while(buttonchange == 0) // Loops until a valid pulse is realized. Pulse detection
    {
    buttonchange = 0; // a change is the switch position from zero to one will record a change
    int button1 = input(0); // initial button position
    pause(50); // wait 10ms to get beyond any bounces
    int button2 = input(0); // button position after pause
    if (button1 != button2 && button1 == 1) // button has made a complete transition without bounces
    { buttonchange=1;}

    }
    ++pulses;

    pause(100); // Wait 0.1 second before repeat - a subsequent pulse is not possible for the next 0.1 second
    print("pulses = %d\n", pulses); // Just for testing the program
    buttonchange = 0; // Return to pulse detection loop

    }

    }
  • ReinhardReinhard Posts: 489
    edited 2015-04-14 07:22
    Maybe you can use waitpeq or waitpne (see propeller.h) in an extra cog and write countervalue into a global variable.
    Of course you synchronize the reading in main with global flag or semiphore.

    So you need less power than with polling.
  • DomanikDomanik Posts: 233
    edited 2015-04-14 08:12
    Looking at a closure on a scope it seems the bouncing occurs rapidly as the metal parts flex and the contacts bounce off each other. The bounces occur quickly but the wait period for stability is unpredictable due to the mechanical and electrical characteristics of the switch. 10 ms might be enough except when ---?

    Once the bouncing stops the switch is probably stable and the reading can be used. I would start the 10ms wait timer after the detected bouncing stops for a typical single bounce cycle. So it might be that using the propeller to look for a lack of bounces / transitions for a short period of time, then waiting a little longer, would be a way optimize the overall wait time to take a reading.

    This is a real tricky problem that appears to be solved until the one in a hundred (or thousand) unpredictable-unrepeatable hiccup occurs. Temperature will change the relative position of opposing contacts and change the nature of the beast too.
  • prof_brainoprof_braino Posts: 4,313
    edited 2015-04-14 13:15
    Toggle and latch at the first transition detected, and ignore all bounces/transitions for 500ms. Would this be simplest? 1 detect and 1 counter of 500ms.
  • jmgjmg Posts: 15,173
    edited 2015-04-14 13:20
    Sparko wrote: »
    jmg - Thanks - Am I correct in thinking that running the counter and SW debounce in a separate cog allows the main program to proceed at will (asynchronous from the counter) and read the count and zero the count any time it is convenient? If this is the case is there any advantage to using a counter in the edge detecting mode at all?

    Yes, a separate COG can move the result to a simple async, anytime, HIUB read.

    However, you may not need to go that far - the Counter may seem almost 'not needed' but it does give HW edge detection, so you know you do not miss any edges, the problem shifts to what to do about the 'bonus edges' :)

    With a counter, you can check that quite infrequently in the main SW, and then apply your simpler rate filter.
    You do not have to worry about the duty cycle of the trigger signal, and it will work on opto-interruptor style signals too.

    Psuedo code is along the lines of this, called slower than longest bounce time, and faster than your known shortest pulse-pulse time.
    tmp = PHSA-LastCtrValue // sample HW value [B]in one place[/B]
    IF tmp > 0 THEN
        INC(SwCounter)     // turns N edges into one edge
        LastCtrValue = LastCtrValue + tmp  // Resync == PHSA sample above
    ENDIF
    
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-04-14 14:20
    Put it in a separate cog (if there is a spare available).This removes the counting and debouncing from your main code. This is the advantage of the prop after all.

    I agree with jmg's concept. Wait for the first transition with a waitpeq/waitpne as this lowers power consumption. You could then fire off your counter to count the transitions, and monitor the transitions until they stop counting for a period of time (use the CNT to time the steady state time). Each time you get a toggle, reset the time.
    It is quite simple and lends itself to be done in pasm (or spin) as its own object running in its own cog. Then its only a matter of that object updating a hub variable each time a valid count is seen (its going to be quite slow - how fast can you put a coin in a slot - not that fast in reality).
  • Skywise711Skywise711 Posts: 37
    edited 2015-04-14 15:28
    Here's some sample code, with comments. It's in Spin, but you should be able to translate the concept to C just fine.

    As others have said, you can throw this in another cog if you like.

    It works by having a timer keep track of the system counter. It only checks pin states if the counter has elapsed past the debounce time. Then it uses a flag to track the previous state of the pin.

    As written it looks for pin high on a pull-down resistor. To use a pull-up like you said you need, just move the action (incrementing the counter) to the "if pin == 0" block.

    Brian
    CON
      _clkmode = xtal1 + pll16x     
      _xinfreq = 5_000_000
    
      CR      = $0D
      LF      = $0A
      NULL    = $00
      CLR     = $00
      HOME    = $01
    
      SwitchPin    = 0
      DebounceTime = 80_000_000/20  '50ms at 80Mhz
      
    VAR
      LONG  SwitchCounter
      LONG  DebounceTimer
      LONG  pinflag
      LONG  LoopCounter
      LONG  pin
      LONG  debounce
    
    OBJ
      pst           : "Parallax Serial Terminal"
    
    
    
    PUB Main
      dira[SwitchPin] := 0  
    
      pst.Start(115200)
      waitcnt(cnt+80_000_000)     'allow time to enable terminal
    
      pst.char(CLR)               'clear screen
      debounce := cnt             'initialize debounce timer to current clock
      pinflag := 0                'initialize debounce flag
    
      'Main Loop
      repeat
        'print some stuff on terminal screen
        pst.char(HOME)
        pst.str(string("Program Loop: "))
        pst.dec(LoopCounter)
        pst.char(CR)
        pst.str(string("Switch Count: "))
        pst.dec(SwitchCounter)
        pst.char(CR)
        pst.str(string("Chip Counter: $"))
        pst.hex(cnt,8)
    
    
        'routine to detect one and only one switch pulse
        'without pausing the program during the debounce time
    
        if cnt > debounce                   'has it been longer than the debounce time?
          debounce := cnt + DebounceTime    ' then set debounce counter to new debounce time
    
          pin := ina[SwitchPin]                  'read the pin
    
          if pin == 1                            'is the pin high?
            if pinflag == 0                      ' and the pin was low before? 
              pinflag := 1                       '  then set pin flag to know it's been high
              SwitchCounter++                    '  and increment the switch counter
    
          if pin == 0                            'is the pin low?
            if pinflag == 1                      ' and the pin was high before?
              pinflag := 0                       '  then reset pin flag to know it's been low
              'SwitchCounter++                    'USE CODE HERE FOR PULL UP RESISTOR
    
        LoopCounter++                            'just to show the loop is running
    
    
    
Sign In or Register to comment.