MultiButton: An Efficient State Machine Library Supporting 7 Button Events for Bare Metal and RTOS

Repository Address:https://github.com/0x1abin/MultiButton

An efficient and flexible multi-button state machine library that supports various button event detection.

Features

  • Multiple Button Events: Press, Release, Single Click, Double Click, Long Press Start, Long Press Hold, Repeat Press
  • Hardware Debounce: Built-in digital filtering to eliminate button bounce
  • State Machine Driven: Clear state transition logic with high reliability
  • Multi-Button Support: Supports an unlimited number of button instances
  • Callback Mechanism: Flexible event callback function registration
  • Memory Optimization: Compact data structures with low memory usage
  • Flexible Configuration: Customizable timing parameters and functional options
  • Parameter Validation: Comprehensive error checking and boundary condition handling

Quick Start

1. Include Header File

#include "multi_button.h"

2. Define Button Instance

static Button btn1;

3. Implement GPIO Read Function

/**
 * @brief  Button GPIO sampling function
 * @param  button_id  Button ID
 * @return uint8_t    Current button level (0/1)
 */
uint8_t read_pin_level(uint8_t button_id)
{
switch (button_id)
  {
case 1: // KEY1
    return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

default:
    return 0;
  }
}

4. Initialize Button

  /* 1. Initialize button object
       Parameters:
       - &btn1:             Button object
       - read_pin_level:   User-provided GPIO read function
       - 1:                 Button active level (1=pressed)
       - 1:                 Button ID (for identification in callback)
  */
  button_init(&btn1, read_pin_level, 1, 1);

5. Register Event Callback

/**
 * @brief Single Click Callback
 */
void btn1_single_click_cb(Button *btn)
{
  if (btn->button_id == 1)
  {
    key1_state = KEY1_STATE_SINGLE_CLICK; // Single Click
  }
}

button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_cb);

6. Start Button Processing

button_start(&btn1);

7. Periodically Call Processing Function

void HAL_SYSTICK_Callback(void)
{
  static uint8_t tick_5ms = 0;

  if (++tick_5ms >= 5)
  {
    tick_5ms = 0;
    button_ticks(); // Scan every 5ms
  }
}

Complete Example

static Button btn1;

typedef enum
{
KEY1_STATE_IDLE = 0,     // No operation
KEY1_STATE_SINGLE_CLICK, // Single click confirmed
KEY1_STATE_DOUBLE_CLICK, // Double click confirmed
KEY1_STATE_LONG_PRESS    // Long press triggered (start long press)
} Key1State;

Key1State key1_state = KEY1_STATE_IDLE;

/**
 * @brief  Button GPIO sampling function (called by button library)
 * @param  button_id  Button ID (supports multiple buttons)
 * @return uint8_t    Current button level (0/1)
 */
uint8_t read_pin_level(uint8_t button_id)
{
switch (button_id)
  {
case 1: // KEY1
    return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

default:
    return 0;
  }
}

/**
 * @brief Single Click Callback
 */
void btn1_single_click_cb(Button *btn)
{
if (btn->button_id == 1)
  {
    key1_state = KEY1_STATE_SINGLE_CLICK; // Single Click
  }
}

/**
 * @brief Double Click Callback
 */
void btn1_double_click_cb(Button *btn)
{
if (btn->button_id == 1)
  {
    key1_state = KEY1_STATE_DOUBLE_CLICK; // Double Click
  }
}

/**
 * @brief Long Press Start Callback
 */
void btn1_long_press_start_cb(Button *btn)
{
if (btn->button_id == 1)
  {
    key1_state = KEY1_STATE_LONG_PRESS; // Long Press
  }
}

/**
 * @brief Initialize Button 1: Bind GPIO, configure active level, register event callbacks
 */
void my_button_library_init(void)
{
/* 1. Initialize button object
       Parameters:
       - &btn1:             Button object
       - read_pin_level:   User-provided GPIO read function
       - 1:                 Button active level (1=pressed)
       - 1:                 Button ID (for identification in callback)
  */
button_init(&btn1, read_pin_level, 1, 1);

/* 2. Register callbacks (automatically triggered by button library) */
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_cb);
button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_cb);
button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_press_start_cb);

/* 3. Start button (add to internal linked list / state machine) */
button_start(&btn1);
}

void HAL_SYSTICK_Callback(void)
{
static uint8_t tick_5ms = 0;

if (++tick_5ms >= 5)
  {
    tick_5ms = 0;
    button_ticks(); // Scan every 5ms
  }
}

Configuration Options

In multi_button.h, you can configure the following parameters:

// Configuration constants - modify as per your requirements
#define TICKS_INTERVAL 5                   // ms - Timer interrupt interval
#define DEBOUNCE_TICKS 3                   // MAX 7 (0 ~ 7) - Debounce filter depth
#define SHORT_TICKS (300 / TICKS_INTERVAL) // Short press threshold
#define LONG_TICKS (1000 / TICKS_INTERVAL) // Long press threshold
#define PRESS_REPEAT_MAX_NUM 15            // Maximum repeat count

Usage Notes

  1. 1. Timer Settings: Must configure a timer interrupt that matches the TICKS_INTERVAL macro, and call button_ticks()
  2. 2. GPIO Configuration: Button pins must be configured as input mode, enabling pull-up or pull-down resistors as needed
  3. 3. Callback Functions: Callback functions should be kept short to avoid long blocking
  4. 4. Memory Management: Button instances can be global variables or dynamically allocated
  5. 5. Multi-Button: Each physical button requires a separate Button instance and a unique button_id

State Machine Description

[IDLE] --Press--> [PRESS] --Long Press--> [LONG_HOLD]
   ^                |                    |
   |             Release|                 Release|
   |                v                    |
   |          [RELEASE] <----------------+
   |          |       ^
   |       Timeout|       |Quick Press
   |          |       |
   +----------+   [REPEAT]

Compatibility

  • C99 Standard
  • Compatible with various microcontroller platforms (STM32, Arduino, ESP32, etc.)
  • Supports Bare Metal and RTOS environments
  • Low memory footprint, suitable for resource-constrained systems

Examples

Microcontroller: STM32F407VGT6

https://www.notion.so/ahhh111/MultiButton-7-RTOS-2aaa84b2a47b80dcb445ca3c77446466?source=copy_link

Click to read the original text to access the repository address

Leave a Comment