7 steps to reusable debounce (and other) code
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, though, it is obvious to me that these implementations are not meant for reuse. The implementations and strategies these sites present often lack scalability, portability, and the ability to readily debounce multiple buttons. They are also tightly coupled to the hardware on which they were designed to run. These attributes need not be the case. There are seven simple steps to creating 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 result, please download the example source code HERE
Step #1 – Define the software architecture
Defining a software architecture should be the first item on a software developer's “to do” list. The software architecture acts as a blueprint 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 that of Figure 1. The diagram shows that this algorithm will consist of four interacting pieces that should each be modular and separate.
Figure 1 - Layered software architecture for a button
Tip #2 – Outline the API
An application programming interface (API) is a great way to start developing software that is to be reusable. The API defines functions and 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: an initialization routine, a debouncing function, and a state machine. A developer might also be interested in having a fourth function, for retrieving a button's debounced state. An example button API can be seen in Figure 2.
Tip #3 – Identify the button states
Every application will have different requirements on what states a button may occupy. Very simple applications might only 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 time window. A properly defined API doesn’t care about these different states but can easily handle all the 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 Figure 3.
Figure 3 - An example of possible button states for an application
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. The simplest is to read the pin state, delay for some debounce period, and then read the pin again. Using the delay method isn’t particularly efficient, however, nor is it always correct. More 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 you choose 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, however, always results in having to rewrite the code over and over. Figure 4 shows some example flowcharts for some common button debouncing algorithms.
Figure 4 - Button debounce implementation flowcharts
Tip #6 – Now, 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 developer's language of choice. As mentioned before, example code associated with the seven steps can be downloaded HERE. An example of the Button_Debounce function can be seen in Figure 5.
Figure 5 - Button_Debounce implementation
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 being in the pressed state, and turn the LED off otherwise.
Writing code that is reusable doesn’t need to take 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 than this upfront investment. These steps have demonstrated how a reusable button algorithm can be developed but these same steps and strategies can also be applied to nearly any component that a developer would use or develop for an embedded system.
Jacob Beningo is a Certified Software Development Professional (CSDP) whose expertise is in embedded software. He works with companies to decrease costs and time to market while maintaining a quality and robust product. Feel free to contact him at firstname.lastname@example.org, at his website www.beningo.com, and sign-up for his monthly Embedded Bytes Newsletter here.