1 Star 0 Fork 22

月夜风/通用多功能按键

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gm_multi_key.c 14.26 KB
一键复制 编辑 原始数据 按行查看 历史
/*******************************************************************************
** 文件名称:gm_multi_key.c
** 文件作用:多功能按键
** 编写作者:Tom Free 付瑞彪
** 编写时间:2020-05-27
** 文件备注:此文件是多功能按键的源文件,主要包括API的具体实现和数据定义
**
** Copyright (c) 2018-2020 付瑞彪 All Rights Reserved
**
** 1 Tab == 4 Spaces UTF-8 ANSI C Language(C99)
*******************************************************************************/
#include "gm_multi_key.h"
#include "stdio.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* 轮询检测间隔时间,单位:ms,一般5-20ms为宜,
* 太长反应迟钝,太快占用CPU时间 */
#ifndef GM_MULTI_KEY_POLL_INTERVAL
#define GM_MULTI_KEY_POLL_INTERVAL (5u)
#endif /* GM_MULTI_KEY_POLL_INTERVAL */
/* 防抖动滴答数量,时间等于上门的数乘以数量,一般在10-20ms为宜,
* 太少消抖不完全,太长反应迟钝 */
#ifndef GM_MULTI_KEY_DEBOUNCE_TICKS
#define GM_MULTI_KEY_DEBOUNCE_TICKS (3u)
#endif /* GM_MULTI_KEY_DEBOUNCE_TICKS */
/* 连击最长间隔时间,超过此时间认为连击被终结,换算成滴答数 */
#ifndef GM_MULTI_KEY_HIT_AGIN_TICKS
#define GM_MULTI_KEY_HIT_AGIN_TICKS (300u / GM_MULTI_KEY_POLL_INTERVAL)
#endif /* GM_MULTI_KEY_HIT_AGIN_TICKS */
/* 长按触发时间,超过此时间认为进入长按,换算成滴答数 */
#ifndef GM_MULTI_KEY_LONG_TICKS
#define GM_MULTI_KEY_LONG_TICKS (1000u / GM_MULTI_KEY_POLL_INTERVAL)
#endif /* GM_MULTI_KEY_LONG_TICKS */
/* 长按重复触发时间,即隔多长时间触发一次,换算成滴答数 */
#ifndef GM_MULTI_KEY_REPEAT_TRIG_TICKS
#define GM_MULTI_KEY_REPEAT_TRIG_TICKS (100u / GM_MULTI_KEY_POLL_INTERVAL)
#endif /* GM_MULTI_KEY_REPEAT_TRIG_TICKS */
/* 连击最大次数,主要是怕数据溢出 */
#ifndef GM_MULTI_KEY_CLICK_COUNT_MAX
#define GM_MULTI_KEY_CLICK_COUNT_MAX UINT8_MAX
#endif /* GM_MULTI_KEY_CLICK_COUNT_MAX */
/* 按键状态定义 */
typedef enum _gm_multi_key_fsm_status_t
{
/* 按键已释放状态 */
GM_MULTI_KEY_FSM_STATUS_RELEASE,
/* 按键已按下状态 */
GM_MULTI_KEY_FSM_STATUS_PRESS,
/* 按键松开,等待再次按下,用于判断是否连击 */
GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN,
/* 按键再次按下状态,此时不能再响应长按,
* 所以和GM_MULTI_KEY_FSM_STATUS_PRESS处理不同 */
GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN,
/* 等待按键释放状态 */
GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE,
/* 总状态数量 */
GM_MULTI_KEY_FSM_STATUS_NUM,
} gm_multi_key_fsm_status_t;
/* 按键管理器 */
typedef struct _gm_multi_key_mgr_t
{
/* 按键链表头节点 */
gm_multi_key_t *p_list_head;
/* 链表长度,仅用于统计节点个数 */
uint16_t list_length;
} gm_multi_key_mgr_t;
/* 执行事件回调 */
#define GM_MULTI_KEY_EVENT_PROC(p_key) \
do \
{ \
if (p_key->event_proc_cb != NULL) \
{ \
p_key->event_proc_cb(p_key); \
} \
} while (0)
/* 管理器参数,为了防止编译器不支持C99,所以采用标准的初始化 */
static gm_multi_key_mgr_t multi_key_mgr =
{
/* .p_list_head = */ NULL,
/* .list_length */ 0,
};
/* 按键管理器初始化 */
void gm_multi_key_mgr_init(void)
{
/* 初始化头指针为空 */
multi_key_mgr.p_list_head = NULL;
}
/* 按键对象初始化 */
gm_multi_key_result_t gm_multi_key_init(gm_multi_key_t *p_key,
gm_multi_key_read_io_cb_t *read_io_cb,
gm_multi_key_event_cb_t *event_cb)
{
if ((p_key == NULL) ||
(read_io_cb == NULL) ||
(event_cb == NULL))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
/* 初始化参数 */
p_key->next = NULL;
p_key->read_io_cb = read_io_cb;
p_key->event_proc_cb = event_cb;
p_key->click_cnt_max = GM_MULTI_KEY_CLICK_COUNT_MAX;
p_key->debounce_ticks = GM_MULTI_KEY_DEBOUNCE_TICKS;
p_key->hit_again_ticks = GM_MULTI_KEY_HIT_AGIN_TICKS;
p_key->long_press_ticks = GM_MULTI_KEY_LONG_TICKS;
p_key->long_repeat_ticks = GM_MULTI_KEY_REPEAT_TRIG_TICKS;
p_key->internal_cnt = 0;
p_key->debounce_cnt = 0;
p_key->click_cnt = 0;
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
p_key->event = GM_MULTI_KEY_EVENT_NONE;
/* 读取一次电平并保存 */
p_key->level = p_key->read_io_cb(p_key);
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置读取IO回调 */
gm_multi_key_result_t gm_multi_key_set_read_io_cb(gm_multi_key_t *p_key,
gm_multi_key_read_io_cb_t *read_io_cb)
{
if ((p_key == NULL) ||
(read_io_cb == NULL))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->read_io_cb = read_io_cb;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置事件处理回调 */
gm_multi_key_result_t gm_multi_key_set_event_cb(gm_multi_key_t *p_key,
gm_multi_key_event_cb_t *event_cb)
{
if ((p_key == NULL) ||
(event_cb == NULL))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->event_proc_cb = event_cb;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置连击最大次数 */
gm_multi_key_result_t gm_multi_key_set_click_cnt_max(gm_multi_key_t *p_key,
uint8_t click_cnt_max)
{
if ((p_key == NULL) ||
(click_cnt_max == 0))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->click_cnt_max = click_cnt_max;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置防抖周期 */
gm_multi_key_result_t gm_multi_key_set_debounce_ticks(gm_multi_key_t *p_key,
uint8_t debounce_ticks)
{
if ((p_key == NULL) ||
(debounce_ticks == 0))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->debounce_ticks = debounce_ticks;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置连击最大间隔时间周期 */
gm_multi_key_result_t gm_multi_key_set_hit_again_ticks(gm_multi_key_t *p_key,
uint8_t hit_again_ticks)
{
if ((p_key == NULL) ||
(hit_again_ticks == 0))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->hit_again_ticks = hit_again_ticks;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置长按触发时间周期 */
gm_multi_key_result_t gm_multi_key_set_long_press_ticks(gm_multi_key_t *p_key,
uint8_t long_press_ticks)
{
if ((p_key == NULL) ||
(long_press_ticks == 0))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->long_press_ticks = long_press_ticks;
return GM_MULTI_KEY_RESULT_OK;
}
/* 设置长按重复按下事件触发时间周期 */
gm_multi_key_result_t gm_multi_key_set_long_repeat_ticks(gm_multi_key_t *p_key,
uint8_t long_repeat_ticks)
{
if ((p_key == NULL) ||
(long_repeat_ticks == 0))
{
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
p_key->long_repeat_ticks = long_repeat_ticks;
return GM_MULTI_KEY_RESULT_OK;
}
/* 添加按键对象到按键链表中 */
gm_multi_key_result_t gm_multi_key_add(gm_multi_key_t* p_key)
{
gm_multi_key_t* target;
if (p_key == NULL)
{
/* 指针非法,返回参数错误 */
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
/* 搜寻是否已存在于链表中 */
target = multi_key_mgr.p_list_head;
while (target != NULL)
{
if (target == p_key)
{
/* 已存在 */
return GM_MULTI_KEY_RESULT_EXIST;
}
target = target->next;
}
/* 未搜索到,添加到头部 */
p_key->next = multi_key_mgr.p_list_head;
/* 更新头节点 */
multi_key_mgr.p_list_head = p_key;
/* 链表数量加1 */
multi_key_mgr.list_length++;
return GM_MULTI_KEY_RESULT_OK;
}
/* 从按键链表中移除按键对象 */
gm_multi_key_result_t gm_multi_key_remove(gm_multi_key_t* p_key)
{
gm_multi_key_t* ptemp;
gm_multi_key_t* prev;
if (p_key == NULL)
{
/* 指针非法 */
return GM_MULTI_KEY_RESULT_ARGS_ERR;
}
if ((multi_key_mgr.list_length == 0) ||
(multi_key_mgr.p_list_head == NULL))
{
/* 链表空,未搜索到 */
return GM_MULTI_KEY_RESULT_NOT_FOUND;
}
if (multi_key_mgr.p_list_head == p_key)
{
/* 如果需要删除的节点位于链表头,需要更新链表头指针 */
multi_key_mgr.p_list_head = multi_key_mgr.p_list_head->next;
/* 保证删除节点的指针安全 */
p_key->next = NULL;
return GM_MULTI_KEY_RESULT_OK;
}
/* 从第二个开始搜索 */
prev = multi_key_mgr.p_list_head;
ptemp = multi_key_mgr.p_list_head->next;
for (; ptemp != NULL; ptemp = ptemp->next)
{
if (ptemp == p_key)
{
/* 搜索到,删除节点 */
prev->next = ptemp->next;
/* 保证删除节点的指针安全 */
ptemp->next = NULL;
return GM_MULTI_KEY_RESULT_OK;
}
prev = ptemp;
}
/* 未搜索到 */
return GM_MULTI_KEY_RESULT_NOT_FOUND;
}
/* 轮询一个按键 */
static void gm_multi_key_poll_one_key(gm_multi_key_t* p_key)
{
/* 读取电平 */
gm_multi_key_level_t key_level = p_key->read_io_cb(p_key);
/* 消抖 */
if (key_level != p_key->level)
{
if (++(p_key->debounce_cnt) >= p_key->debounce_ticks)
{
p_key->level = key_level;
p_key->debounce_cnt = 0;
}
}
else
{
p_key->debounce_cnt = 0;
}
/* 默认按键无事件 */
p_key->event = GM_MULTI_KEY_EVENT_NONE;
/* 状态机 */
switch (p_key->fsm_status)
{
case GM_MULTI_KEY_FSM_STATUS_RELEASE:
if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
{
/* 按键按下事件 */
p_key->event = GM_MULTI_KEY_EVENT_PRESS;
/* 清除计数器 */
p_key->internal_cnt = 0;
/* 清除连击计数 */
p_key->click_cnt = 1;
/* 切换到按键按下状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_PRESS;
}
break;
case GM_MULTI_KEY_FSM_STATUS_PRESS:
if (p_key->level != GM_MULTI_KEY_LEVEL_PRESS)
{
/* 未达到长按时间之前抬起,按键松开事件 */
p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
/* 清除计数器 */
p_key->internal_cnt = 0;
/* 切换到等待连击状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN;
}
else if (++(p_key->internal_cnt) > p_key->long_press_ticks)
{
/* 超过长按时间还未松开,首次触发长按事件 */
p_key->event = GM_MULTI_KEY_EVENT_LONG_FIRST;
/* 切换到等待按键松开状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE;
}
break;
case GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN:
if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
{
/* 按键按下事件 */
p_key->event = GM_MULTI_KEY_EVENT_PRESS;
/* 连击计数加1 */
if (p_key->click_cnt < p_key->click_cnt_max)
{
p_key->click_cnt++;
}
/* 清除计数器 */
p_key->internal_cnt = 0;
/* 切换到按键再次按下状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN;
}
else if (++(p_key->internal_cnt) > p_key->hit_again_ticks)
{
/* 超过连击间隔时间还未再次按下按键 */
if (p_key->click_cnt == 1)
{
/* 仅点击了一次 */
p_key->event = GM_MULTI_KEY_EVENT_SINGLE_CLICK;
}
else if (p_key->click_cnt == 2)
{
/* 双击 */
p_key->event = GM_MULTI_KEY_EVENT_DOUBLE_CLICK;
}
else
{
/* 多次连击 */
p_key->event = GM_MULTI_KEY_EVENT_MULTI_CLICK;
}
/* 切换到按键抬起状态,此次按键流程结束 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
}
break;
case GM_MULTI_KEY_FSM_STATUS_PRESS_AGIN:
if (p_key->level != GM_MULTI_KEY_LEVEL_PRESS)
{
/* 按键松开事件 */
p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
/* 清除计数器 */
p_key->internal_cnt = 0;
/* 切换到等待按键再次按下状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_WAIT_PRESS_AGIN;
}
break;
case GM_MULTI_KEY_FSM_STATUS_WAIT_RELEASE:
if (p_key->level == GM_MULTI_KEY_LEVEL_PRESS)
{
if (++(p_key->internal_cnt) > p_key->long_repeat_ticks)
{
/* 清除计数 */
p_key->internal_cnt = 0;
/* 继续按下,按键重复按下事件 */
p_key->event = GM_MULTI_KEY_EVENT_REPEAT;
}
}
else
{
/* 按键松开,按键抬起事件 */
p_key->event = GM_MULTI_KEY_EVENT_RELEASE;
/* 清除计数器 */
p_key->internal_cnt = 0;
/* 清除连击计数 */
p_key->click_cnt = 0;
/* 回到释放状态 */
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
}
break;
default:
/* 异常,回到释放状态 */
p_key->internal_cnt = 0;
p_key->click_cnt = 0;
p_key->fsm_status = GM_MULTI_KEY_FSM_STATUS_RELEASE;
break;
}
/* 有事件产生,执行回调 */
if (p_key->event != GM_MULTI_KEY_EVENT_NONE)
{
GM_MULTI_KEY_EVENT_PROC(p_key);
}
}
/* 轮询所有按键 */
void gm_multi_key_poll(void)
{
gm_multi_key_t* p_key;
for (p_key = multi_key_mgr.p_list_head; p_key != NULL; p_key = p_key->next)
{
gm_multi_key_poll_one_key(p_key);
}
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/yyf123321/gm_multi_key.git
git@gitee.com:yyf123321/gm_multi_key.git
yyf123321
gm_multi_key
通用多功能按键
master

搜索帮助