EmbeddedButton,嵌入式按键处理框架
? 简介
EmbeddedButton是一个轻量级简单易用的嵌入式按键处理框架,可无限拓展按键;
- 支持多连击、长按、短按长按等多种按键事件;
- 模块通过几个简单原则完成了整个代码逻辑的支撑;
- 核心处理采取数据驱动方式,支持位运算键值匹配,仅内置基本键值定义,其他键值含义由用户通过配置键值匹配规则自行定义,而不用侵入式修改代码,灵活性极强;
? 特性
1.依靠简单几个原则,支持起整个按键判断逻辑
- 只要键值非零,时间tick++
- 只要按键状态发生变化,改变一次键值(__append_bit()),tick时间清零(确保tick为按下或抬起的时间)
- 以tick时间的长短及按键抬起作为一次状态结束的判断依据,可以很好的实现短按长按等操作;
2.使用C语言实现,巧妙利用位运算来实现每个按键键值的二进制记录表示,1代表按下,0代表松开
键值 |
说明 |
---|
0b0 |
未按下 |
0b010 |
单击 |
0b01010 |
双击 |
0b01010...n |
n连击 |
0b011 |
长按开始 |
0b0111 |
长按保持 |
0b01110 |
长按结束 |
0b01011 |
短按然后长按 |
0b0101011 |
双击然后长按 |
0b01010..n11 |
n连击然后长按 |
3.核心处理采取数据驱动方式,支持位运算键值匹配:
typedef struct {
key_value_type_t operand;
kv_match_operator_type_t operator;
key_value_type_t tar_result;
void (*kv_func_cb)(void*);
} key_value_match_map_t;
key_value_type_t operand_origin = button->kv_match_map_ptr[i].operand;
key_value_type_t operand_result = button->kv_match_map_ptr[i].operand;
kv_match_operator_type_t operator =button->kv_match_map_ptr[i].operator;
key_value_type_t tar_result = button->kv_match_map_ptr[i].tar_result;
if(operator == KV_MATCH_OPERATOR_NULL)
operand_result = button->key_value;
else if(operator & KV_MATCH_OPERATOR_BITWISE_AND)
operand_result = (operand_origin & button->key_value);
else if(operator & KV_MATCH_OPERATOR_BITWISE_OR)
operand_result = (operand_origin | button->key_value);
else if(operator & KV_MATCH_OPERATOR_BITWISE_NOT)
operand_result = ~(button->key_value);
else if(operator & KV_MATCH_OPERATOR_BITWISE_XOR)
operand_result = (operand_origin ^ button->key_value);
if(operand_result == tar_result)
{
button->kv_match_map_ptr[i].kv_func_cb(button);
}
#define KV_MATCH_OPERATOR_NULL (0)
#define KV_MATCH_OPERATOR_BITWISE_AND (1 << 0)
#define KV_MATCH_OPERATOR_BITWISE_OR (1 << 1)
#define KV_MATCH_OPERATOR_BITWISE_NOT (1 << 2)
#define KV_MATCH_OPERATOR_BITWISE_XOR (1 << 2)
4.基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理
? 如何食用
1)使用
点击展开/折叠C代码
#include "embedded_button.h"
struct button_obj_t button1;
uint8_t read_button_pin(uint8_t button_id)
{
switch(button_id)
{
case 0:
return get_button1_value();
break;
default:
return 0;
break;
}
return 0;
}
void single_click_handle(void* btn)
{
printf("/****single click****/\r\n");
}
void double_click_handle(void* btn)
{
printf("/****double click****/\r\n");
}
void long_press_handle(void* btn)
{
printf("/****long press****/\r\n");
}
void single_click_then_long_press_handle(void* btn)
{
printf("/****single click and long press****/\r\n");
}
void quintuple_click_handle(void* btn)
{
if(check_is_repeat_click_mode(btn))
printf("/****quintuple click****/\r\n");
}
const key_value_match_map_t button1_map[] =
{
{
.tar_result = SINGLE_CLICK_KV,
.kv_func_cb = single_click_handle
},
{
.tar_result = DOUBLE_CLICK_KV,
.kv_func_cb = double_click_handle
},
{
.tar_result = LONG_PRESEE_START,
.kv_func_cb = long_press_handle
},
{
.tar_result = SINGLE_CLICK_THEN_LONG_PRESS_KV,
.kv_func_cb = single_click_then_long_press_handle
},
{
.operand = 0b1010101010,
.operator = KV_MATCH_OPERATOR_BITWISE_AND,
.tar_result = 0b1010101010,
.kv_func_cb = quintuple_click_handle
}
};
int main()
{
button_init(&button1, read_button_pin, 0, 0, button1_map, ARRAY_SIZE(button1_map));
button_start(&button1);
__timer_start(button_ticks, 0, 5);
while(1)
{}
}
2)调试
点击展开/折叠
- 定义EB_DEBUG_PRINTF宏后将会开启键值打印,例如下面,需要将printf换成你的打印函数:
#define EB_DEBUG_PRINTF printf
⚡ 其他
- 本项目基于本人实际开发中遇到的一些按键驱动使用体验问题,在他人项目(见参考链接)的思想基础上,开发的此按键驱动模块,之前提到了本模块的优势,下面说下有待改进的地方:对于多按键时组合按键的表示方式,目前还没有想到比较优雅的实现方式,后续有头绪后会进一步改进,补齐这一环。最后,感谢帮助思考我的小伙伴shawnfeng0以及正在使用此模块的小伙伴,欢迎一起开发改进!
- 更多高级用法见 examples
? 参考链接