一个高效、灵活的多按键状态机库,支持多种按键事件检测。
- ✅ 多种按键事件: 按下、抬起、单击、双击、长按开始、长按保持、重复按下
- ✅ 硬件去抖: 内置数字滤波,消除按键抖动
- ✅ 状态机驱动: 清晰的状态转换逻辑,可靠性高
- ✅ 多按键支持: 支持无限数量的按键实例
- ✅ 回调机制: 灵活的事件回调函数注册
- ✅ 内存优化: 紧凑的数据结构,低内存占用
- ✅ 配置灵活: 可自定义时间参数和功能选项
- ✅ 参数验证: 完善的错误检查和边界条件处理
- 更清晰的枚举命名 (
BTN_PRESS_DOWN
vsPRESS_DOWN
) - 增加状态机状态枚举,提高可读性
- 统一的函数命名规范
- 更好的代码注释和文档
- 新增
button_detach()
- 动态移除事件回调 - 新增
button_reset()
- 重置按键状态 - 新增
button_is_pressed()
- 查询当前按键状态 - 新增
button_get_repeat_count()
- 获取重复按下次数 - 改进的
button_get_event()
函数
- 完善的参数验证
- 空指针检查
- 数组越界保护
- 更好的错误返回值
- 内联函数优化 GPIO 读取
- 更安全的宏定义
- 减少不必要的计算
- 优化的状态机逻辑
- 清晰的状态转换
- 模块化设计
- 配置文件分离
- 详细的使用示例
# 编译所有内容 (库 + 示例)
make
# 只编译库
make library
# 只编译示例
make examples
# 编译特定示例
make basic_example
make advanced_example
make poll_example
# 运行测试
make test
# 清理构建文件
make clean
# 查看帮助
make help
# 使脚本可执行
chmod +x build.sh
# 编译所有内容
./build.sh
# 只编译库
./build.sh library
# 编译特定示例
./build.sh basic_example
# 查看帮助
./build.sh help
编译完成后,文件结构如下:
build/
├── lib/
│ └── libmultibutton.a # 静态库
├── bin/
│ ├── basic_example # 基础示例
│ ├── advanced_example # 高级示例
│ └── poll_example # 轮询示例
└── obj/ # 目标文件
演示基本的按键事件处理:
./build/bin/basic_example
功能:
- 单击、双击、长按检测
- 重复按下计数
- 按键状态查询
- 自动化演示序列
演示高级功能和动态管理:
# 运行完整演示
./build/bin/advanced_example
# 详细输出模式
./build/bin/advanced_example -v
# 安静模式 (手动测试)
./build/bin/advanced_example -q
功能:
- 多按键管理
- 动态回调函数添加/移除
- 配置按键
- 运行时状态监控
演示轮询模式使用:
./build/bin/poll_example
功能:
- 无回调函数的轮询模式
- 事件状态查询
- 主循环集成示例
- 预定义按键模式演示
#include "multi_button.h"
static Button btn1;
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;
}
}
// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
button_init(&btn1, read_button_gpio, 0, 1);
void btn1_single_click_handler(void* btn)
{
printf("Button 1: Single Click\n");
}
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler);
button_start(&btn1);
// 在 5ms 定时器中断中调用
void timer_5ms_interrupt_handler(void)
{
button_ticks();
}
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, // 长按保持
BTN_NONE_PRESS // 无事件
} ButtonEvent;
void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
功能: Initialize button instance
参数:
handle
: 按键句柄pin_level
: GPIO 读取函数指针active_level
: 有效电平 (0 或 1)button_id
: 按键 ID
功能: Attach event callback function
参数:
handle
: 按键句柄event
: 事件类型cb
: 回调函数
功能: Detach event callback function
参数:
handle
: 按键句柄event
: 事件类型
功能: Start button processing
返回值: 0=成功, -1=已存在, -2=参数错误
功能: Stop button processing
功能: Background processing function (call every 5ms)
功能: Get current button event
功能: Get repeat press count
功能: Reset button state to idle
功能: Check if button is currently pressed
返回值: 1=按下, 0=未按下, -1=错误
在 multi_button_config.h
中可以自定义以下参数:
#define TICKS_INTERVAL 5 // 定时器中断间隔 (ms)
#define DEBOUNCE_TIME_MS 15 // 去抖时间 (ms)
#define SHORT_PRESS_TIME_MS 300 // 短按时间阈值 (ms)
#define LONG_PRESS_TIME_MS 1000 // 长按时间阈值 (ms)
#define PRESS_REPEAT_MAX_NUM 15 // 最大重复计数
- 定时器设置: 必须配置 5ms 定时器中断,在中断中调用
button_ticks()
- GPIO 配置: 按键引脚需配置为输入模式,根据需要启用上拉或下拉电阻
- 回调函数: 回调函数应尽量简短,避免长时间阻塞
- 内存管理: 按键实例可以是全局变量或动态分配
- 多按键: 每个物理按键需要独立的 Button 实例和唯一的 button_id
[IDLE] --按下--> [PRESS] --长按--> [LONG_HOLD]
^ | |
| 抬起| 抬起|
| v |
| [RELEASE] <----------------+
| | ^
| 超时| |快速按下
| | |
+----------+ [REPEAT]
MultiButton/
├── multi_button.h # 主头文件
├── multi_button.c # 主源文件
├── Makefile # 构建脚本
├── build.sh # 备用构建脚本
├── examples/ # 示例目录
│ ├── basic_example.c # 基础示例
│ ├── advanced_example.c # 高级示例
│ └── poll_example.c # 轮询示例
├── build/ # 构建输出目录
│ ├── lib/ # 库文件
│ ├── bin/ # 可执行文件
│ └── obj/ # 目标文件
└── README.md # 说明文档
- C99 标准
- 适用于各种微控制器平台 (STM32, Arduino, ESP32, etc.)
- 支持裸机和 RTOS 环境
- 内存占用小,适合资源受限的系统