Skip to content
项目

0x1abin/MultiButton has 2,394 GitHub stars and 749 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-08 20:19 UTC. Last push 2026-03-17.

这些数字如何统计

Button driver for embedded system

可以做什么

  • 7 event types: press down, press up, single click, double click, long press start, long press hold, repeat press
  • Hardware debounce: built-in digital filter eliminates contact bounce
  • State machine driven: reliable state transitions with clear logic
  • Unlimited buttons: linked-list architecture supports any number of button instances

实测星标增长

24小时
0
7天
更新延迟
15天
更新延迟
1个月
更新延迟

各周期内的星标净变化,周期相互重叠,并非每日新增。缺失的测量保持空白。星标代表关注度,不代表软件质量。

README_CN.md

原文

MultiButton

一个高效、灵活的多按键状态机库,支持多种按键事件检测。

功能特性

  • 多种按键事件: 按下、抬起、单击、双击、长按开始、长按保持、重复按下
  • 硬件去抖: 内置数字滤波,消除按键抖动
  • 状态机驱动: 清晰的状态转换逻辑,可靠性高
  • 多按键支持: 支持无限数量的按键实例
  • 回调机制: 灵活的事件回调函数注册,支持 void* user_data 上下文指针
  • 内存优化: 紧凑的数据结构,低内存占用
  • 配置灵活: 可自定义时间参数和功能选项
  • 参数验证: 完善的错误检查和边界条件处理
  • 线程安全: 可选的 RTOS 锁支持,裸机零开销

编译和构建

使用 Makefile (推荐)

# 编译所有内容 (库 + 示例)
make

# 只编译库
make library

# 只编译示例
make examples

# 编译特定示例
make basic_example
make advanced_example
make poll_example

# 运行测试
make test

# 清理构建文件
make clean

# 查看帮助
make help

构建输出

编译完成后,文件结构如下:

build/
+-- lib/
|   +-- libmultibutton.a    # 静态库
+-- bin/
|   +-- basic_example       # 基础示例
|   +-- advanced_example    # 高级示例
|   +-- poll_example        # 轮询示例
+-- obj/                    # 目标文件

快速开始

1. 包含头文件

#include "multi_button.h"

2. 定义按键实例

static Button btn1;

3. 实现 GPIO 读取函数

uint8_t read_button_gpio(uint8_t button_id)
{
    switch (button_id) {
        case 1:
            return HAL_GPIO_ReadPin(BUTTON1_GPIO_Port, BUTTON1_Pin);
        default:
            return 0;
    }
}

4. 初始化按键

// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
button_init(&btn1, read_button_gpio, 0, 1);

5. 注册事件回调

void btn1_single_click_handler(Button* btn, void* user_data)
{
    printf("Button 1: Single Click\n");
}

button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler, NULL);

6. 启动按键处理

button_start(&btn1);

7. 定时调用处理函数

// 在 5ms 定时器中断中调用
void timer_5ms_interrupt_handler(void)
{
    button_ticks();
}

API 参考

按键事件类型

typedef enum {
    BTN_PRESS_DOWN = 0,     // 按键按下
    BTN_PRESS_UP,           // 按键抬起
    BTN_PRESS_REPEAT,       // 重复按下检测
    BTN_SINGLE_CLICK,       // 单击完成
    BTN_DOUBLE_CLICK,       // 双击完成
    BTN_LONG_PRESS_START,   // 长按开始
    BTN_LONG_PRESS_HOLD,    // 长按保持 (每个 tick 触发)
    BTN_NONE_PRESS          // 无事件
} ButtonEvent;

核心函数

void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)

功能: 初始化按键实例 参数:

  • handle: 按键句柄
  • pin_level: GPIO 读取函数指针
  • active_level: 有效电平 (0 或 1)
  • button_id: 按键 ID

void button_attach(Button* handle, ButtonEvent event, BtnCallback cb, void* user_data)

功能: 注册事件回调函数 参数:

  • handle: 按键句柄
  • event: 事件类型
  • cb: 回调函数
  • user_data: 用户上下文指针(同一按键的所有回调共享)

void button_detach(Button* handle, ButtonEvent event)

功能: 移除事件回调函数

int button_start(Button* handle)

功能: 启动按键处理 返回值: 0=成功, -1=已存在, -2=参数错误

void button_stop(Button* handle)

功能: 停止按键处理(可在回调中安全调用)

void button_ticks(void)

功能: 后台处理函数 (每 5ms 调用一次)

工具函数

ButtonEvent button_get_event(Button* handle)

功能: 获取当前按键事件

uint8_t button_get_repeat_count(Button* handle)

功能: 获取重复按下次数

void button_reset(Button* handle)

功能: 重置按键状态

int button_is_pressed(Button* handle)

功能: 查询按键是否按下 返回值: 1=按下, 0=未按下, -1=错误

user_data 上下文指针

每个回调函数都会收到一个 void* user_data 指针,通过 button_attach() 设置:

typedef struct {
    int led_pin;
    int count;
} MyContext;

MyContext ctx = { .led_pin = 13, .count = 0 };

void on_click(Button* btn, void* user_data)
{
    MyContext* ctx = (MyContext*)user_data;
    toggle_led(ctx->led_pin);
    ctx->count++;
}

button_attach(&btn1, BTN_SINGLE_CLICK, on_click, &ctx);

同一按键的所有回调共享同一个 user_data(按键级别存储,非事件级别)。

实现三击 (N-Click)

库原生支持单击和双击事件。三击及以上可通过 BTN_PRESS_REPEAT + button_get_repeat_count() 实现:

void on_repeat(Button* btn, void* user_data)
{
    uint8_t count = button_get_repeat_count(btn);
    if (count == 3) {
        // 三击!
    }
}

button_attach(&btn, BTN_PRESS_REPEAT, on_repeat, NULL);

说明: BTN_SINGLE_CLICK 在 repeat==1 时触发,BTN_DOUBLE_CLICK 在 repeat==2 时触发。repeat>=3 时,仅 BTN_PRESS_REPEAT 在按下过程中触发。

配置选项

在 multi_button.h 中可以自定义以下参数:

#define TICKS_INTERVAL          5       // 定时器中断间隔 (ms)
#define DEBOUNCE_TICKS          3       // 去抖深度 (最大 7)
#define SHORT_TICKS             (300  / TICKS_INTERVAL)  // 短按阈值
#define LONG_TICKS              (1000 / TICKS_INTERVAL)  // 长按阈值
#define PRESS_REPEAT_MAX_NUM    15      // 最大重复计数

重要注意事项

BTN_LONG_PRESS_HOLD 每 tick 触发

BTN_LONG_PRESS_HOLD 在长按保持期间 每个 tick(默认 5ms = 200Hz)触发一次。如果回调中有耗时操作,请自行节流:

void on_long_hold(Button* btn, void* user_data)
{
    static uint16_t throttle = 0;
    if (++throttle < 20) return;  // 每 100ms 触发一次
    throttle = 0;
    // ... 执行操作 ...
}

回调执行上下文

如果 button_ticks() 在定时器中断(ISR)中调用,所有回调都在中断上下文中执行。回调应尽量简短,避免阻塞操作。建议在回调中设置标志位,在主循环中处理。

如果 button_ticks() 在主循环或 RTOS 任务中调用,回调在该上下文中执行,无 ISR 限制。

线程安全

#define MULTIBUTTON_THREAD_SAFE
#define MULTIBUTTON_LOCK()   osMutexAcquire(btn_mutex, osWaitForever)
#define MULTIBUTTON_UNLOCK() osMutexRelease(btn_mutex)
#include "multi_button.h"

回调在锁外执行,因此可以在回调中安全调用 button_stop()/button_start(),不会死锁。使用普通互斥量即可(无需递归锁)。

状态机说明

                          +-- 长按 -->  [LONG_HOLD]
                          |                  |
[IDLE] -- 按下 --> [PRESS]                 抬起
   ^                  |                      |
   |               抬起                      |
   |                  v                      |
   |             [RELEASE] <-----------------+
   |             |       ^
   |          超时|       | 快速按下
   |             |       |
   +-------------+   [REPEAT] -- 按住太久 --> [PRESS]

示例程序

  • examples/basic_example.c - 基础示例:单击、双击、长按检测
  • examples/advanced_example.c - 高级示例:多按键管理、动态回调
  • examples/poll_example.c - 轮询模式示例

FAQ

Q: 如何检测三击? A: 注册 BTN_PRESS_REPEAT 回调,在回调中用 button_get_repeat_count() 检查次数。

Q: 在回调中调用 button_stop() 安全吗? A: 安全。库在调用回调前缓存了 next 指针,遍历过程中移除按键不会崩溃。

Q: ticks 计数器会溢出吗? A: 不会。计数器在 UINT16_MAX (65535) 处饱和,不会回绕。在 5ms 间隔下可覆盖约 327 秒的持续按住。

Q: 能在多线程 RTOS 中使用吗? A: 可以。定义 MULTIBUTTON_THREAD_SAFE 并提供锁宏。回调在锁外执行,普通互斥量即可。

兼容性

  • C99 标准
  • 适用于各种微控制器平台 (STM32, Arduino, ESP32, etc.)
  • 支持裸机和 RTOS 环境
  • 内存占用小,适合资源受限的系统

项目结构

MultiButton/
+-- multi_button.h          # 主头文件
+-- multi_button.c          # 主源文件
+-- Makefile               # 构建脚本
+-- examples/              # 示例目录
|   +-- basic_example.c    # 基础示例
|   +-- advanced_example.c # 高级示例
|   +-- poll_example.c     # 轮询示例
+-- tests/
|   +-- test_button.c      # 单元测试
+-- build/                 # 构建输出目录
+-- README.md              # 英文文档
+-- README_CN.md           # 中文文档

探索相关项目