In this tutorial, we’ll discuss how to implement STM32 Button Debouncing using different Button Debounce Techniques With STM32 microcontrollers. You’ll also learn how to use the STM32 systick timer to replace delay-based button (switch) debounce techniques. Without further ado, let’s get right into it!
Table of Contents
- STM32 Button Debounce Preface
- STM32 Button Debouncing Techniques
- STM32 Button Debounce Code Example
- Wrap Up
STM32 Button Debounce Preface
A push button is a mechanical switch that we use in all sorts of electronic projects. Ideally, a push button (switch) will connect two pins (leads) together when it’s held down which produces a perfect clean voltage transition on the input pin (High to Low or the opposite).
However, in the real world, this ideal behavior is not always how a push button behaves. The mechanical motion results in a contact bounce that therefore creates a jitter in the input voltage signal that we typically refer to as “switch bounce”.
I’ve captured an input pin signal while clicking a push button and here is how it looks on my oscilloscope. Those glitches in the digital signal are what we consider “button bouncing”.
I’ve clicked the push button hundreds of times in order to capture those bouncing glitches that you’ve seen in the oscilloscope’s screenshots above. This doesn’t mean that switch bouncing is a rare event, it does depend on the switch quality and some other factors. So it can be described as a random event.
Switch bouncing is a random event. This means it can happen and introduce aggressive sharp transitions to the input signal, or it can not happen at all. You need to press the push button multiple times to confirm the fact that this is a random event that doesn’t always happen. Yet we need to protect our systems against it.
STM32 Button Debouncing Techniques
There are so many techniques for button debouncing that you can use in your project and it just depends on the needs of your project. In this section, we’ll explore all possible button debouncing techniques as a general overview of what we’ll discuss in detail later on in this tutorial.
Button debouncing techniques can be categorized into the following two main categories:
- Hardware Button Debouncing Techniques
- Software Button Debouncing Techniques
Hardware button debouncing is to achieve button input debouncing with a hardware electronic circuit so that the microcontroller doesn’t have to deal with a noisy input and no extra software is required to handle such an issue in the first place.
- RC Filter Circuit
- Schmit Trigger Circuit
- Flip-Flop Circuit
- Special Debouncing ICs
- etc…
Software button debouncing, on the other hand, saves you extra hardware electronic parts and uses different algorithms and a bit of the CPU time to process the incoming non-debounced signal and clean it out before sending it to the designated software component that needs this input signal.
- Using delay
- Using timers
- Bit Shifting
- Moving average (digital filter)
- etc…
We’ll discuss a couple of software-based techniques in detail hereafter and see how to implement them with STM32 microcontrollers on real projects.
1. STM32 Button Debounce (With Delay)
The first and easiest software debouncing technique is to just insert a delay after reading the button input pin state. Then, we can read it again and make sure that the pin state didn’t change due to the button bouncing. In this way, we can guarantee that the input pin state change is not some random noise due to the button bouncing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * LAB Name: STM32 Button Debouncing (Delay) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint8_t BtnState = 0; int main(void){ ... BtnState = HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); if(BtnState == 1) { HAL_Delay(20); BtnState = HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); if(BtnState == 1) { // Toggle The Output LED Pin HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); } } ... } |
Using delay for button debouncing is not recommended at all. But if you’re prototyping something really quick and only need one button, this can suffice. However, I’d highly recommend that you keep going with this tutorial and check the other button debouncing techniques presented hereafter.
2. STM32 Button Debounce (With Delay + Counter)
This is a very similar method to the previous one, except the fact that it tries to make it less inefficient by reducing the delay amount and replacing that with a digital counter to count the consecutive times of the input pin state.
If the pin state has been high for 20 consecutive times, the button press event is confirmed. Otherwise, we’ll reset the counter and start all over again. With a delay of 1 ms, we expect the button to stay HIGH for 20 ms in order to register a confirmed button click event.
Here is the code example for STM32 Button Debouncing With Delay + Counter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/* * LAB Name: STM32 Button Debouncing (Delay + Counter) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint8_t BtnState = 0; uint8_t BtnDebounce_Counter = 0; int main(void){ ... BtnState = HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); if(BtnState == 1) { HAL_Delay(1); BtnState = HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); if(BtnState == 1) { BtnDebounce_Counter++; if(BtnDebounce_Counter >= 20) { // Toggle The Output LED Pin HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); BtnDebounce_Counter = 0; } } else { BtnDebounce_Counter = 0; } } ... } |
3. STM32 Button Debounce (Without Delay)
And now let’s get to some reliable debouncing techniques. First of which is the shown below which uses the built-in STM32’s SysTick Timer interrupt to keep track of elapsed time (SysTicks Counter Variable).
Instead of using delays, we’ll now keep track of time without forcing the CPU to busy wait with a delay function. Which was unnecessarily blocking the CPU, holding the system back, and potentially making it unscalable at all.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/* * LAB Name: STM32 Button Debouncing (Without Delay) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint32_t SysTicks = 0; uint32_t LastDebounceTime = 0; // The Last Time The Output Pin Was Toggled uint32_t DebounceDelay = 20; // The Ddebounce Time; increase it if the output still flickers uint8_t BtnState; // The Current Reading From The Input Pin uint8_t LastBtnState = 0; // The previous reading from The Input Pin int main(void){ uint8_t TempBtnReading; ... ... TempBtnReading = HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); // read the state of the switch into a local variable: if(TempBtnReading != LastBtnState) // If the switch changed, due to noise or pressing: { LastDebounceTime = SysTicks; // reset the debouncing timer } if((SysTicks - LastDebounceTime) > DebounceDelay) { if(TempBtnReading != BtnState) { BtnState = TempBtnReading; if(BtnState == 1) { HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); // Toggle The Output LED Pin } } } LastBtnState = TempBtnReading; ... ... } void SysTick_CallBack(void) { SysTicks++; ... } |
For this SysTick_Callback() handler function to work, you need to add a prototype for it in the main.h file as shown below:
1 |
void SysTick_CallBack(void); |
And we need also to call it within the stm32f1xx_it.c file, inside the SysTick_Handler() function as shown below:
1 2 3 4 5 6 7 8 9 10 11 |
void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); SysTick_CallBack(); // <--- Add This line of code! /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ } |
4. STM32 Button Debouncing (Moving Average Filter)
This is a very elegant button debouncing technique. And here is how it goes: We first call the BtnScan() function at fixed time intervals (let’s say 5 ms).
In the BtnScan() function, we create a circular buffer that holds the history of the push button states over the past n samples. Then, we apply a special case of the general FIR low-pass filter which is the “Moving Average Filter”. I’m not rigiously following the moving average filter’s formula, to be honest. The method shown in this example code is building on the same concept with a few tweaks to get even more robust results.
The buffer variable that holds the button states history is bit-wise manipulated to save on some space. So we don’t need to create an array of uint8_t variables. And we count the 1’s in the variable using the __builtin_popcount() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/* * LAB Name: STM32 Button Debouncing (Moving Average Filter) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint32_t SysTicks = 0; uint32_t T1 = 0, T2 = 0; uint16_t Btn1_States = 0; uint8_t TimeInterval = 5; // Button Scanning Time Interval int main(void){ ... T2 = SysTicks; if( (T2-T1) >= TimeInterval ) { BtnScan(); T1 = SysTicks; } ... } void BtnScan(void) { int SumStates = 0; Btn1_States <<= 1; Btn1_States |= HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin); SumStates = __builtin_popcount(Btn1_States); if(SumStates == 16) { HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); // Toggle The Output LED Pin Btn1_States = 0; } } void SysTick_CallBack(void) { SysTicks++; ... } |
For this SysTick_Callback() handler function to work, you need to add a prototpye for it in the main.h file and call it inside the SysTick_Handler() function within the stm32f1xx_it.c file as shown in the earlier example.
The __builtin_popcount() function is a standard built-in function in the GCC compiler itself. This function is used to count the number of 1’s bits in an unsigned integer variable.
5. STM32 Button Debouncing (Bit-Shifting)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/* * LAB Name: STM32 Button Debouncing (Bit-Shifting) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #include <stdbool.h> #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint32_t SysTicks = 0; uint32_t T1 = 0, T2 = 0; uint8_t TimeInterval = 5; // Button Scanning Time Interval int main(void){ ... T2 = SysTicks; if( (T2-T1) >= TimeInterval ) { if(BtnDebounce()) { HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); // Toggle The Output LED Pin } T1 = SysTicks; } ... } bool BtnDebounce(void) { static uint16_t Btn1_States = 0; Btn1_States = (Btn1_States<<1) | (!HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin)); return (Btn1_States == 0xFFF0); } void SysTick_CallBack(void) { SysTicks++; ... } |
For this SysTick_Callback() handler function to work, you need to add a prototpye for it in the main.h file and call it inside the SysTick_Handler() function within the stm32f1xx_it.c file as shown in the earlier example.
Note that in the example above, I’m using a pull-down resistor for the button’s input pin. In case you’re using a pull-up inverted logic, the debounce function logic will need a small change and will be as follows. (We just need to invert the digital pin state read result)
1 2 3 4 5 6 |
bool BtnDebounce(void) { static uint16_t Btn1_States = 0; Btn1_States = (Btn1_States<<1) | (HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin)); return (Btn1_States == 0x000F); } |
STM32 Button Debounce Code Example
In this example project, we’ll use the STM32 button debouncing technique #5 which was illustrated in the previous section. We’ll define a couple of GPIO pins, one for an output LED, and the other for an input push button (pull-down).
Upon each button press event, the LED should be toggled. And we’ll observe both signals on a DSO (digital storage oscilloscope) to see the effectiveness of this debouncing technique in filtering the noisy button signal.
Step #1
Open STM32CubeMX, create a new project, and select the target microcontroller.
Step #2
Configure a GPIO pin in output mode (LED).
Configure a GPIO pin in input mode with internal pull-down (Push Button).
Step #3
Go to the RCC clock configuration page and enable the HSE external crystal oscillator input.
Step #4
Go to the clock configurations page, and select the HSE as a clock source, PLL output, and type in 72MHz for the desired output system frequency. Hit the “ Enter” key, and let the application solve for the required PLL dividers/multipliers to achieve the desired clock rate.
Step #5
Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using.
STM32 Button Debouncing Example Code
Here is The Application Code For This LAB (main.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* * LAB Name: STM32 Button Debouncing (Bit-Shifting) * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #include "main.h" #include <stdbool.h> #define Button1_Pin GPIO_PIN_7 // The GPIO Pin Number of Button1 #define Button1_GPIO GPIOA // The GPIO Port of Button1 Pin #define LED1_Pin GPIO_PIN_6 // The GPIO Pin Number of LED1 #define LED1_GPIO GPIOA // The GPIO Port of LED1 Pin uint32_t SysTicks = 0; uint32_t T1 = 0, T2 = 0; uint8_t TimeInterval = 5; // Button Scanning Time Interval void SystemClock_Config(void); static void MX_GPIO_Init(void); bool BtnDebounce(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { T2 = SysTicks; if( (T2-T1) >= TimeInterval ) { if(BtnDebounce()) { HAL_GPIO_TogglePin(LED1_GPIO, LED1_Pin); // Toggle The Output LED Pin } T1 = SysTicks; } } } void SysTick_CallBack(void) { SysTicks++; } bool BtnDebounce(void) { static uint16_t Btn1_States = 0; Btn1_States = (Btn1_States<<1) | (!HAL_GPIO_ReadPin(Button1_GPIO, Button1_Pin)); return (Btn1_States == 0xFFF0); } |
STM32 Button Debounce Example Testing
Here is the testing result of this example project running on my STM32 blue pill (F103C8T6) board.
Yellow Trace: Bouncy Push Button Signal
Blue Trace: Clean Output Signal on The LED
Required Parts For STM32 Examples
All the example Code/LABs/Projects in this STM32 Series of Tutorials are done using the Dev boards & Electronic Parts Below:
QTY. | Component Name | Amazon.com | AliExpress | eBay |
1 | STM32-F103 BluePill Board (ARM Cortex-M3 @ 72MHz) | Amazon | AliExpress | eBay |
1 | Nucleo-L432KC (ARM Cortex-M4 @ 80MHz) | Amazon | AliExpress | eBay |
1 | ST-Link V2 Debugger | Amazon | AliExpress | eBay |
2 | BreadBoard | Amazon | AliExpress | eBay |
1 | LEDs Kit | Amazon & Amazon | AliExpress | eBay |
1 | Resistors Kit | Amazon & Amazon | AliExpress | eBay |
1 | Capacitors Kit | Amazon & Amazon | AliExpress & AliExpress | eBay & eBay |
1 | Jumper Wires Pack | Amazon & Amazon | AliExpress & AliExpress | eBay & eBay |
1 | Push Buttons | Amazon & Amazon | AliExpress | eBay |
1 | Potentiometers | Amazon | AliExpress | eBay |
1 | Micro USB Cable | Amazon | AliExpress | eBay |
★ Check The Links Below For The Full Course Kit List & LAB Test Equipment Required For Debugging ★
Download Attachments
You can download all attachment files for this Article/Tutorial (project files, schematics, code, etc..) using the link below. Please consider supporting our work through the various support options listed in the link down below. Every small donation helps to keep this website up and running and ultimately supports the whole community.
Wrap Up
In conclusion, we’ve explored different button debouncing techniques that can be used with any STM32 microcontroller. Delay-based button (switch) debounce techniques should work just fine, however, the provided examples in this tutorial will give you a much more reliable/expandable setup, and your CPU will be much more free to do more important processing tasks.
You can build on top of the example provided in this tutorial and/or explore the other STM32 tutorial series linked below.