前言:在我们使用按键时如果遇到多个功能都需要按键来控制,但是数量又太少时,我们就可以使用按键的多功能,让按键的短按,长按,双击都实现不同的功能。
一、原理介绍
我们知道按键其实就是通过单片机引脚进行一个上拉输入来读取按键引脚的电平,没按下就是高电平,按下就读取到低电平,由此来判断按键按下,并执行程序。
那么我们要怎么实现短按,长按,和双击的区分呢?
先说长按:长按与短按的区别就在于按下的时间不同对吧,所以要想实现长按就要用到单片机内部定时器中断了,在定时器中断里写一个时间变量t,随着时间自增。然后让这个自增时间变量t在我们按键按下时(即读取到低电平时)开始从0自增。然后我们就可以对这个t进行比较,比如当它超过某一个值时(物理意义就是当按键按下的时间超过某一个值时)判定它为长按,小于某一个阈值时判定为短按。
接下来说双击怎么实现:想要实现对双击的识别,就要对两次按下同一个按键的时间间隔进行一个判断,这同样需要在定时中断里面写一个随时间自增的时间变量t2,当你第一次松开按键的时候t2开始从零开始增加。然后设定一个阈值,当t2(间隔时间)小于这个阈值的时候判定为双击,否则为单击。
二、代码实现
理论存在,实践开始。
下面用C语言演示,单片机是TC264
头文件:anjian.h
#include "zf_common_headfile.h"
#ifndef CODE_ANJIAN_H_
#define CODE_ANJIAN_H_
typedef struct {
uint32 u32time1; //第一次按下后开始计时,主要用于判断长按,放在定时器中自加
uint32 u32time2; //第一次松手后开始计时,用于判断双击或单击,放在定时器中自加
uint8 u8key_flag; //第一次按下标志
uint8 u8key_double_flag; //第二次标志
}Key;
uint8 KEY_Scan(void);
void Key_control(void);
#define KEY1 P15_7
#define KEY1_status gpio_get_level(KEY1)//读取按键
#define KEY1_PRES 1 //单击
#define LONG_PRES 2 //长按
#define DOUBLE_PRES 3 //双击
#endif /* CODE_ANJIAN_H_ */
anjian.c
#include "zf_common_headfile.h"
#include "anjian.h"
uint32 time1;
uint32 time2;
Key skey;
control_flag=0;
uint8 KEY_Scan(void)
{
static uint8 press = 0;
if(KEY1_status==0)//按键按下
{
system_delay_ms(10);//消抖
if(KEY1_status==0)
{
if(skey.u8key_flag==0)
{
skey.u8key_flag=1; //第一次按下,标志位置1。同时计数值归零
time1=0; //当第一次按下的时候把中断里自增的time1置0,让它从0开始计
}
else if(skey.u8key_flag==1) //按键标志位为1,代表按键是按下的
{
if(!press && time1 > 1000)//如果第一次按下且时间超过1S,视为长按
{
press = 1; //防止一直长按造成多次判定
return LONG_PRES;//返回长按键值
}
}
}
}
else if(KEY1_status==1)//按键松开
{
if(skey.u8key_flag==1) //第一次按键松开
{
skey.u8key_flag=0;
if(time1>1000)//按下后超过1秒才松开,已经返回了键值,松手后把标志位都归零
{
press = 0;
time1 = 0;
time2 = 0;
skey.u8key_flag=0;
skey.u8key_double_flag=0;
}
else if(skey.u8key_double_flag==0)//松开的标志位
{
skey.u8key_double_flag=1; //第一次松开之后标志位置1开始等待第二次按键松开,给1之后第二次松开就不会经过这个else if
time2=0; //在第一次松开之后
}
else if(skey.u8key_double_flag==1) //在第二次松开之后进行间隔时间的判断
{
if(time2<500) //如果第二次松开时间间隔小于0.5S,视为双击
{
skey.u8key_double_flag=0; //判定完成,标志位重新置零
return DOUBLE_PRES; //返回双击对应的值
}
}
}
else if(skey.u8key_double_flag==1)
{
if(time2>=500) //如果间隔时间太久就视为是两次单击
{
skey.u8key_double_flag=0;
return KEY1_PRES;//如果第一次松开之后0.5s没有第二次按键操作,视为短按
}
}
}
return 0;//没有按键按下返回0
}
void Key_control(void)//上面的函数时区分短按,长按和双击,现在这个函数是编写每个按键操作对应控制的代码
{
int i=KEY_Scan(); //设变量i接收返回值
if(i==1)
{
control_flag=1; //写一个标志位,如果短按标志位就置1,把标志在头文件里面声明为全局变量,根据标志位进行对其他函数的实现
}
else if(i==2)
{
control_flag=0; //写标志位是为了更好地去和其他外设函数联动,使代码更有逻辑性
}
else if(i==3) //这边也可以写具体的操作代码,但是最好还是用标志位
{
int j=0;
if(j==0)
{
tft180_set_color(RGB565_BLUE, RGB565_WHITE);//蓝色字体
tft180_show_int (1,1, encoder_data_1, 5);//左边编码器速度
tft180_show_int (1,49, encoder_data_2, 5);//右边编码器速度
tft180_set_color(RGB565_GREEN, RGB565_WHITE);//绿色字体
// tft180_show_int (1,33, mypid_Value_Left.output, 5);//左电机占空比
// tft180_show_int (1,81, mypid_Value_Right.output, 5);//右电机占空比
j=1;
}
if(j==1)
{
tft180_set_color(RGB565_BLUE, RGB565_WHITE);//黑色字体
tft180_show_int (1,1, encoder_data_1, 5);//左边编码器速度
tft180_show_int (1,49, encoder_data_2, 5);//右边编码器速度
j=0;
}
}
}
中断
pit_ms_init(CCU61_CH0, 100);//每0.1秒自增100
IFX_INTERRUPT(cc61_pit_ch0_isr, 0, CCU6_1_CH0_ISR_PRIORITY)
{
interrupt_global_enable(0); // 开启中断嵌套
pit_clear_flag(CCU61_CH0);
time1 += 100;
time2 += 100;
}
最后把函数 Key_control()放在主函数循环里面就好了。