昨晚上课时,老师说:“用单片机检测按键的双击,看似简单,但实现起来,需要一点小技巧。”

这句话引起了我的兴趣,我打算自己尝试一下。经过折腾,算是解决了这个问题。

Update(2013.8.3): 最近看了 从单片机初学者迈向单片机工程师 这个帖子,里面有更合理的方法,值得借鉴。

最初的思路

当判断按键按下后,延时适当时间,再判断一次按键是否按下,如果按下,说明按键双击,如果没按下,说明是单击。

不过,这样做的话,对两次按键的时间间隔有要求,不能过长或过短。实际测试时确实如此,基本上不能正常、稳定地使用。

程序如下:

//51单片机的键盘双击检测,简易版
//程序功能:单击按键灭灯,双击亮灯
//By Blanboom
//https://blanboom.org
//2013.6.6

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit LED=P0^4;
sbit KEY=P3^4;

void delay(uint time);       //毫秒级延时子程序

int main()
{   
    LED=0;
    while(1)
    {
        if(KEY==0)              //先判断是否按键
        {
            delay(10);
            if(KEY==0)
            {
                while(!KEY);        
                delay(100);
                if(KEY==0)      //如果按键,则判断是否有第二次按键
                {
                    LED=0;      //如果第二次按键(双击),则灯亮
                }
                else
                {
                    LED=1;       //如果第二次没按键,则灯不亮
                    while(!KEY); //按键松下后才能继续执行。
                    delay(50);   //消除抖动
                }       
            }   
        }
    }           
}

void delay(uint time)             
{
    uint i,j;
    for(i=110;i>0;i--)
        for(j=time;j>0;j--);
}

最终方案

后来,上网找了一些资料,通过判断 每次按键按下之后的一段时间内,判断按键是否再次按下 的方法,把问题解决了:

//51单片机的键盘双击检测,定时器版
//程序功能:单击按键灭灯,双击亮灯
//By Blanboom
//https://blanboom.org
//晶振频率:11.0592M
//2013.6.7

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit LED = P0^4;         //LED所连接的IO口
sbit KEY = P3^4;         //按键所连接的IO口
#define KEY_AVALIABLE 0  //值为0时,按键低电平触发;值为1时,按键高电平触发
#define LED_AVALIABLE 0  //值为0时,低电平LED亮;值为1时,高电平LED亮
#define MAX_WAIT_TIME 4  //按键后等待另一次按键的时间,单位:50ms

uint wait_time = 0;      //按键后的等待时间
uchar key_state = 0;     //按键次数

void timer1_initial();   //定时器初始化
void delay(uint time);   //毫秒级延时子程序
uchar key_read();        //按键读取程序

void main()
{
    //LED,按键,定时器的初始化
    LED = LED_AVALIABLE;
    KEY = !KEY_AVALIABLE;
    timer1_initial();  

    while(1)
    {
        uchar key_state_temp;
        key_state_temp = key_read();
        if(key_state_temp == 1)
        {
            LED = !LED_AVALIABLE;       //按键1次,灯灭
        }
        else if(key_state_temp >=2)
        {
            LED = LED_AVALIABLE;        //按键大于1次,灯亮
        }
    }
}

void timer1() interrupt 3       //定时器1:50ms
{
    TH1 = (65536-45872)/256;
    TL1 = (65536-45872)%256;
    wait_time++;
}

void timer1_initial()            //定时器初始化
{
    TH1 = (65536-45872)/256;
    TL1 = (65536-45872)%256;             
    IE = 0x88;                             
    TMOD = 0x10;                     
    TR1 = 1;                         
}

void delay(uint time)
{
    uint i,j;
    for(i=110;i>0;i--)
        for(j=time;j>0;j--);
}

uchar key_read()                         //读取按键,并返回按键次数
{   
    uchar key_state_temp = 0;
    if(KEY == KEY_AVALIABLE)
    {
        delay(10);
        if(KEY == KEY_AVALIABLE);
        {
            while(KEY == KEY_AVALIABLE);
            wait_time = 0;               //按键后重新计时
            key_state++;
        }
    }

    //超过一定时间没按键,函数将返回按键次数
    if(wait_time >= MAX_WAIT_TIME)   
    {
        wait_time = 0;
        key_state_temp = key_state;
        key_state = 0;
    }

    return key_state_temp;
}

实际测试中,这种方法已经能够正常使用了。如果有更好的办法欢迎告诉我。

最后修改日期: 2021-05-14

留言

膜拜一个。我一直都挺喜欢硬件,但就是一直都没能去接触……另外我是王哲强的高中同学。欢迎来看看我的blogwww.freemeepo.com

    看来你是玩 ACM 的大神了。 我上大学之后才开始接触单片机,现在还处于入门阶段。另外如果想玩一下硬件,可以尝试 Arduino。只要懂一点点 C,很快就能入门。

      我比较水…那些硬件都是自己买的吗?我不知道怎么入门……完全不了解……

        学单片机的话,一般用开发板入门。开发板上集成了常用元件(LED、按键、显示屏等),能够直接把程序下载进去。不过如果只是想自己玩一玩的话,还是推荐用 Arduino. 比单片机方便许多。可以先上网了解下,或者看一下《爱上Arduino》这本书或者这个教程:http://goo.gl/dQPWC 确定要学习的话就去淘宝买入门套件。

好的。在空闲时间我会考虑学学这个的。挺有意思的。

1. 类型重定义用typedef吧,define在定义多个变量时会有问题2. 如果我来实现按键,我会将单次按键作为一个低耦合的模块(一次完整的过程:按下,去抖n次,松开),然后通过按键之间的间隔来是否满足双击的条件(太长和太短都不行)。

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据