A quick google search reveals that there are over 212,000 websites that examine how to debounce a button. After examining a number of them it is obvious that these implementations are not meant for reuse. The implementations and strategies often lack scalability, the ability to debounce multiple buttons easily and portability. They are tightly coupled to the hardware on which they were designed to run. This need not be the case. There are seven simple steps that can be followed to create a reusable debounce algorithm that can be used in nearly any embedded system. To follow along with these steps or to take advantage of the output of them, please download the example source code that you can find below:
Step #1 – Define the software architecture
Defining a software architecture should be the first item on a software developers “to do” list. The software architecture acts as a blue print that tells the developer exactly what is being developed. Skipping the architecture phase is like deciding to build a skyscraper on-the-fly with no forethought on how tall the building would be or how many floors it would have. For a button debounce algorithm a very simple layered architecture can be used similar to the image shown below.
The diagram shows that this algorithm will consist of four interacting pieces that should be modular and separate.
Tip #2 – Outline the API
An application programming interface (API) is a great way to start developing software that is reusable. The API defines functions, objects and provides the building blocks for developing an application. The API allows the lower level details, the implementation, to be abstracted out. For a switch debounce algorithm there are three primary interfaces that a developer would be interested in. The interfaces would consist of an initialization routine, a debouncing function and then a state machine. A developer might also be interested in having a fourth function for retrieving the debounced state a button. An example button API can be seen in the image below:
Tip #3 – Identify the button states
Every application will have different requirements on what states a button may be in. Very simple applications will have NOT_PRESSED and PRESSED. A complex application may have far more states such detecting when a button is released or how many times it has been pressed within a defined window of time. A properly defined API doesn’t care about these different states but can easily handle the different possible states in the implementation or through the configuration layer. An example enumeration of the possible button states for a typical application can be seen in the image below:
Tip #4 – Select the implementation type
Defining an API is a great way to abstract out the implementation details of how a button debounce algorithm works but there comes a time when a developer needs to decide what those details will be. There are many different ways to debounce a button in software. For example, the simplest is to read the pin state, delay for the debounce period and then read the pin again. Using the delay method isn’t particularly efficient nor is it always correct. Common implementations for a button debounce will periodically sample the pin and only after x samples have consecutively read the same will the button be considered debounced. The implementation can be dependent upon the initial requirements and the target application.
Tip #5 – Flowchart the design
The easiest way to get a handle on the implementation details is to flowchart the implementation before ever writing a single line of code. Flowcharting allows a developer to wrap their mind around the code that they are about to write. The temptation always exists to bypass flowcharting and instead jump straight to writing code. Writing code first always results in having to rewrite the code over and over. The image below shows some example flowcharts for some common button debouncing algorithms:
Tip #6 – Write the code
Writing the embedded software should only occur after the API and the flowchart have been developed. With the flowchart in hand, writing the embedded software becomes as easy as translating the flowchart into the language of the developers chose. In the spirit of developing a reusable button debounce algorithm, example code that is associated with these steps can be downloaded HERE. An example of the Button_Debounce function can be seen below:
Tip #7 – Test and validate
Finally after working so hard on the previous six steps, it is now time to test and validate that the selected implementation and design actually work. One way to test the debounce algorithm is to pick a low cost development kit, such as an STM32 Nucleo board, and write some application code that will debounce the onboard button and turn an LED on. For a test keep it simple and turn the LED on if the button is debounced as pressed and turn the LED off otherwise.
Writing code that is reusable doesn’t need to be taking more time or cost more than code written to be a one off. Spending a little bit of time up front to think through an API, select an implementation and flowchart the design will save more time on the back-end then the upfront investment. These steps have demonstrated how a reusable button algorithm can be developed but these same steps and strategies can be applied to nearly any component that a developer would use or develop for an embedded system.
Good stuff, but after saying it’s important to do a flowchart, why does the code example not match the flowchart?
Probably from minor changes over time.
I suspect I probably flowcharted, wrote the code, debugged the code and just didn’t update the flowchart. It’s not meant to be production intent but give some starter examples.
Perfection is the death of innovation!
Thanks for the comment and pointing this out. I appreciate it!
That’s all you need.
These are good …. but I like my code far better. Much more flexible, reusable and documented (and easier to read!)
Agreed, Method 1 is a “quick and dirty” debounce algorithm one might use in say bread-boarding up a circuit and testing proof of concept… not great, but could be suitable in some places. Method 2 is heading in the right direction, but if I’m not mistaken seems to have a flaw in it… switch release is not being debounced at all. To be sure, my perspective is that debounce routines are used equally as much on noisy digital inputs as they are on actual physical switches… assume there could be some noise on the signal in both logic states, as well as during transitions. Flow chart could be amended by having separate press and release debounce counters. Alternatively, and an improvement, would be to have a single up/down debounce counter, with separate thresholds for “pressed” and “not-pressed” to introduce hysteresis.
I see that “Release” is also debounced in the code. But, with same sample time as “Press”. As Jacob pointed out in first comment, this is just a kick-off. You can add “else if…” and INNOVATE as per your architecture and flow defined.