基于事件驱动的按键驱动(支持单双击长按)

按键作为最简单的一种人机交互方式,只要在轮询或者是中断中读取按键的电平,加个延时消抖就可以实现简单的按键驱动,但是如果按键资源有限同时要实现多种功能,就需要我们对按键驱动进行优化,参考了网上的一些代码,最近写了一下基于事件驱动的按键驱动(支持单双击长按)。

代码里我都加了注释,很容易理解,同时没有完全由独立c编写,不基于任何一个平台,可以很容易的移植到各个平台。这里我以stm32为例,阐释调用过程。

example

button_t button1;   //定义按键1
uint8_t  GET_BUTTON1_LEVEL(void);   //获取按键1电平函数(自己实现)

/*******自己定义***********/
void button1_pressdown(void * par);  //按键1按下回调函数
void button1_longpress(void * par);  //按键1长按回调函数
void button1_doublepress(void * par);  //按键1长双击回调函数

void main()
{
	button_init(&button1,0,GET_BUTTON1_LEVEL);   //初始化按键1
	button_start(&button1);   //开启按键任务
	register_callback(&button1,singleclick,button1_pressdown);   //注册单击回调函数
	register_callback(&button1,longclick,button1_longpress);    //注册长按回调函数
	register_callback(&button1,doubleclick,button1_doublepress);   //注册双击回调函数

}


uint8_t  GET_BUTTON1_LEVEL()   //获取按键1电平函数
{
	return HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3);
}

//安键1按下回调函数
void button1_pressdown(void * par)
{
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);
}
//安键1长按回调函数
void button1_longpress(void * par)
{
	//HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_7);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
}
//安键1双击回调函数
void button1_doublepress(void * par)
{
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_7);
	//HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
}




/***************定时器周期回调函数**************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	
  /* Prevent unused argument(s) compilation warning */
  if(htim==&htim1)
  {
     button_process();   //只需要在定时器中断里加入这个函数就可以了
		
  }


}

按键驱动c文件

#include "button.h"

//按键链表的头
button_t *head_handle=NULL;

/**
  * @brief  按键初始化函数.
  * @param  button: 按键句柄.
  * @param  trigerlevel: 触发电平.
  * @param  get_button_Level: 获取当前电平函数指针.
  * @retval None
  */
void button_init(button_t* button,uint8_t trigerlevel,uint8_t  (*get_button_Level)())
{
	memset(button,0,sizeof(button_t));
	button->event=nopress;
	button->trigerlevel=trigerlevel;
	button->get_button_Level=get_button_Level;
	button->nowlevel=button->get_button_Level();	
}

/**
  * @brief  注册按键回调函数.
  * @param  button: 按键句柄.
  * @param  click_type_t: 点击类型.
  * @param  cb: 回调函数.
  * @retval None
  */
void register_callback(button_t* button,click_type_t type,callback cb){
	
	button->cb[type]=cb;
}

/**
  * @brief  获取当前按键的事件类型.
  * @param  button: 按键句柄.
  * @retval 事件类型
  */
click_type_t  get_button_event(button_t* button)
{
	return button->event;	
}

/**
  * @brief  按键处理函数.
  * @param  button: 按键句柄.
  * @retval none
  */
void button_handle(button_t* button)
{
	if(button->get_button_Level()!=button->nowlevel)
	{
		if(++button->debuncetick>=DEBUNCETICK)    //大于消抖次数
		{
			button->nowlevel=button->get_button_Level();   //更新当前电平
			button->debuncetick=0;
			
		}	
	}else{
		
		button->debuncetick=0;
	}
	
	
	switch(button->state){
		
	case 0:     //初始状态
		if(button->nowlevel==button->trigerlevel)    //按下
		{
			button->event=pressdown;
			button->state=1;
			if(button->cb[pressdown])   //如果注册了按下回调函数
			{
				button->cb[pressdown]((button_t*) button);
				
			}
			
		}
		break;
		
	
	case 1:    //按下之后的状态
		if(button->nowlevel!=button->trigerlevel)  //弹起
		{
			button->event=pressup;
			button->state=2;
			button->longtick=0;
			if(button->cb[pressup])   //如果注册了弹起回调函数
			{
				button->cb[pressup]((button_t*) button);
				
			}
		}else{
			
			if(++button->longtick>=LONGCLICK)     //大于长按计数
			{
				button->event=longclick;
				button->state=3;
				button->longtick=0;
				if(button->cb[longclick])   //如果注册了长按回调函数
				{
					button->cb[longclick]((button_t*) button);
				
				}
				
			}
		}
		break;
		
		
	case 2:    //按下之后又弹起状态
		if(button->nowlevel==button->trigerlevel)   //第二次按下
		{
			 
			button->doubletick=0;
			if(button->cb[pressdown])   //如果注册了按下回调函数
			{
				button->cb[pressdown]((button_t*) button);
				
			}
			
			if(button->cb[doubleclick])   //如果注册了双击回调函数
			{
				button->event=doubleclick;
				button->state=3;
				button->cb[doubleclick]((button_t*) button);	
			}else{
				button->state=1; 
				button->event=pressdown;
				if(button->cb[singleclick])   //如果注册了单击回调函数
				{
					button->cb[singleclick]((button_t*) button);
				
				}
			}
			
		}else{
			
			if(++button->doubletick>=DOUBLETICK)
			{
				button->doubletick=0;
				button->state=0;
				if(button->cb[singleclick])   //如果注册了单击回调函数
				{
					button->cb[singleclick]((button_t*) button);
				
				}
			}
			
		}
		break;
		 
	case 3:    //等待弹起状态
		if(button->nowlevel!=button->trigerlevel)  //弹起
		{
			
			if(button->cb[pressup])   //如果注册了弹起回调函数
			{
				button->cb[pressup]((button_t*)  button);
				
			}
			
			button->state=0;
		}
		
		break;
	}
}


/**
  * @brief  把按键加入链表函数.
  * @param  button: 按键句柄
  * @retval 0:成功. -1: 已经存在.
  */
int button_start(button_t* button)
{
	button_t* target = head_handle;
	while(target) {
		if(target == button) return -1;	//already exist.
		target = target->next;
	}
	button->next = head_handle;
	head_handle = button;
	return 0;
}


/**
  * @brief  把按键从链表删除.
  * @param  button: 按键句柄
  * @retval None
  */
void button_stop(button_t* button)
{
	button_t** curr;
	for(curr = &head_handle; *curr; ) {
		button_t* entry = *curr;
		if (entry == button) {
			*curr = entry->next;
//			free(entry);
		} else
			curr = &entry->next;
	}
}


/**
  * @brief  按键轮询函数.
  * @param  None.
  * @retval None
  */
void button_process()
{
	button_t* target;
	for(target=head_handle; target; target=target->next) {
		button_handle(target);
	}
}

按键驱动头文件

#ifndef _BUTTON_H_
#define _BUTTON_H_

#include "stdint.h"
#include "string.h"



#define DEBUNCETICK  1       //消抖计时次数
#define LONGCLICK    50      //长按计时次数
#define DOUBLETICK   30      //双击计时次数


typedef void (*callback)(void*); 


typedef enum  _click_type_t{
	
	pressdown=0,     //按下
	pressup,       //弹起
	singleclick,    //单击
	longclick,     //长按
	doubleclick,    //双击
	num_of_type,    //类型数目	
	nopress         //没有按键按下
}click_type_t;    //按键类型

typedef struct _button_t button_t;
struct _button_t{
	uint8_t debuncetick;    //消抖计数
	uint8_t doubletick;    //双击计数
	uint8_t longtick;      //长按计数
	uint8_t state;         //状态
	uint8_t event;         //按键事件
	uint8_t trigerlevel;   //触发电平
	uint8_t nowlevel;     //当前电平
    uint8_t  (*get_button_Level)(void);    //获取当前电平函数指针
	callback cb[num_of_type];               //回调函数数组指针
	button_t*next;                        //下一个按键
};    //按键

#ifdef __cplusplus  
extern "C" {  
#endif  

void button_init(button_t* button,uint8_t trigerlevel,uint8_t  (*get_button_Level)());
void register_callback(button_t* button,click_type_t type,callback cb);
click_type_t  get_button_event(button_t* button);
int button_start(button_t* button);
void button_stop(button_t* button);
void button_process(void);

#ifdef __cplusplus
} 
#endif

#endif


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值