按键作为最简单的一种人机交互方式,只要在轮询或者是中断中读取按键的电平,加个延时消抖就可以实现简单的按键驱动,但是如果按键资源有限同时要实现多种功能,就需要我们对按键驱动进行优化,参考了网上的一些代码,最近写了一下基于事件驱动的按键驱动(支持单双击长按)。
代码里我都加了注释,很容易理解,同时没有完全由独立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