Pushbutton Event Handler: Part 4

Pushbutton Handler

The handler routine is started by calling PushButton_Init function. This function registers PushButton_Routine at the end as a callback of the software timer.

   35 void PushButton_Init(uint8_t mask)
   36 {
   37     int i ;
   38 
   39     // clear data
   40     pp.old_state = pp.new_state = 0;
   41     pp.mask = mask;
   42     pp.mode = 0;
   43     pp.flag = false;
   44 
   45     // clear log
   46     for(i = 0; i < 8; i++)
   47     {
   48         PushButton_ClearLog(i);
   49     }
   50 
   51     // register pushbutton main routine
   52     UsrTimer_Set(PUSHBTN_TMR_PERIOD, 0, PushButton_Routine);
   53 }

The PushButton_Routine is then called at every 80msec and tracks the button state. If the button state is ON for at least three consecutive polling (80 x 3 = 240msec) then it declares CLICK by posting an event. These timing can be set to fit the bouncing characteristic of the mechanical switch.

   71 /// PushButton_Routine timer period in msec
   72 #define PUSHBTN_TMR_PERIOD      80
   73 /// Criteria for determination of short click and long click
   74 #define PUSHBTN_TO_SHORT        3       // 3 * PUSHBTN_TMR_PERIOD
   75 #define PUSHBTN_TO_LONG         10      // 10 * PUSHBTN_TMR_PERIOD
   76 #define PUSHBTN_TO_MAX          255     // maximum duration count

The PushButton_Routine, distinguishes single, double, triple click and long click (push and hold) and generates event accordingly, which in turn collected by the event handler in the main loop.

You can also change the mode from click detection to up/down detection, in which event is constantly generated when you hold the button down.

(source code)

Pushbutton Event Handler: Part 2

Software Timer

In typical hardware timer use cases, you set up a timer with certain settings such as interval then assign a callback function that is called when the timer expires and let the function do certain task. You can instead write a timer callback function to call other callback functions, i.e. cascade the callback functions. This way, you can hook up multiple callback functions to a timer with arbitrary interval. Original timer now works as a base timer but the callback function works as a software timer.

Cortex-M processors have generic 1 millisecond timer called SysTick. Write a SysTick callback function that handles host of other callback functions with intervals of multiple of 1msec. For example, you can make the SysTick callback function call pushbutton state check routine every 300msec, as well as many other routines that are called at various intervals.

In STM32Cube framework, SysTick interrupt handler calls the function HAL_SYSTICK_IRQHandler (Warning: This was broken in the version 5.0.0 of STM32CubeMX. So you need to do it manually), which in turn calls HAL_SYSTICK_Callback function.

    1 /**
    2 * @brief This function handles System tick timer.
    3 */
    4 void SysTick_Handler(void)
    5 {
    6     /* USER CODE BEGIN SysTick_IRQn 0 */
    7 
    8     /* USER CODE END SysTick_IRQn 0 */
    9     HAL_IncTick();
   10     HAL_SYSTICK_IRQHandler();
   11     /* USER CODE BEGIN SysTick_IRQn 1 */
   12 
   13     /* USER CODE END SysTick_IRQn 1 */
   14 }

Thus it is a good place to put your software timer routine.

  355 /** SysTick callback function override.
  356  */
  357 void HAL_SYSTICK_Callback()
  358 {
  359     // UsrTimer_Routine will have 1msec resolution
  360     UsrTimer_Routine();
  361 }

Following example registers a function that toggles LED at every 100msec.

    1 void TestCallback()
    2 {
    3     HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    4 }
    5 
    6 main()
    7 {
    8     // start software timer routine
    9     UsrTimer_Init();
   10 
   11     // register a callbackfunction with 100msec interval
   12     UsrTimer_Set(100, 0, TestCallback);

Software timer implemented in the sample project file support following functions

   43 /// Initialize all timers
   44 void UsrTimer_Init();
   45 /// Enable or disable main routine
   46 void UsrTimer_Enable(bool flag);
   47 /// Clear the timer
   48 void UsrTimer_Clear(uint32_t index);
   49 /// Pause the timer 
   50 void UsrTimer_Pause(uint32_t index);
   51 /// Resume the timer
   52 void UsrTimer_Resume(uint32_t index);
   53 /// Main timer routine
   54 void UsrTimer_Routine(void);
   55 /// Set a new timer with the callback function
   56 int UsrTimer_Set(uint32_t interval, uint32_t duration, usrtimer_callback f);

(source code)

Pushbutton Event Handler: Part 1

Project Design

Pushbuttons or tactile switches are most popular input devices. But properly handling them is not so trivial. Let us check out what do we need to consider for the implementation.

(Chattering and Debouncing) Most type of mechanical contact switches produce contact bouncing effect. Following snapshot of an oscilloscope taken from Wikipedia illustrates this phenomenon well.

Actual situation varies a lot depending on the type of switch however. In a certain way chattering like above is quite exceptional, since it lasts only 2-3msec. Such short chattering can be dealt with simple hardware like R-C filter. More typically however chattering can last more than 100msec easily.

Robust implementation of pushbutton input handler thus requires polling of the switch state with a regular interval, eliminating erroneous input caused by momentary glitches.

(Short Click, Long Click and Multiple Click) Sometimes you want to assign a multiple functions on a single switch. You may want to distinguish between short click and long click or you may want to assign different tasks for single click, double click, triple click and so on.

This requires an algorithm that keeps track of the state of the button in time. For example when a single click is detected, it should be able to determine whether it has to wait certain amount of time for the next click or it can declare the single click event now. The timer based pushbutton state polling routine is well suited for this purpose.

(Event Generation and Handling) Finally, there is a situation when you want to divide one task into at least two parts, one part that needs to be executed in very short duration of time, usually much less than a millisecond, and the other part that has no particular time limit and that can be interrupted at any moment.

Instead of having a single routine that (1) polls states of pushbuttons regularly (2) determines the type of the button action based on the history, (3) then run certain tasks depending on the result of (2), you can divide them into two parts, i.e. fast processing part (1+2) and slow processing part (3). The fast processing part can be implemented as a interrupt callback function since it takes only a few microseconds at most. Then it generates an event when valid input is detected. The slow processing part can be called in a main loop checking the event, runs dedicated function if necessary, with no particular time constraint.

In the following posts, we will implement basic structure on a STM32 platform.