Debouncing buttons on EFM32 Happy Gecko
Button bounce is always a problem for microcontrollers. There are many ways to deal with the issue. The pins can be sampled at a low frequency so that the bounce will settle between consecutive samplings. They can be low-pass filtered in software. Some approaches require the pin to be stable for some amount of time to register a press.
Interrupts are usually avoided because the MCU could register almost every edge of the button bounce. With a small piece of code however interrupts can be used for one-shot operation and "rearmed" later from a timer interrupt (after the bounce period). This is the approach the driver uses. A major benefit is that the system reacts to the press immediately.
Interface
The driver has an init function that has to be called once at startup, a periodic task that reenables interrupts after the bounce stabilizes and function to get current press.
1 2 3 4 5 6 7 8 9 10 11 |
|
Implementation
The driver is written to run on an EFM32 Happy Gecko but should be easily portable to other MCUs in the family (and porting it to a totally different MCU should not be that difficult). First the pins are initialized as inputs with pullups (and filters), they are set to generate interrupts on the falling edge (I usually wire buttons from MCU pin to GND to save components by using the internal pullups).
When an interrupt is raised a flag is set indicating the button that was pressed. A predefined value (DEBOUNCE_PERIODS
) is written to a per-button counter. This flag can be immediately (well... after the ISR) used by the application giving very nice, responsive operation to the user. The interrupt handler also disables the interrupt for that particular pin (to avoid receiving the bounces). Happy Gecko GPIOs have separate interrupt vectors for even and odd pin numbers so I used gcc alias attribute to point both vectors to the same function.
Second part happens in a periodic task. If a button that has been pressed a counter value is checked and decremented. When it reaches zero the button interrupt is reenabled.
The driver can be easily extended to use more buttons by configuring more pins and adding the masks to BUTTON_PIN_MASK
array. button_t
enum values can be adjusted to use meaningful names elsewhere in the application (the values should follow the order of pins in BUTTON_PIN_MASK
).
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
How to use
The driver needs a periodic timer interrupt. I simply used the systick timer configured to fire at 100 Hz. The 100 Hz tick rate with DEBOUNCE_PERIODS
of 20 means that the button is ignored for 20/100th of a second after a press (so only 5 pressed per second can be delivered - well enough for my application), this is a very safe value.
1 2 3 |
|
The timer interrupt handler:
1 2 3 |
|
Happy debouncing :-)
I release this code into public domain.