優雅的按鍵模塊-----Multi-button


優雅的按鍵模塊-----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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM