【多功能按键MultiButton模块使用解析,按键扫描,按键事件】

按键扫描是并不难实现的功能,但是想要优雅的处理按键消抖,以及实现按键的各种事件(按下响应应,松手响应,单击/双击响应,长按响应等),仍然需要花费一定的心思来实现.

本篇文章以第一视角解读使用开源按键库MultiButton,轻松实现上述按键事件。
开源地址放在了文章的最下方

在开始之前,要了解如何定义函数指针,以及如何调用函数指针

//定义了一个新的类型 BtnCallback,该类型的变量指向 返回值为void,成员变量为void*的函数
//后面讲到的回调函数基于此
typedef void (*BtnCallback)(void*); 

//在核心逻辑函数中,有调用到一行代码
#define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((Button*)handle)
//简化 if(	handle->cb[ev](handle)	)	当handle->cb[ev](handle) 不为空时,会调用该变量指向的函数

接下来正式开始讲解几个简单的函数:
MultiButton有两个核心文件multibutton_master.c和multibutton.h 我们以面向对象的思想来将按键用结构体表示

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  button_id;             //按键id
	uint8_t  (*hal_button_Level)(uint8_t button_id_);  //一个函数指针
	BtnCallback  cb[number_of_event];                  //按键事件
	struct Button* next;			//按键使用链表存储
}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;

正文开始,按键结构体看起来定义的很复杂,但是使用起来却很简单,我们只需要实现三个函数即可

/**
  * @brief  初始化按键结构体变量handle
  * @param  handle: 按键结构体变量
  * @param  pin_level: 读取按键电平的函数(这里需要定义一个指定类型的函数来获取按键电平)
  * @param  active_level: 按键按下时的电平
  * @param  button_id: 自定义的按键ID
  * @retval None
  */
void button_init(struct Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
	memset(handle, 0, sizeof(struct Button));
	handle->event = (uint8_t)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;
}

第二步:将当前按键与按键事件相关联

/**
  * @brief  Attach the button event callback function.
  * @param  handle: 定义的按键结构体
  * @param  event: 按键事件,想要一个按键触发多个事件时,可以多次调用该函数
  * @param  cb: 回调函数(需要人为定义)
  * @retval None
  */
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
{
	handle->cb[event] = cb;
}

第三步:该按键已经初始化完成,我们需要将该按键加入到按键链表之中

/**
  * @brief  将(Button*)handle 添加到链表头部
  * @param  handle: target handle struct.
  * @retval 0: succeed. -1: already exist.
  */
int button_start(struct Button* handle)
{
	struct Button* target = head_handle;
	while(target) {	//如果当前结构体已经在链表中存在,返回-1
		if(target == handle) return -1;	//already exist.
		target = target->next;
	}
	handle->next = head_handle;
	head_handle = handle;
	return 0;
}

到这里,基本的步骤我们已经完成,只需要定时扫描链表即可

/**
  * @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);	//核心逻辑实现,按键回调事件在该函数中调用(满足条件)
		//实现逻辑并不复杂,我就不贴我写的注释了.只需要关注回调函数是如何实现的即可.
	}
}

接下来看用户代码如何调用:

#define kb1_id 0  //按键ID
struct Button kb1; //定义按键结构体
/* 定义的按键电平读取函数 */
uint8_t read_button_GPIO(uint8_t button_id)
{
	
	switch(button_id){
		case kb1_id:
			return HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		default:
			return 1;
	}
}
/* 按键注册,重点关注这里 */
void button_register()
{
	button_init(&kb1,read_button_GPIO,0,kb1_id);
	/* 注册单击回调事件 */
	button_attach(&kb1,(PressEvent)SINGLE_CLICK,(BtnCallback)KB1_SingleHandler);
	button_start(&kb1);	//将该按键条件到按键链表之中
}
/* 按键回调事件 */
void KB1_SingleHandler(void *btn)
{
	printf("按下kb1\r\n");
}

// 钩子函数,用于定时,每5ms进入一次按键扫描函数
/*	我在定时器中进行计时,每1ms调用1次该函数 */
void vbtn_timer_hook(void)
{
	static u8 cnt = 0;
	if(cnt++ == 5)
	{
		button_ticks();
		cnt = 0;
	}
}
/* Timer的定时中断回调 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

  /*1ms定时器,每1ms执行一次 */
	if(htim->Instance == TIM8)
	{
		extern void vbtn_timer_hook(void); //引用和调用钩子函数,这么写主要是为了接耦合
		//但是不建议将具体的逻辑实现在中断中编写,这样会影响运行效率!!!!!!
		//可以尝试,只在这里设置标志位,在while()中进行判断执行
		vbtn_timer_hook();
	}
  /* USER CODE END Callback 1 */
}

到这里,每当点击一次KB1,就会执行一次KB1_SingleHandler()函数,耦合性很低,非常适合移植,感谢大大:https://github.com/0x1abin/MultiButton
点击直达
如果访问不了,备用链接百度网盘
提取码:3s5r

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值