该驱动用于检测按键,方便拓展,不限平台,只要是用c语言的均可运行。可实现的功能有:单击(按下立即执行和按下松开再执行),双击,长按(支持连续触发长按,也可触发一次)。单击和双击的时间间隔可配置,长按触发时间可配置,长按连续触发间隔时间可配置。话不多说,看代码。
//hal_btn.c
#include "hal_btn.h"
/**
* @brief 按键扫描
* @param *btn:按键扫描的参数配置结构体,以及按键事件输出参数
* @retval none
*/
void btn_scan(btn_module_t *btn)
{
btn->btn_event = btn_null;
switch (btn->index)
{
case 0:
if (btn->pin_state == btn->pree_state)
{
btn->index = 1;
}
btn->Press_count = 0;
btn->Press_num_count = 0;
break;
case 1:
if (btn->pin_state == btn->pree_state)
{
btn->index = 2;
btn->btn_event = btn_pree;
}
else
{
btn->index = 0;
}
break;
case 2:
if (btn->pin_state == btn->pree_state)
{
if (++btn->Press_count > LONG_PREE_TIME)
{
btn->Press_count = 0;
btn->index = 3;
btn->btn_event = btn_long_pree;
btn->Press_num_count = 0;
}
}
else
{
//按键松开
if (btn->Press_count && btn->Press_count < LONG_PREE_TIME)
{
btn->Press_count = 0;
btn->index = 4;
btn->Press_num_count++;
}
else
{
btn->index = 0;
}
}
break;
case 3:
if (btn->pin_state == btn->pree_state)
{
#if BTN_LONGPREE_CONTINUITY
if (++btn->Press_count >= LONG_PREE_CONTINUITY_TIME)
{
btn->Press_count = 0;
//连续触发长按间隔
btn->btn_event = btn_long_pree;
}
#endif
}
else
{
#if BTN_LONGPREE_CONTINUITY
btn->Press_count = 0;
#endif
btn->index = 0;
}
break;
case 4:
if (btn->pin_state == btn->pree_state)
{
btn->index = 1;
btn->Press_count = 0;
}
else
{
//按键松开
if (++btn->Press_count >= INTERVAL_TIMEOUT_TIME)
{
btn->Press_count = 0;
//多击间隔超时
btn->index = 5;
}
}
break;
case 5:
if (btn->Press_num_count == 1)
{
btn->btn_event = btn_short_pree;
}
else if (btn->Press_num_count == 2)
{
btn->btn_event = btn_double_pree;
}
btn->Press_num_count = 0;
btn->index = 0;
break;
default:
btn->index = 0;
btn->btn_event = btn_null;
break;
}
}
/******************使用方法如下*******************/
//例1:该方式将直接处理按键事件,整体较为直观,但会使得按键驱动变得臃肿
/*10ms循环调用该函数 btn1_handle(); ,函数名可自定义,调用的时间决定了配置的时间参数,因使用 unsigned char 类型,所以10ms调用可以满足大部分需求。当然,可以根据需求更改成员变量的类型*/
btn_module_t btn1 = {0};
void btn1_handle(void)
{
btn1.pree_state = 0;//按键按下的电平
btn1.pin_state = PIN_BTN1;//按键对应的IO口
btn_scan(&btn1);
if (btn1.btn_event == btn_pree)
{
//按下立即执行
}
if (btn1.btn_event == btn_short_pree)
{
//短按松开执行
}
if (btn1.btn_event == btn_double_pree)
{
//双击
}
if (btn1.btn_event == btn_long_pree)
{
//长按
}
}
//例2:事件函数调用,该方式的按键响应可在其他文件定义,使得按键驱动变得简洁
/*10ms循环调用该函数 btn2_handle(); ,函数名可自定义调用的时间决定了配置的时间参数,因使用 unsigned char 类型,所以10ms调用可以满足大部分需求。当然,可以根据需求更改成员变量的类型*/
btn_module_t btn2 = {0};
void btn2_handle(void)
{
btn2.pree_state = 0;//按键按下的电平
btn2.pin_state = PIN_BTN2;//按键对应的IO口
btn_scan(&btn2);
if (btn2.btn_event == btn_pree)
{
//按下立即执行
btn2_pree_event();
}
if (btn2.btn_event == btn_short_pree)
{
//短按松开执行
btn2_short_event();
}
if (btn2.btn_event == btn_double_pree)
{
//双击
btn2_double_event();
}
if (btn2.btn_event == btn_long_pree)
{
//长按
btn2_long_event();
}
}
头文件的声明,可配置的参数在此处声明,以及IO的相关宏定义
//hal_btn.h
#ifndef _HAL_BTN_H
#define _HAL_BTN_H
#include "main.h"
typedef enum
{
btn_null = 0x00,
btn_pree = 0x02,
btn_short_pree = 0x03,
btn_double_pree = 0x04,
btn_long_pree = 0x05,
} btn_event_t;
typedef struct
{
btn_event_t btn_event;
unsigned char index;
unsigned char Press_count;
unsigned char Press_num_count;
unsigned char pin_state : 1;
unsigned char pree_state : 1;
} btn_module_t;
#define LONG_PREE_TIME 90 //触发长按时间,触发长按事件的时间,注:该参数是触发长按事件的时间,跟长按连续触发间隔有区别
#define INTERVAL_TIMEOUT_TIME 5 //多击间隔超时时间,该值影响单击松开执行和双击的判定
#define BTN_LONGPREE_CONTINUITY 0 //连续触发长按使能
#if BTN_LONGPREE_CONTINUITY
#define LONG_PREE_CONTINUITY_TIME 60 //连续触发长按间隔,影响连续触发的间隔
#endif
#define PIN_BTN1 P20
void btn1_handle(void);
#define PIN_BTN2 P21
void btn2_handle(void);
#endif
按键检测的大致流程图,流程图有些细节可能没有体现,或者是流程图有问题,仅作参考。
如有问题可留言,如有问题,麻烦告知,多谢您的支持!