Tuesday, March 31, 2015

Buttons, pushbuttons and debouncing story




     Today subject might look a trivial one but is not. At all. I will try to keep it as easy
to understand as possible without too much "behind the scene" theory.

     Contact bounce is a common problem with mechanical and electromecanical switches, relays, solenoids, etc.
  Switch contacts are usually made of springy metals. When the contacts strike together, their momentum and elasticity act together to cause them to bounce apart one or more times before making steady contact. The result is a rapidly pulsed electric current instead of a clean transition from zero to full current.

 But what happens during the bounce? Every bounce of every switch is different, a non deterministic function, a totally random event. Many produce only high speed hash till a solid one or zero appeared. Others can generate a serious pulse train of discernible logic levels that can give nightmares to the most sophisticated software debounce routine.

Ugly Bouncing Button - pulse train


   The effect is usually unimportant in power circuits, but causes problems in some analogue and logic circuits that respond fast enough to misinterpret the on‑off pulses as a data stream or change of the input logic level.

   They are many options and techniques that can solve this problem, some of them software based, some pure hardware. Few examples:

1. RC Filter Debouncing

   The RC filter slows down the signal transitions generated by the switch so the each button press gives one slow edge.  It is usually best to follow the RC filter stage with a Schmitt trigger buffer. Schmitt Trigger, which is simply an inverter equipped with hysteresis, further limits the switch bouncing.


RC Filter with Schmitt Trigger on output


2. Hardware - Dedicated Integrated Circuits

    Another way to debounce switches in hardware is to take advantage of application-specific integrated circuits.  Semiconductor manufacturers such as Maxim Integrated offer switch debouncers like the MAX6816 in very small packages, with built-in ESD protection. These parts cost a few dollars, depending on the purchasing volume. Digikey example : MAX6816. Although they are extremely easy to use, they are more expensive and offer less flexibility than a custom tailored solution.


3. Software routines, time delay 

   This is accomplished by inserting, depending of the pushbutton behaviour, a 20 to 50ms lockout delay in the function responding to port pin changes. The delay prevents the MCU from responding to the multiple switch transitions related to bouncing. The value should be choosen in such a way that is not affecting the true, real, switch transitions created by a true switch event. Maximum desired valid switching speed should also be determined for proper defining of the process.


   From my experience I think the best option is a combination of both Hardware and Software sides, so, what we will study it's a combined solution, a First order Low Pass Filter implemented in Hardware combined with Software Interrupt trigger routines on falling/rising edge. It's a simple setup and the results are great.

   Don't understand it wrong: it's no technique or solution for a piece of crap contact! What I have learned after some horror stories with switches and push buttons is that no matter the supplier, spend 5-10-15 minutes, take few from the batch, study their behavior and characterize them on the Oscilloscope! It's a truly life saver, no joke at all!


Let's continue with our choosen option.  

What we will need:
  •  CBDB Board
  •  USB adapter (take a look on Part 1 for details how to connect them together)
  •  RC Filter as described below 
  •  LED module
  •  Tested Push button

 For programming and uploading the driver and the software we will continue to use the LuaUploader as before.



 1. HARDWARE
 
First order RC Low pass filter

   One simple low-pass filter circuit consists of a resistor in series with a load, and a capacitor in parallel with the load. The capacitor exhibits reactance, and blocks low-frequency signals, forcing them through the load instead. At higher frequencies the reactance drops, and the capacitor effectively functions as a short circuit. The combination of resistance and capacitance gives the time constant of the filter:

   The break frequency, also called the turnover frequency or cutoff frequency (in hertz), is determined by the time constant:

or equivalently (in radians per second):

   This circuit may be understood by considering the time the capacitor needs to charge or discharge through the resistor:
  • At low frequencies, there is plenty of time for the capacitor to charge up to practically the same voltage as the input voltage.
  • At high frequencies, the capacitor only has time to charge up a small amount before the input switches direction. The output goes up and down only a small fraction of the amount the input goes up and down. At double the frequency, there's only time for it to charge up half the amount.

    Let's see on the Osciloscope how the value of the capacitor C can affect the rising time and what's the effect on the boucing event.
 
C = 10 nF

C = 100 nF

     So, how big should be the capacitor C? It really depends on the specific application and also a lot on the quality of the pushbutton contacts but for a decent quality human pressed pushbutton a value between 10nF and 100nF might be the answer.



2. SOFTWARE

2.1 Init 

  stat="No key press"    -- initial status
  outpin=3                       -- Select output pin - GPIO0
  gpio.mode(outpin,gpio.OUTPUT)
  inpin=6                        -- Select input pin - GPIO12
  gpio.mode(inpin,gpio.INT,gpio.PULLUP)  -- attach interrupt to inpin, activate internal PULLUP



2.2 Trigger functions

   This 2 functions below are the interrupt callback functions used for push button status detection. A small delay added to "smooth" even more the readout from the pushbutton. Most of the time not necessary as the RC filter combined with the smart change of the trigger activation from falling/rising edge is enough. As this time we use the "Ugly Bouncing Button" (see picture above - worst case scenario) extra delay is added.

2.2.1 Trigger functions

  function pressed()
      print("Key Pressed!")                       -- print status
      st7032i:lcd_setCursor(1,1)               -- set cursor pos LCD
      st7032i:lcd_write("Key Pressed!") 
-- print status LCD
       stat = "ON"        
      gpio.write(outpin,gpio.HIGH)           -- Led ON - Motion detected
      tmr.delay(10)                                      -- time delay for switch debounce                     
      gpio.trig(inpin,"up",released)            -- change trigger on falling edge
      return stat
  end
 

  function released()
       print("Key Released!")
       st7032i:lcd_setCursor(1,1)
       st7032i:lcd_write("Key Released!")
       stat = "OFF"
       gpio.write(outpin,gpio.LOW)   -- Led OFF
       tmr.delay(10)
      gpio.trig(inpin,"down",pressed)  -- trigger on rising edge
      return stat
  end



For testing, just save the code on ESP as 'button_test.lua', restart ESP and run:

        -- INIT LCD
         require('st7032i') -- I suppose you have already uploaded st7032i module from previous articles
         sda, scl = 2, 1
         st7032i:init_i2c(sda, scl)
         st7032i:init_LCD()

         require('button_test')                                --call for new created button Module
         gpio.trig(inpin,"down",pressed)  -- trigger activated on rising edge


Repeatedly quick switching with RC filter in place
 




     As you can see from the test above, the First order Low Pass Filter combined with Interrupt Trigger on falling/rising edge technique gives a very clean response over a broad range of key pressing speed/pressure.



2 comments:

Emlly said...
This comment has been removed by the author.
Anonymous said...

Not sure if I've left an (unapproved) comment before. I keep coming back to this...

Using tmr.delay() is really a bad idea as it stops the world and the networking stack (and other things) can fall over.
I keep a Gist with an IMHO improved version at https://gist.github.com/marcelstoer/75ba30a4aec56d1b3810

Post a Comment