列举三个button按钮的开放功能(一个优雅的按键模块)

在我们日常开发和使用的过程中常常使用了一些按键,利用按键实现不同的功能,比如长按,短按,双击等等但是每次都是采用标志等等来实现信息的读取,是否有一个优雅的方式来使用按键呢?答案是有的,下面我们就来说一说关于列举三个button按钮的开放功能?我们一起去了解并探讨一下这个问题吧!

列举三个button按钮的开放功能(一个优雅的按键模块)

列举三个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

如果本文对你有帮助,麻烦转发关注支持一下

原文地址:https://www.cnblogs.com/ssj-blog/p/15728105.html

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页