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. Timer Settings: Must configure a timer interrupt that matches the TICKS_INTERVAL macro, and call button_ticks()
- 2. GPIO Configuration: Button pins must be configured as input mode, enabling pull-up or pull-down resistors as needed
- 3. Callback Functions: Callback functions should be kept short to avoid long blocking
- 4. Memory Management: Button instances can be global variables or dynamically allocated
- 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