Button debounce handling

Good afternoon everyone)
I continue to transfer my project (on STM32) to RTOS and I really like using RTOS)
But, accustomed to using the classic while, I can’t form an understanding of how to now do the same but with an RTOS…

I have 15 buttons connected to the microcontroller inputs
In the classic scheme, I used a self-written function, which was constantly polled in the main loop.
If the button is pressed continuously for more than 50ms (or another value), then consider that the button is pressed and perform any actions.
If the button is continuously released for more than 50ms (or another value), then consider that the button is released and perform any actions.

I understand that constant polling in an RTOS is not good.
I was thinking about putting my function in an idle task function or a timer daemon function, but I’m not sure if that would be the right solution.
For reference,. my function is not blocking, but it takes some time to compare the time interval and write some flag, but nothing that would take a lot of time.

What solution could be found in an RTOS?

If you don’t want or can’t use HW event driven button handling (using EXTI interrupts) you could use a polling task as you proposed. You could use a function hooked into the idle loop or have a dedicated, low prio task. This function or task in turn sends the detected events to a task doing the desired event processing e.g. using a queue of event descriptor items e.g. containing the button number along with up/down flag. This might be encoded into a single byte, of course. Using the idle hook allows that you don’t have to take care about putting a polling task into sleep to avoid starving other tasks with lower priority. The idle hook function must not block or sleep as documented.

I can use interrupts, but in this case, due to the nature of the mechanical button, the interrupt may be called several times…

Absolutely right. In fact you would have to debounce in the button ISR event post-processing task. So background polling at idle prio could be the most simple and robust approach :+1:

Using the Idle hook or Tick hook would both work for your use case. Hartmut’s suggestion is wise. This would be simpler than registering an interrupt handler.

Hook documentation

1 Like

Hi,

You must implement your keypad as a state machine (or properly stated, as Finite State Machine, FSM), so that you code doesn’t get stuck into a loop.

Your keypad will pass among several states while it’s decoded and debounced. Each state only ask for a condition: if it’s met, then some action takes place and the state machine pointer will point to another state; else, then nothing happens. Either way, the state machine always gets out and releases the CPU. Look at this pseudo-C code:

state_machine()
{
   switch( state )
   {
      case 0:
         if( a condition is met )
         {
            do something
            state = 1
         }
         break
         
      case 1:
         if( a condition is met )
         {
            do something
            state = 0
         }
         break
         
      // more states (case's)
         
      default:  // always include the default clause!
         something went wrong
         fix it
         break
   }
}

IdleHook() (or TickHook() or any other periodic task)
{
   other stuff()
   state_machine()
}

You should deploy the state machine function in a periodic task, like the IdleTask or even inside the TickHook.

A state machine implementation is easier that it sounds. I have an article in my blog about this topic: I implemented a keypad state machine that captures not only a single press button, but also long presses and repetitions. It works awesome!

Actually it’s two versions: a simple one that detects single presses and another more convoluted that detects short and long presses and repetions. Choose the one that fits your needs.

I wrote the source code for these state machines as classes in C++, but you can adapt them to plain C without any problems, either using free functions or as an abstract data type (Object Oriented C). I wrote them for the Arduino platform, but again, you can adapt them easily to the one you’re using.

Both versions has only 3 meaningful functions, and the magic happens inside the state_machine() method:

// Version for single presses (or strokes)
class Keypad
{
public:
   void init( uint8_t* keys, uint8_t num_keys ); // Constructor Arduino's style
   void state_machine();                         // Magic happens here
   uint8_t read();                               // For the client
};

You can reach the article HERE.

Hope it helps :smile:!

1 Like

Thanks for the answer, I’ll do that)
Most likely I will create a separate task with low priority)

Thank you for your very detailed answer, I will definitely take a look at your code)

The way it works for me in the moment is to wait for a notification from ISR until the first input change, only then wait once for the debounce time and then read in the Input again to see whether it settled on an active state. After handling the Button(s), I again indefinitely wait for a notification from ISR.