stm32 设计一个按键驱动,可以检测单机,连击,长按等操作
1.期望:
设计一个通用驱动代码:
- 不需要外部中断
- 不限制于stm32,可以快速移植到各种单片机(只需要修改一句话)
- 可以轻松识别单机,连击,长按
- 不需要堵塞延时,写在主函数也不会堵塞其它代码执行
- 代码尽量多次分层,减少计算量,写在中断也不会占用过长时间
- 使用宏定义,可以快速更改时间间隔
- 软件完美消抖,减少硬件电路设计
2.实现效果:
stm32按键驱动
https://www.bilibili.com/video/BV1G34y1t7CA/
3.先来看驱动代码
使用宏定义,可以快速更改成想要的时间和方便移植
gkey.h
#ifndef GKEY_H__
#define GKEY_H__
#include "main.h"
#define ERROR_TOUCH_T 50 //误触时间(消抖)
#define SINGLE_TOUCH_T 800 //单机最长事件,大于此数值会认为长按
#define CONTINUE_INTERVAL_T 500//连击间隔时间
#define GetSysTimeMs() HAL_GetTick() //获取当前系统时间(ms)
#define retSta_SinglePush 0x01 //单次按下,sta变成的值
//...... 后面的连击会在retSta_SinglePush基础下累加,例如五连击会是retSta_SinglePush+4
#define retSta_LongPush 0xFF //长按,sta变成的值
//按键
class KEY
{
public:
KEY(bool keypush,uint8_t (*Callback)())
{
keyPush=keypush;
keyReadFun=Callback;
}
void keyScan(void);
public:
uint8_t sta;//实际输出状态
private:
bool keyPush;//按键按下后会是啥样
uint8_t (*keyReadFun)();//函数变量keyReadFun
};
#endif //GKEY_H__
gkey.cpp
#include "gkey.h"
void KEY::keyScan(void)
{
//定义相关变量
static uint8_t trigFlag;
static uint8_t waitRelease;
static long key_trig_t;
static long lastSingle_trig_t;
uint8_t keySta=keyReadFun();//读取当前电平
if(waitRelease==1)//等待释放按键,长按时使用
{
if(keySta==keyPush) {sta=0;return;}
waitRelease=0;
}
if(keySta==keyPush)//按键处于按下状态
{
if(trigFlag==0)
{
trigFlag=1;//触发按键按下
key_trig_t=GetSysTimeMs();//获取第一次按下时间
}
else if(trigFlag==1)//按键已经按下,再次触发中断了
{
long intervalT=GetSysTimeMs()-key_trig_t;
if(intervalT<=ERROR_TOUCH_T) return;//消抖
else if(intervalT>=SINGLE_TOUCH_T)//长按
{
trigFlag=0;//清空标志位
waitRelease=1;//等待释放按键
sta=retSta_LongPush;//长按
}
}
}else
{
if(trigFlag==0) {sta=0;return;}//按键没有按下,本身也没有触发事件,直接返回
else //按键虽然没按下,但事件处于被触发状态
{
long intervalT=GetSysTimeMs()-key_trig_t;
if(intervalT<=ERROR_TOUCH_T) {sta=0;return;}//消抖
else if(intervalT<=SINGLE_TOUCH_T)
{
static uint8_t trigCnt;//触发次数记录,用来记录多次连击
if(GetSysTimeMs()-lastSingle_trig_t<=CONTINUE_INTERVAL_T)//两次单机之间小于一定时间,认定为双击
{
trigCnt++;
sta=retSta_SinglePush+trigCnt;//双击
}else//仍为单机事件
{
trigCnt=0;
sta=retSta_SinglePush;//单机
}
trigFlag=0;//清除标志位
lastSingle_trig_t=GetSysTimeMs();//记录上次单次按下事件
}
}
}
}
4.使用示例
定义在全局
uint8_t keyRead(void)
{
//用户需要修改的地方,方便移植,只需要更改return后面的函数,读取电平函数
return HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15);
}
//初始化按键,第一个参数是按下后按键的电平,第二个是上面的函数名
KEY mykey(0,keyRead);
使用在1000hz处
mykey.keyScan();
if(mykey.sta!=0)
gDebug()<<mykey.sta<<endl;//这里是打印输出,效果就是printf,你们改成自己的