Files

335 lines
9.2 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved.
*/
#include "multi_button.h"
/* 回调执行宏包含空值检查传递user_data */
#define EVENT_CB(ev) do { if (handle->cb[ev]) handle->cb[ev](handle, handle->user_data); } while(0)
/* 按键句柄链表头 */
static Button* head_handle = NULL;
/* 前向声明 */
static void button_handler(Button* handle);
static inline uint8_t button_read_level(Button* handle);
/*
* 函数功能:初始化按键结构体句柄
* 入口参数handle - 按键句柄结构体指针
* pin_level - 读取按键GPIO电平的函数指针
* active_level - 按下时的有效电平
* button_id - 按键标识符
* 返回值:无
* 函数说明:初始化按键状态机和所有参数
*/
void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
if (!handle || !pin_level) {
return;
}
memset(handle, 0, sizeof(Button));
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->hal_button_level = pin_level;
handle->button_level = !active_level;
handle->active_level = active_level;
handle->button_id = button_id;
handle->state = BTN_STATE_IDLE;
}
/*
* 函数功能:绑定按键事件回调函数
* 入口参数handle - 按键句柄结构体指针
* event - 触发事件类型
* cb - 回调函数
* user_data - 传递给回调的用户上下文指针
* 返回值:无
* 函数说明:为指定事件注册回调函数
*/
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb, void* user_data)
{
if (!handle || event >= BTN_EVENT_COUNT) {
return;
}
handle->cb[event] = cb;
handle->user_data = user_data;
}
/*
* 函数功能:解绑按键事件回调函数
* 入口参数handle - 按键句柄结构体指针
* event - 触发事件类型
* 返回值:无
* 函数说明:移除指定事件的回调函数
*/
void button_detach(Button* handle, ButtonEvent event)
{
if (!handle || event >= BTN_EVENT_COUNT) {
return;
}
handle->cb[event] = NULL;
}
/*
* 函数功能:获取按键事件
* 入口参数handle - 按键句柄结构体指针
* 返回值:按键事件
* 函数说明:返回当前发生的按键事件
*/
ButtonEvent button_get_event(Button* handle)
{
if (!handle) {
return BTN_NONE_PRESS;
}
return (ButtonEvent)(handle->event);
}
/*
* 函数功能:获取按键重复按下次数
* 入口参数handle - 按键句柄结构体指针
* 返回值:重复按下次数
* 函数说明:返回连击次数
*/
uint8_t button_get_repeat_count(Button* handle)
{
if (!handle) {
return 0;
}
return handle->repeat;
}
/*
* 函数功能:重置按键状态到空闲
* 入口参数handle - 按键句柄结构体指针
* 返回值:无
* 函数说明:将按键状态机重置为初始状态
*/
void button_reset(Button* handle)
{
if (!handle) {
return;
}
handle->state = BTN_STATE_IDLE;
handle->ticks = 0;
handle->repeat = 0;
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->debounce_cnt = 0;
}
/*
* 函数功能:检查按键是否按下
* 入口参数handle - 按键句柄结构体指针
* 返回值1 - 按下0 - 未按下,-1 - 错误
* 函数说明:检查当前按键是否处于按下状态
*/
int button_is_pressed(Button* handle)
{
if (!handle) {
return -1;
}
return (handle->button_level == handle->active_level) ? 1 : 0;
}
/*
* 函数功能:读取按键电平
* 入口参数handle - 按键句柄结构体指针
* 返回值:按键电平
* 函数说明通过HAL函数读取GPIO电平
*/
static inline uint8_t button_read_level(Button* handle)
{
return handle->hal_button_level(handle->button_id);
}
/*
* 函数功能:按键驱动核心函数,驱动状态机
* 入口参数handle - 按键句柄结构体指针
* 返回值:无
* 函数说明:实现按键消抖和事件检测的状态机
*/
static void button_handler(Button* handle)
{
uint8_t read_gpio_level = button_read_level(handle);
/* 非空闲状态下增加tick计数器饱和处理 */
if (handle->state > BTN_STATE_IDLE) {
if (handle->ticks < UINT16_MAX) {
handle->ticks++;
}
}
/* 按键消抖处理 */
if (read_gpio_level != handle->button_level) {
if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
handle->button_level = read_gpio_level;
handle->debounce_cnt = 0;
}
} else {
handle->debounce_cnt = 0;
}
/* 状态机 */
switch (handle->state) {
case BTN_STATE_IDLE:
if (handle->button_level == handle->active_level) {
/* 检测到按键按下 */
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
handle->ticks = 0;
handle->repeat = 1;
handle->state = BTN_STATE_PRESS;
} else {
handle->event = (uint8_t)BTN_NONE_PRESS;
}
break;
case BTN_STATE_PRESS:
if (handle->button_level != handle->active_level) {
/* 按键释放 */
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE;
} else if (handle->ticks > LONG_TICKS) {
/* 检测到长按 */
handle->event = (uint8_t)BTN_LONG_PRESS_START;
EVENT_CB(BTN_LONG_PRESS_START);
handle->state = BTN_STATE_LONG_HOLD;
}
break;
case BTN_STATE_RELEASE:
if (handle->button_level == handle->active_level) {
/* 按键再次按下 */
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
if (handle->repeat < PRESS_REPEAT_MAX_NUM) {
handle->repeat++;
}
handle->event = (uint8_t)BTN_PRESS_REPEAT;
EVENT_CB(BTN_PRESS_REPEAT);
handle->ticks = 0;
handle->state = BTN_STATE_REPEAT;
} else if (handle->ticks > SHORT_TICKS) {
/* 超时,判断点击类型 */
if (handle->repeat == 1) {
handle->event = (uint8_t)BTN_SINGLE_CLICK;
EVENT_CB(BTN_SINGLE_CLICK);
} else if (handle->repeat == 2) {
handle->event = (uint8_t)BTN_DOUBLE_CLICK;
EVENT_CB(BTN_DOUBLE_CLICK);
}
handle->state = BTN_STATE_IDLE;
}
break;
case BTN_STATE_REPEAT:
if (handle->button_level != handle->active_level) {
/* 按键释放 */
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
if (handle->ticks < SHORT_TICKS) {
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE;
} else {
handle->state = BTN_STATE_IDLE;
}
} else if (handle->ticks > SHORT_TICKS) {
/* 按住时间过长,视为普通按下 */
handle->ticks = 0;
handle->repeat = 0;
handle->state = BTN_STATE_PRESS;
}
break;
case BTN_STATE_LONG_HOLD:
if (handle->button_level == handle->active_level) {
/* 继续保持按下 */
handle->event = (uint8_t)BTN_LONG_PRESS_HOLD;
EVENT_CB(BTN_LONG_PRESS_HOLD);
} else {
/* 从长按中释放 */
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->state = BTN_STATE_IDLE;
}
break;
default:
/* 无效状态,重置为空闲 */
handle->state = BTN_STATE_IDLE;
break;
}
}
/*
* 函数功能:启动按键工作,将句柄添加到工作链表
* 入口参数handle - 目标句柄结构体指针
* 返回值0 - 成功,-1 - 已存在,-2 - 无效参数
* 函数说明:将按键添加到轮询链表中
*/
int button_start(Button* handle)
{
if (!handle) {
return -2;
}
Button* target = head_handle;
while (target) {
if (target == handle) {
return -1;
}
target = target->next;
}
handle->next = head_handle;
head_handle = handle;
return 0;
}
/*
* 函数功能:停止按键工作,从工作链表移除句柄
* 入口参数handle - 目标句柄结构体指针
* 返回值:无
* 函数说明:将按键从轮询链表中移除
*/
void button_stop(Button* handle)
{
if (!handle) {
return;
}
Button** curr;
for (curr = &head_handle; *curr; ) {
Button* entry = *curr;
if (entry == handle) {
*curr = entry->next;
entry->next = NULL;
return;
} else {
curr = &entry->next;
}
}
}
/*
* 函数功能后台tick处理定时器重复调用间隔5ms
* 入口参数:无
* 返回值:无
* 函数说明:遍历链表调用每个按键的状态机处理函数
*/
void button_ticks(void)
{
Button* target;
Button* next;
target = head_handle;
while (target) {
next = target->next;
button_handler(target);
target = next;
}
}