OpenEarable 2.0 - 按键模块 (Buttons Module)
2025-07-07
概述
按键模块负责处理OpenEarable设备上的物理按键输入,包括按键事件检测、防抖处理、状态管理和事件分发。该模块为用户提供交互接口,支持播放/暂停、音量控制等功能。
核心组件
1. Button 类 (Button.h/cpp
)
主要的按键抽象类,处理单个按键的完整生命周期:
核心接口
class Button {
public:
Button(gpio_dt_spec spec);
void begin(); // 初始化按键
void end(); // 清理按键资源
button_action getState() const; // 获取当前状态
private:
k_work_delayable button_work; // 延时工作队列
const struct gpio_dt_spec button; // GPIO规格
static struct gpio_callback button_cb_data; // 中断回调数据
button_action _buttonState; // 当前状态
button_action _temp_buttonState; // 临时状态
static void button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins);
int update_state();
static void button_work_handler(struct k_work * work);
};
状态管理
- BUTTON_RELEASED: 按键释放状态
- BUTTON_PRESS: 按键按下状态
- 使用防抖机制避免误触发
防抖机制
#define BUTTON_DEBOUNCE K_MSEC(10) // 10ms防抖延时
- 中断触发时启动延时工作
- 延时结束后确认状态变化
- 状态未变化时取消延时工作
2. 按键管理器 (button_manager.h/c
)
负责按键事件的消息队列管理和事件分发:
消息队列
K_MSGQ_DEFINE(button_queue, sizeof(struct button_msg), 1, 4);
ZBus 通道
ZBUS_CHAN_DEFINE(button_chan, struct button_msg, NULL, NULL, ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(0));
发布线程
void button_pub_task() {
int ret;
struct button_msg msg;
while (1) {
k_msgq_get(&button_queue, &msg, K_FOREVER);
ret = zbus_chan_pub(&button_chan, &msg, K_FOREVER);
if (ret) {
LOG_ERR("Failed to publish button msg, ret: %d", ret);
}
}
}
K_THREAD_DEFINE(button_publish, CONFIG_BUTTON_PUBLISH_STACK_SIZE, button_pub_task,
NULL, NULL, NULL, K_PRIO_PREEMPT(CONFIG_BUTTON_PUBLISH_THREAD_PRIO), 0, 0);
3. 按键分配 (button_assignments.h
)
定义按键映射和枚举:
enum button_pin_names {
BUTTON_EARABLE = DT_GPIO_PIN(DT_ALIAS(sw0), gpios),
BUTTON_VOLUME_UP, // 预留
BUTTON_VOLUME_DOWN, // 预留
BUTTON_4, // 预留
BUTTON_5, // 预留
};
#define BUTTON_PLAY_PAUSE BUTTON_EARABLE // 别名定义
4. 按键状态查询 (button_pressed.cpp
)
提供按键状态查询接口:
int button_pressed(enum button_pin_names pin, bool * pressed) {
switch (pin) {
case BUTTON_EARABLE:
*pressed = (earable_btn.getState() == BUTTON_PRESS);
return 0;
default:
*pressed = false;
return 0;
}
}
工作流程
1. 初始化流程
Button构造 → GPIO配置 → 中断配置 → 回调注册 → 初始状态读取
2. 按键事件处理流程
GPIO中断触发 → button_isr() → 启动延时工作 → button_work_handler() →
update_state() → 消息入队 → button_pub_task() → ZBus发布
3. 防抖处理
中断触发 → 读取临时状态 →
如果状态相同 → 取消延时工作
如果状态不同 → 重新调度延时工作 → 延时后确认状态变化
硬件接口
GPIO 配置
- 使用设备树别名
sw0
定义主按键 - 配置为输入模式 (
GPIO_INPUT
) - 启用双边沿中断 (
GPIO_INT_EDGE_BOTH
)
中断处理
- 共享中断回调函数
button_isr
- 支持多按键扩展(当前仅实现主按键)
- 中断安全的状态更新
配置选项
Kconfig 配置
CONFIG_BUTTON_PUBLISH_STACK_SIZE # 发布线程栈大小
CONFIG_BUTTON_PUBLISH_THREAD_PRIO # 发布线程优先级
CONFIG_MODULE_BUTTON_HANDLER_LOG_LEVEL # 日志级别
设备树配置
aliases {
sw0 = &button0; // 主按键别名
};
扩展性设计
多按键支持
- 代码中预留了多个按键的实现框架
- 可通过取消注释轻松添加音量按键等
- 支持不同按键的独立处理
按键功能映射
- 通过
button_assignments.h
集中管理按键功能 - 支持别名定义,便于功能重映射
- 可扩展复杂按键组合功能
集成接口
ZBus 消息
struct button_msg {
uint32_t button_pin; // 按键引脚
button_action button_action; // 按键动作
};
订阅者接口
其他模块可通过ZBus订阅按键事件:
ZBUS_SUBSCRIBER_DEFINE(button_sub, 1);
ZBUS_CHAN_ADD_OBS(button_chan, button_sub, 0);
日志和调试
日志模块
LOG_MODULE_REGISTER(button, CONFIG_MODULE_BUTTON_HANDLER_LOG_LEVEL);
关键日志点
- 按键状态变化记录
- 消息队列满警告
- GPIO配置错误
- 中断配置失败
功耗优化
中断驱动
- 使用GPIO中断而非轮询
- 按键空闲时零功耗
- 中断唤醒系统
工作队列优化
- 使用延时工作队列进行防抖
- 避免持续CPU占用
- 适当的线程优先级配置
错误处理
GPIO 错误
- 设备就绪检查
- 引脚配置失败处理
- 中断配置错误处理
消息队列错误
- 队列满时的警告日志
- 发布失败的错误处理
- 资源竞争保护
典型使用场景
1. 播放/暂停控制
// 在音频模块中订阅按键事件
void audio_button_handler(const struct zbus_channel *chan) {
struct button_msg msg;
zbus_chan_read(chan, &msg, K_FOREVER);
if (msg.button_pin == BUTTON_EARABLE && msg.button_action == BUTTON_PRESS) {
// 执行播放/暂停逻辑
toggle_playback();
}
}
2. 系统状态切换
// 在状态管理模块中处理按键
void state_button_handler(const struct zbus_channel *chan) {
struct button_msg msg;
zbus_chan_read(chan, &msg, K_FOREVER);
if (msg.button_pin == BUTTON_EARABLE && msg.button_action == BUTTON_PRESS) {
// 状态切换或功能激活
switch_system_state();
}
}
总结
按键模块提供了完整的物理按键交互支持,具有以下特点:
- 可靠性: 硬件防抖和软件状态确认
- 扩展性: 支持多按键和复杂功能映射
- 高效性: 中断驱动和事件发布机制
- 集成性: 通过ZBus与其他模块无缝集成
- 低功耗: 中断唤醒和适当的线程调度
该模块为OpenEarable设备的用户交互提供了稳定可靠的基础设施。