【开源移植】MultiButton_小型按键驱动模块移植

avatar
作者
筋斗云
阅读量:0

MultiButton

简介

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

使用方法

1.先申请一个按键结构

struct Button button1; 

2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平

button_init(&button1, read_button_pin, 0, 0); 

3.注册按键事件

button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler); button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler); ... 

4.启动按键

button_start(&button1); 

5.设置一个5ms间隔的定时器循环调用后台处理函数

while(1) {     ...     if(timer_ticks == 5) {         timer_ticks = 0;          button_ticks();     } } 

特性

MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理:

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  button_id; 	uint8_t  (*hal_button_Level)(uint8_t  button_id_); 	BtnCallback  cb[number_of_event]; 	struct Button* next; }; 

这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。

按键事件

事件说明
PRESS_DOWN按键按下,每次按下都触发
PRESS_UP按键弹起,每次松开都触发
PRESS_REPEAT重复按下触发,变量repeat计数连击次数
SINGLE_CLICK单击按键事件
DOUBLE_CLICK双击按键事件
LONG_PRESS_START达到长按时间阈值时触发一次
LONG_PRESS_HOLD长按期间一直触发

Examples

#include "button.h"  unit8_t btn1_id = 0;  struct Button btn1;  uint8_t read_button_GPIO(uint8_t button_id) { 	// you can share the GPIO read function with multiple Buttons 	switch(button_id) 	{ 		case btn1_id: 			return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin); 			break;  		default: 			return 0; 			break; 	} } void BTN1_PRESS_DOWN_Handler(void* btn) { 	//do something... }  void BTN1_PRESS_UP_Handler(void* btn) { 	//do something... }  ...  int main() { 	button_init(&btn1, read_button_GPIO, 0, btn1_id); 	button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler); 	button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler); 	button_attach(&btn1, PRESS_REPEAT,     BTN1_PRESS_REPEAT_Handler); 	button_attach(&btn1, SINGLE_CLICK,     BTN1_SINGLE_Click_Handler); 	button_attach(&btn1, DOUBLE_CLICK,     BTN1_DOUBLE_Click_Handler); 	button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); 	button_attach(&btn1, LONG_PRESS_HOLD,  BTN1_LONG_PRESS_HOLD_Handler); 	button_start(&btn1);  	//make the timer invoking the button_ticks() interval 5ms. 	//This function is implemented by yourself. 	__timer_start(button_ticks, 0, 5);  	while(1) 	{} } 

开源移植 STM32

源文件main.c

#include "stm32f10x.h" #include "Delay.h" #include "LED.h" #include "KEY.h" #include "multi_button.h"  int main() { 	LED_Init(); // LED初始化 	KEY_Init(); // 按键初始化  	while (1) 	{ 		Delay_ms(5); 		button_ticks();// 按键运行 	} }  

源文件multi_button.c

/*  * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>  * All rights reserved  */  #include "multi_button.h"  #define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((void*)handle) #define PRESS_REPEAT_MAX_NUM  15 /*!< The maximum value of the repeat counter */  //button handle list head. static struct Button* head_handle = NULL;  static void button_handler(struct Button* handle);  /**   * @brief  Initializes the button struct handle.   * @param  handle: the button handle struct.   * @param  pin_level: read the HAL GPIO of the connected button level.   * @param  active_level: pressed GPIO level.   * @param  button_id: the button id.   * @retval None   */ void button_init(struct Button* handle, u8(*pin_level)(u8), u8 active_level, u8 button_id) { 	memset(handle, 0, sizeof(struct Button)); 	handle->event = (u8)NONE_PRESS; 	handle->hal_button_Level = pin_level; 	handle->button_level = handle->hal_button_Level(button_id); 	handle->active_level = active_level; 	handle->button_id = button_id; }  //  /*button1 init*/ //  button_init(&btn1, read_button_GPIO, 0, KEY1_id);                      // 初始化按键结构体 //  button_attach(&btn1, SINGLE_CLICK, BTN1_SINGLE_CLICK_Handler);         // 添加单击事件 //  button_attach(&btn1, DOUBLE_CLICK, BTN1_DOUBLE_CLICK_Handler);         // 添加双击事件 //  button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); // 添加长按事件 //  button_start(&btn1);                                                   // 启动按键    /**   * @brief  Attach the button event callback function.   * @param  handle: the button handle struct.   * @param  event: trigger event type.   * @param  cb: callback function.   * @retval None   */ void button_attach(struct Button* handle, PressEvent event, BtnCallback7 cb) { 	handle->cb[event] = cb; }  /**   * @brief  Inquire the button event happen.   * @param  handle: the button handle struct.   * @retval button event.   */ PressEvent get_button_event(struct Button* handle) { 	return (PressEvent)(handle->event); }  /**   * @brief  Button driver core function, driver state machine.   * @param  handle: the button handle struct.   * @retval None   */ static void button_handler(struct Button* handle) { 	u8 read_gpio_level = handle->hal_button_Level(handle->button_id);  	//ticks counter working..4 	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 { //level 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 = (u8)PRESS_DOWN; 			EVENT_CB(PRESS_DOWN); 			handle->ticks = 0; 			handle->repeat = 1; 			handle->state = 1; 		} else { 			handle->event = (u8)NONE_PRESS; 		} 		break;  	case 1: 		if(handle->button_level != handle->active_level) { //released press up 			handle->event = (u8)PRESS_UP; 			EVENT_CB(PRESS_UP); 			handle->ticks = 0; 			handle->state = 2; 		} else if(handle->ticks > LONG_TICKS) { 			handle->event = (u8)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 = (u8)PRESS_DOWN; 			EVENT_CB(PRESS_DOWN); 			if(handle->repeat != PRESS_REPEAT_MAX_NUM) { 				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 = (u8)SINGLE_CLICK; 				EVENT_CB(SINGLE_CLICK); 			} else if(handle->repeat == 2) { 				handle->event = (u8)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 = (u8)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) { // SHORT_TICKS < press down hold time < LONG_TICKS 			handle->state = 1; 		} 		break;  	case 5: 		if(handle->button_level == handle->active_level) { 			//continue hold trigger 			handle->event = (u8)LONG_PRESS_HOLD; 			EVENT_CB(LONG_PRESS_HOLD); 		} else { //released 			handle->event = (u8)PRESS_UP; 			EVENT_CB(PRESS_UP); 			handle->state = 0; //reset 		} 		break; 	default: 		handle->state = 0; //reset 		break; 	} }  /**   * @brief  Start the button work, add the handle into work list.   * @param  handle: target handle struct.   * @retval 0: succeed. -1: already exist.   */ 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; }  /**   * @brief  Stop the button work, remove the handle off work list.   * @param  handle: target handle struct.   * @retval None   */ void button_stop(struct Button* handle) { 	struct Button** curr; 	for(curr = &head_handle; *curr; ) { 		struct Button* entry = *curr; 		if(entry == handle) { 			*curr = entry->next; //			free(entry); 			return;//glacier add 2021-8-18 		} else { 			curr = &entry->next; 		} 	} }  /**   * @brief  background ticks, timer repeat invoking interval 5ms.   * @param  None.   * @retval None   */ void button_ticks(void) { 	struct Button* target; 	for(target=head_handle; target; target=target->next) { 		button_handler(target); 	} }  

头文件multi_button.h

/*  * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>  * All rights reserved  */  #ifndef _MULTI_BUTTON_H_ #define _MULTI_BUTTON_H_  #include <string.h> #include "stm32f10x.h"  //According to your need to modify the constants. #define TICKS_INTERVAL    5	//ms  #define DEBOUNCE_TICKS    3	//MAX 7 (0 ~ 7) #define SHORT_TICKS       (300 /TICKS_INTERVAL) #define LONG_TICKS        (1000 /TICKS_INTERVAL)   typedef void (*BtnCallback)(void*);  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 { 	u16 ticks; 	u8  repeat; 	u8  event; 	u8  state; 	u8  debounce_cnt; 	u8  active_level; 	u8  button_level; 	u8  button_id; 	u8  (*hal_button_Level)(u8 button_id_); 	BtnCallback  cb[number_of_event]; 	struct Button* next; }Button;   void button_init(struct Button* handle, u8(*pin_level)(u8), u8 active_level, u8 button_id); void button_attach(struct Button* handle, PressEvent event, BtnCallback cb); PressEvent get_button_event(struct Button* handle); int  button_start(struct Button* handle); void button_stop(struct Button* handle); void button_ticks(void);   #endif  

源文件KEY2.C

/**  ******************************************************************************  * @file    KEY2.c  * @author  LQ  * @version  * @date    2024-4-29  * @brief   multi_button  ******************************************************************************  * @attention  *  ******************************************************************************  */  #include "stm32f10x.h" #include "KEY.h" #include "LED.h"  #ifdef USE_KEY2  struct Button btn1; struct Button btn2;  // GPIO读取回调函数 u8 read_button_GPIO(u8 button_id) {   u8 read_v;   switch (button_id)   {   case KEY1_id:     read_v = GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN);     break;   case KEY2_id:     read_v = GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN);     break;   default:     break;   }    return read_v; }  // 单击事件 void BTN1_SINGLE_CLICK_Handler(void *btn) {   switch (((Button *)btn)->button_id)   {   case KEY1_id:     LED_ON(LED1);     break;   case KEY2_id:     LED_OFF(LED1);     break;    default:     break;   } } // 双击事件 void BTN1_DOUBLE_CLICK_Handler(void *btn) {   LED_Toggle(LED1); }  // 长按事件 void BTN1_LONG_PRESS_START_Handler(void *btn) {   LED_OFF(LED1); }  /**  * @brief  KEY初始化  * @param  None  * @retval None  */ void KEY_Init(void) {   /* GPIO_KEY Clock */   RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);   RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK, ENABLE);   /* GPIO_KEY Pin */   GPIO_InitTypeDef GPIO_InitStructure;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;   GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);   GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;   GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);    /*button1 init*/   button_init(&btn1, read_button_GPIO, 0, KEY1_id);                      // 初始化按键结构体   button_attach(&btn1, SINGLE_CLICK, BTN1_SINGLE_CLICK_Handler);         // 添加单击事件   button_attach(&btn1, DOUBLE_CLICK, BTN1_DOUBLE_CLICK_Handler);         // 添加双击事件   button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); // 添加长按事件   button_start(&btn1);                                                   // 启动按键                                                //    /*button2 init*/   button_init(&btn2, read_button_GPIO, 0, KEY2_id);   button_attach(&btn2, SINGLE_CLICK, BTN1_SINGLE_CLICK_Handler);   button_attach(&btn2, DOUBLE_CLICK, BTN1_DOUBLE_CLICK_Handler);   button_start(&btn2); }  #endif  

头文件KEY.h

/**  ******************************************************************************  * @file    KEY.h  * @author  LQ  * @version V1.0  * @date    2023-4-29  * @brief   multi_button  ******************************************************************************  * @attention  *  ******************************************************************************  */  #ifndef _KEY_H #define _KEY_H  #define USE_KEY2 // 定义使用KEY2.c,注释则使用KEY1.c  #ifndef USE_KEY2  // 如果USE_KEY2没有被定义就定义名为USE_KEY1的宏 #define USE_KEY1 #endif   #include "multi_button.h"  /*GPIO宏定义*/ #define KEY1_GPIO_PORT GPIOA #define KEY1_GPIO_PIN GPIO_Pin_8 #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA  #ifdef USE_KEY2  // 如果USE_KEY2被定定义 #define KEY2_GPIO_PORT GPIOA #define KEY2_GPIO_PIN GPIO_Pin_9 #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOA  enum Button_IDs { 	KEY1_id, 	KEY2_id, }; #endif  void KEY_Init(void);  #endif  

源文件KEY1.C

/**  ******************************************************************************  * @file    KEY1.c  * @author  LQ  * @version  * @date    2024-4-29  * @brief   multi_button  ******************************************************************************  * @attention  *  ******************************************************************************  */  #include "stm32f10x.h" #include "KEY.h" #include "LED.h"  #ifdef USE_KEY1  struct Button btn1;  // GPIO读取回调函数 u8 read_button_GPIO(u8 button_id) {   u8 led_sta;   led_sta = GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN);   return led_sta; }  // 单击事件 void BTN1_SINGLE_CLICK_Handler(void *btn) {   LED_ON(LED1); }  // 双击事件 void BTN1_DOUBLE_CLICK_Handler(void *btn) {   LED_Toggle(LED1); }  // 长按事件 void BTN1_LONG_PRESS_START_Handler(void *btn) {   LED_OFF(LED1); }  /**  * @brief  KEY初始化  * @param  None  * @retval None  */ void KEY_Init(void) {   /* GPIO_KEY Clock */   RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);    /* GPIO_KEY pin */   GPIO_InitTypeDef GPIO_InitStructure;   GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);    /*button init*/   button_init(&btn1, read_button_GPIO, 0, 0);                            // 初始化按键结构体   button_attach(&btn1, SINGLE_CLICK, BTN1_SINGLE_CLICK_Handler);         // 添加单击事件   button_attach(&btn1, DOUBLE_CLICK, BTN1_DOUBLE_CLICK_Handler);         // 添加双击事件   button_attach(&btn1, LONG_PRESS_START, BTN1_LONG_PRESS_START_Handler); // 添加长按事件   button_start(&btn1);                                                   // 启动按键 }  #endif  

参考资料

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!