优雅的按键模块-----Multi-button

​ 在我们日常开发和使用的过程中常常使用了一些按键,利用按键实现不同的功能,比如长按,短按,双击等等。但是每次都是采用标志等等来实现信息的读取,是否有一个优雅的方式来使用按键呢?答案是有的。

## Multi-button

​ 作者的简介是:

MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。

首先来看看头Mul-Button的数据结构

typedef enum {
PRESS_DOWN = 0,
PRESS_UP,
PRESS_REPEAT,
SINGLE_CLICK,
DOUBLE_CLICK,
LONG_PRESS_START,
LONG_PRESS_HOLD,
number_of_event,
NONE_PRESS
}PressEvent; typedef struct Button {
uint16_t ticks;
uint8_t repeat : 4;
uint8_t event : 4;
uint8_t state : 3;
uint8_t debounce_cnt : 3;
uint8_t active_level : 1;
uint8_t button_level : 1;
uint8_t (*hal_button_Level)(void);
BtnCallback cb[number_of_event];
struct Button* next;
}Button;

第一个是枚举类型,像按键按下的类型的枚举

  • PRESS_DOWN

按下

  • PRESS_UP

按下后弹起

  • PRESS_REPEAT

重复按

  • SINGLE_CLICK

单击

  • DOUBLE_CLICK

双击

  • LONG_PRESS_START

长按到一定阈值触发

  • LONG_PRESS_HOLD

长按期间一直触发

第二个是按键对下结构体的定义,解释其中重要的

button_level:有效电平

(*hal_button_Level)(void):按键读取函数

BtnCallback cb:按键对应事件的回调函数

大家有没有注意到

	uint8_t  repeat : 4;
uint8_t event : 4;
uint8_t state : 3;
uint8_t debounce_cnt : 3;
uint8_t active_level : 1;
uint8_t button_level : 1;

这个“:”是结构体中的位域,因为有些后面的数字代表着占据了多少的bit,这个有机会在以后来讲,总之是一个节省内存的方式

接下来看看几个开放出来比较重要的API

/*初始化Button的结构体*/
void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level);
/*结构体和对应事件以及其回调函数的绑定*/
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb);
/*开启按键*/
int button_start(struct Button* handle);
/*按键的时钟*/
void button_ticks(void);

具体的用法如下

首先先创建Button对象

/*
申请三个按键对象
*/
struct Button Button_Up;
struct Button Button_OK;
struct Button Button_Down;

为Button添加时基

/*我这里选择STM32上的定时器11*/
void TIM1_TRG_COM_TIM11_IRQHandler(void)
{
/* USER CODE BEGIN TIM1_TRG_COM_TIM11_IRQn 0 */ /* USER CODE END TIM1_TRG_COM_TIM11_IRQn 0 */
HAL_TIM_IRQHandler(&htim11);
/* USER CODE BEGIN TIM1_TRG_COM_TIM11_IRQn 1 */
button_ticks();
HAL_TIM_Base_Start_IT(&htim11);
/* USER CODE END TIM1_TRG_COM_TIM11_IRQn 1 */
}

为Button创建读取电平的函数,设置其有效电平,并启动对象

/*设置读取电平的函数*/
uint8_t read_button_up()
{
return HAL_GPIO_ReadPin(Button_Up_GPIO_Port,Button_Up_Pin);
}
uint8_t read_button_ok()
{
return HAL_GPIO_ReadPin(Button_OK_GPIO_Port,Button_OK_Pin);
}
uint8_t read_button_down()
{
return HAL_GPIO_ReadPin(Button_Down_GPIO_Port,Button_Down_Pin);
}
/*初始化三个对象,并为其绑定读取函数*/
button_init(&Button_Up, read_button_up, 0);
button_init(&Button_OK, read_button_ok, 0);
button_init(&Button_Down, read_button_down, 0);
/*将对象添加到Button链表*/
button_start(&Button_OK);
button_start(&Button_Up);
button_start(&Button_Down);

为对象的对应事件及其对应时间的函数来绑定

/*为了方便,我这里只举例其中Button_OK*/
/*长按事件触发的函数*/
static void Button_ok_long_press_callback(void *btn)
{
/*将当前页的退出标志位置1*/
Page[Page_Tim_ID].Exit_flag = true;
}
/*绑定到Button_OK的长按事件*/
button_attach(&Button_OK, LONG_PRESS_START, Button_ok_long_press_callback);

然后就可以,效果也是非常的好

其中有几个重要参数在头文件里可以根据自己的情况来修改

#define TICKS_INTERVAL    1	//这个是时基的间隔,单位是ms#define SHORT_TICKS       (50 /TICKS_INTERVAL)//这个是短按的阈值时间#define LONG_TICKS        (500 /TICKS_INTERVAL)//这个是长按的阈值时间

至此multi-Button模块就到这里结束,接下重要的是他的设计思路

int button_start(struct Button* handle){	struct Button* target = head_handle;	while(target) {		if(target == handle) return -1;	//already exist.		target = target->next;	}	handle->next = head_handle;	head_handle = handle;	return 0;}

这个是开启Button的函数,显然是采用链表的形式,每次开启对象都将对象加入链表

void button_ticks(){	struct Button* target;	for(target=head_handle; target; target=target->next) {		button_handler(target);	}}

这个是开启button_ticks()的时基函数,每次触发遍历这个Button的链表,然后去将每个对象传入button_handler()

void button_handler(struct Button* handle){	uint8_t read_gpio_level = handle->hal_button_Level();	//ticks counter working..	if((handle->state) > 0) handle->ticks++;	/*------------button debounce handle---------------*/	if(read_gpio_level != handle->button_level) { //not equal to prev one		//continue read 3 times same new level change		if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {			handle->button_level = read_gpio_level;			handle->debounce_cnt = 0;		}	} else { //leved not change ,counter reset.		handle->debounce_cnt = 0;	}	/*-----------------State machine-------------------*/	switch (handle->state) {	case 0:		if(handle->button_level == handle->active_level) {	//start press down			handle->event = (uint8_t)PRESS_DOWN;			EVENT_CB(PRESS_DOWN);			handle->ticks = 0;			handle->repeat = 1;			handle->state = 1;		} else {			handle->event = (uint8_t)NONE_PRESS;		}		break;	case 1:		if(handle->button_level != handle->active_level) { //released press up			handle->event = (uint8_t)PRESS_UP;			EVENT_CB(PRESS_UP);			handle->ticks = 0;			handle->state = 2;		} else if(handle->ticks > LONG_TICKS) {			handle->event = (uint8_t)LONG_PRESS_START;			EVENT_CB(LONG_PRESS_START);			handle->state = 5;		}		break;	case 2:		if(handle->button_level == handle->active_level) { //press down again			handle->event = (uint8_t)PRESS_DOWN;			EVENT_CB(PRESS_DOWN);			handle->repeat++;			EVENT_CB(PRESS_REPEAT); // repeat hit			handle->ticks = 0;			handle->state = 3;		} else if(handle->ticks > SHORT_TICKS) { //released timeout			if(handle->repeat == 1) {				handle->event = (uint8_t)SINGLE_CLICK;				EVENT_CB(SINGLE_CLICK);			} else if(handle->repeat == 2) {				handle->event = (uint8_t)DOUBLE_CLICK;				EVENT_CB(DOUBLE_CLICK); // repeat hit			}			handle->state = 0;		}		break;	case 3:		if(handle->button_level != handle->active_level) { //released press up			handle->event = (uint8_t)PRESS_UP;			EVENT_CB(PRESS_UP);			if(handle->ticks < SHORT_TICKS) {				handle->ticks = 0;				handle->state = 2; //repeat press			} else {				handle->state = 0;			}		}else if(handle->ticks > SHORT_TICKS){ // long press up			handle->state = 0;		}		break;	case 5:		if(handle->button_level == handle->active_level) {			//continue hold trigger			handle->event = (uint8_t)LONG_PRESS_HOLD;			EVENT_CB(LONG_PRESS_HOLD);		} else { //releasd			handle->event = (uint8_t)PRESS_UP;			EVENT_CB(PRESS_UP);			handle->state = 0; //reset		}		break;	}}

这个则是对传入对象进行对应事件的判断,并且触发对应的事件回调函数,设计的整体式一个状态机的思想,有兴趣的可以自己去看看

OK,码字不易,多多点赞!

开源地址

https://github.com/0x1abin/MultiButton

优雅的按键模块-----Multi-button的更多相关文章

  1. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖

    实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口 ...

  2. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击

    实验三:按键模块② - 点击与长点击 实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作: l 电平变化检测: l 过滤抖动: l 产生有效按键. 实验三我们也会z执行同样的事情 ...

  3. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击

    实验四:按键模块③ - 单击与双击 实验三我们创建了"点击"还有"长点击"等有效按键的多功能按键模块.在此,实验四同样也是创建多功能按键模块,不过却有不同的有效 ...

  4. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验五:按键模块④ — 点击,长点击,双击

    实验五:按键模块④ - 点击,长点击,双击 实验二至实验四,我们一共完成如下有效按键: l 点击(按下有效) l 点击(释放有效) l 长击(长按下有效) l 双击(连续按下有效) 然而,不管哪个实验 ...

  5. Arduino系列之按键模块(二)

    上一节简单介绍啦一下按键模块怎么使用 但是在使用过程中会常常出现延时时间过长,有时候按键会失灵 所以,接下来,我将优化程序,使得按键按下时,就能使count加1 下面是程序思路:同样的定义按键脚: 定 ...

  6. Arduino系列之按键模块(一)

    今天我将简单介绍按键模块计数的原理: 我们常用的按键及按键模块有2脚和4脚的,其内部结构如图所示,当按下按键时就会接通按键两端,当放开时,两端自然断开.                         ...

  7. 外部按键 控制 LED 中断 (参考 http://www.oschina.net/question/565065_115196?sort=time )

    转帖: http://www.oschina.net/question/565065_115196?sort=time 实验目的: mini2440开发板上有6个按键,将其中的前4个按键设为外部中断方 ...

  8. 嵌入式开发之hi3519---GPIO 按键驱动

    摸索了一个星期,终于把海思HI3515开发板的按键中断程序搞出来了,hi3515的核心芯片与网上例子较多的s3c之类的有一些区别,以至于浪费了好些时间去琢磨.管脚配置方式不一样,中断的使用情况也不一样 ...

  9. Qsys配置生成nios系统模块

    1. 本次使用的是别人写好的例程,主要研究学习,使用quartus 11打开工程 2. bdf文件是块编辑器的,相当于原理图,以前只在用NIOS的时候会用到这种方式.接下来新建一个工程,添加原理图元件 ...

随机推荐

  1. Codeforces 1276F - Asterisk Substrings(SAM+线段树合并+虚树)

    Codeforces 题面传送门 & 洛谷题面传送门 SAM hot tea %%%%%%% 首先我们显然可以将所有能够得到的字符串分成六类:\(\varnothing,\text{*},s, ...

  2. 模版 动态 dp

    模版 动态 dp 终于来写这个东西了.. LG 模版:给定 n 个点的数,点有点权, $ m $ 次修改点权,求修改完后这个树的最大独立集大小. 我们先来考虑朴素的最大独立集的 dp \[dp[u][ ...

  3. 洛谷 P5469 - [NOI2019] 机器人(区间 dp+拉格朗日插值)

    洛谷题面传送门 神仙题,放在 D1T2 可能略难了一点( 首先显然对于 P 型机器人而言,将它放在 \(i\) 之后它会走到左边第一个严格 \(>a_i\) 的位置,对于 Q 型机器人而言,将它 ...

  4. CF1208H Red Blue Tree

    CF1208H Red Blue Tree 原本应该放在这里但是这题过于毒瘤..单独开了篇blog 首先考虑如果 $ k $ 无限小,那么显然整个树都是蓝色的.随着 $ k $ 逐渐增大,每个点都会有 ...

  5. NextDenovo 组装基因组

    NextDenovo 是有武汉未来组团队开发出来用于组装ONT,Pacbio, HIFI (默认参数可对60-100X数据更有效),可通过correct--assemble对其进行组装.组装后,每个碱 ...

  6. 【讲座】詹显全——Proteoforms在肿瘤中应用

    Proteoforms/Protein species这个概念还是比较有意义的. 人类结构基因组测序接近尾声,人们就从结构基因组学研究转向功能基因组学研究,即对转录组和蛋白质组进行研究.1995年正式 ...

  7. vector去重--unique

    具体实现见中间源码 function template <algorithm> std::unique equality (1) template <class ForwardIte ...

  8. 【模板】二分图最大匹配(匈牙利算法)/洛谷P3386

    题目链接 https://www.luogu.com.cn/problem/P3386 题目大意 给定一个二分图,其左部点的个数为 \(n\),右部点的个数为 \(m\),边数为 \(e\),求其最大 ...

  9. 华为AppTouch携手全球运营商,助力开发者出海

    内容来源:华为开发者大会2021 HMS Core 6 APP services技术论坛,主题演讲<华为AppTouch携手全球运营商,助力开发者出海>. 演讲嘉宾:华为消费者云服务App ...

  10. 基于 Golang 构建高可扩展的云原生 PaaS(附 PPT 下载)

    作者|刘浩杨 来源|尔达 Erda 公众号 ​ 本文整理自刘浩杨在 GopherChina 2021 北京站主会场的演讲,微信添加:Erda202106,联系小助手即可获取讲师 PPT. 前言 当今时 ...