找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1950|回复: 22
收起左侧

单片机按键状态检测程序问题,不能双击

[复制链接]
ID:566214 发表于 2021-9-23 22:17 | 显示全部楼层 |阅读模式
/*      双击不行,10ms扫描一次,Key_Double_Period=30,不是Key_Double_Period时间的问题
         现在的问题是:第一次按下的时间大于Key_Double_Period,再快速按一下,就可以识别为双击
         但是这样子太别扭了,双击应该是再Key_Double_Period时间内,再次按下,就认为是双击
         调了快一个礼拜,还是同样的问题

*/
uchar Key_Read(void)
{
        static uchar key_state_buffer=key_state_0;
        static uchar key_timer_cnt=0;
        static uchar key_temp=Key_Null;
        uchar key_return=Key_Null;
        uchar key=Key_Null;
        key=Key_Null|Key_Driver();
        switch(key_state_buffer)
        {
                case key_state_0:
                        if(key!=Key_Null)
                        {
                                key_temp=key;
                                key_timer_cnt=0;
                                key_state_buffer=key_state_1;
                        }
                        else
                        {
                                key_return=key;
                        }
                        break;
                case key_state_1:
                        if(key!=Key_Null)
                        {
                                key_return=key|Key_Double;
                                key_state_buffer=key_state_0;
                        }
                        else if(++key_timer_cnt>=Key_Double_Period)
                        {
                                key_return=key_temp;
                                key_state_buffer=key_state_0;
                                key_timer_cnt=0;
                        }
                        break;
        }
        return key_return;
}

回复

使用道具 举报

ID:566214 发表于 2021-9-23 22:21 | 显示全部楼层
Key_Null=0x00;Key_Driver()这个函数返回值是单击的值和长按得值
回复

使用道具 举报

ID:190577 发表于 2021-9-24 06:48 | 显示全部楼层
#include<stc15w202s.h>
#include<stdio.h>
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
#define key_no 0
#define key_click 1
#define key_double 2
#define key_long 3
sbit KEY = P3^3;
sbit LED_E1P = P3^1;
sbit LED_G1 = P3^2;
sbit LED2 = P5^5;        //W2OUT
sbit LED3 = P5^4;  
unsigned char flag;
unsigned char cnt = 0;


static unsigned char key_driver(void)
{
        static unsigned char key_state_buffer1 = key_state_0;
        static unsigned char key_timer_cnt1 = 0;
        unsigned char key_return = key_no;
        unsigned char key;
        
        key = KEY;  //read the I/O states
        
        switch(key_state_buffer1)
        {
                case key_state_0:
                        if(key == 0)
                                key_state_buffer1 = key_state_1;
                                //按键被按下,状态转换到按键消抖和确认状态//
                        break;
                case key_state_1:
                        if(key == 0)
                        {
                                key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                //按键仍然处于按下状态
                                //消抖完成,key_timer开始准备计时
                                //状态切换到按下时间计时状态
                        }
                        else
                                key_state_buffer1 = key_state_0;
                                //按键已经抬起,回到按键初始状态
                        break;  //完成软件消抖
                case key_state_2:
                        if(key == 1)
                        {
                                key_return = key_click;  //按键抬起,产生一次click操作
                                key_state_buffer1 = key_state_0;  //转换到按键初始状态
                        }
                        else if(++key_timer_cnt1 >= 100)  //按键继续按下,计时超过1000ms
                        {
                                key_return = key_long;  //送回长按事件
                                key_state_buffer1 = key_state_3;  //转换到等待按键释放状态
                        }
                        break;
                case key_state_3:  //等待按键释放
                        if(key == 1)  //按键释放
                                key_state_buffer1 = key_state_0;  //切回按键初始状态
                        break;
        }
        return key_return;
}

unsigned char key_read(void)
{
        static unsigned char key_state_buffer2 = key_state_0;
        static unsigned char key_timer_cnt2 = 0;
        unsigned char key_return = key_no;
        unsigned char key;
        
        key = key_driver();
        
        switch(key_state_buffer2)
        {
                case key_state_0:
                        if(key == key_click)
                        {
                                key_timer_cnt2 = 0;  //第一次单击,不返回,到下个状态判断是否会出现双击
                                key_state_buffer2 = key_state_1;
                        }
                        else
                                key_return = key;  //对于无键、长按,返回原事件
                        break;
                case key_state_1:
                        if(key == key_click)  //又一次单击,时间间隔小于500ms
                        {
                                key_return = key_double;  //返回双击事件,回到初始状态
                                key_state_buffer2 = key_state_0;
                        }
                        else if(++key_timer_cnt2 >= 50)
                        {   
                        //这里在下一次的按键来临之前,并且时间是小于500ms的时候,就会一直执行的是这个key_timer_cnt2++.直到下一次的按键到来,再判断看是双击还是单击。
                                //这里500ms内肯定读到的都是无键事件,因为长按大于1000ms
                                //在1s前底层返回的都是无键
                                                                        
                                key_return = key_click;  //500ms内没有再次出现单击事件,返回单击事件
                                key_state_buffer2 = key_state_0;  //返回初始状态
                                       
                        }
                        break;
        }
        
        return key_return;
}

void Timer0Init(void)                //1毫秒@11.0592MHz
{
        AUXR |= 0x80;                //定时器时钟1T模式
        TMOD &= 0xF0;                //设置定时器模式
        TL0 = 0xCD;                //设置定时初值
        TH0 = 0xD4;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        ET0 = 1;

}
void IO_init()
{
   
    P3M0 = 0x01;
    P3M1 = 0x01;
    P5M0 = 0x00;
    P5M1 = 0x00;
   
}


void main(void)
{         
       unsigned char key = 1;
             Timer0Init();
           IO_init();
           LED_E1P = 1;
           LED_G1 = 1;
           LED2 = 1;
           LED3 = 0;
                   EA = 1;
           while(1)
           {
             if(flag)
                 {
                   flag=0;
           key = key_read();
                   switch(key)
                   {
                            case key_click:LED2 = !LED2; break;
                         case key_double:LED2 = !LED2;LED3 = !LED3; break;
                         case key_long: LED2 = 1; LED3 = 1; LED_G1 = 0; LED_E1P = 0; break;
                         default : break;
                   }
                 }
           }
}

void Timr0_ISR() interrupt 1
{
   cnt++;
   if(cnt>=10)
   {
       cnt = 0;
              flag = 1;
   }
}
回复

使用道具 举报

ID:123289 发表于 2021-9-24 08:52 | 显示全部楼层
1、按键有弹动,如果没有防弹动过虑则,则无法界定按了几下(按一下,弹N次)。
2、防弹动的过虑时间,既要能虑除弹动时间,又不能错将双击过虑掉。这就要求过虑时间设置适当。
建议过虑时间设置为32ms。
3、为了能识别按了几次,就需要有一个计数器JN。
4、JN何时起计,何时结束,也是有讲究的。JN计数完成之前,你不要做界定,因为还不确定是连续按了几次。
建议的做法是:双击的间隔时间定义为:0.08-0.6秒之间。
界定开始时间(JN计数结束时间):这个0.6秒就是JN界定的时间,它的意思是:当按键弹起后0.6秒后,开始界定JN。这时JN=几,就是连续击键几次。
如此:无论连续击几次都能识别。
短按:JN=1
双击:JN=2。
N击:JN=N。
JN计时的开始时间(复0):JN被识别后,就清0,准备下次使用。当然,初始化也清0。
由于涉及多种计时,所以,建议做个定时中断,如4ms中断一次,中断服务程序中做以下事件:
1、扫键:
弹动过虑计时器JT:
键按下时:+1,本次键值THIS_KEY与上次键值LAST_KEY不同时清0(不同,就是弹动),相同时+1。>8时(弹动过虑时间)不再增加。
弹起时界定:JT>=8时(32ms),键值有效(可以做个标记:KEY_P=1),即认为按了一次。
2、击键次数界定计时器JC:
键按下时:+1,>150(0.6秒)时不再增加。
弹起时界定:JC>=150时(600ms),去界定JN。随即将JC清0。
3、击键次数JN:
键值被认定有效(KEY_P=1)时+1,>多少时不再增加,自己去定。将KEY_P清0。





回复

使用道具 举报

ID:332444 发表于 2021-9-24 14:36 | 显示全部楼层
现在认为不用双击,用组合键更简单灵活,程序设计也相对容易实现.
回复

使用道具 举报

ID:566214 发表于 2021-9-24 14:51 | 显示全部楼层
bbxyliyang 发表于 2021-9-24 06:48
#include
#include
#define key_state_0 0

感谢回复,这个程序是触摸按键的,机械按键是可以正常识别双击事件的,触摸按键的库是别提供的,只有一个接口函数
回复

使用道具 举报

ID:566214 发表于 2021-9-24 14:52 | 显示全部楼层
xianfajushi 发表于 2021-9-24 14:36
现在认为不用双击,用组合键更简单灵活,程序设计也相对容易实现.

触摸按键
回复

使用道具 举报

ID:566214 发表于 2021-9-24 14:52 | 显示全部楼层
yzwzfyz 发表于 2021-9-24 08:52
1、按键有弹动,如果没有防弹动过虑则,则无法界定按了几下(按一下,弹N次)。
2、防弹动的过虑时间,既 ...

感谢回复,我试一下
回复

使用道具 举报

ID:332444 发表于 2021-9-24 15:36 | 显示全部楼层

只要是按键,不论是何种,都是可以实现组合键,有什么不能实现的理由?
回复

使用道具 举报

ID:332444 发表于 2021-9-24 15:44 | 显示全部楼层

设定一个比较时间,可以实现组合,当然,双击也是.
回复

使用道具 举报

ID:624769 发表于 2021-9-24 15:47 | 显示全部楼层
一般情况下, 长按短按, 单击双击。
这两种区分按键的模式,在没有必要的前提下,不同时使用。
因为,会给你的按键判断复杂程度提高一个级别,如果你采取了 单击双击,还嫌不够可以增加三击四击,没必要再去搞长按。一个键也就算了,键多了,你的按键判断逻辑会变得及其混乱,因为一旦牵涉到长按和双击,那么你必须考虑,第二击你达到了长按标准的话,到底算,长按,还是双击。诸如此类各种麻烦。
回复

使用道具 举报

ID:566214 发表于 2021-9-24 19:16 | 显示全部楼层
xianfajushi 发表于 2021-9-24 15:44
设定一个比较时间,可以实现组合,当然,双击也是.

状态机的逻辑可以实现吗?我同时检测5颗按键
回复

使用道具 举报

ID:566214 发表于 2021-9-24 19:19 | 显示全部楼层
188610329 发表于 2021-9-24 15:47
一般情况下, 长按短按, 单击双击。
这两种区分按键的模式,在没有必要的前提下,不同时使用。
因为, ...

逻辑不会很乱,因为一次只能检测一个按键
回复

使用道具 举报

ID:566214 发表于 2021-9-24 19:24 | 显示全部楼层
yzwzfyz 发表于 2021-9-24 08:52
1、按键有弹动,如果没有防弹动过虑则,则无法界定按了几下(按一下,弹N次)。
2、防弹动的过虑时间,既 ...

每次单击都可以检测到,就是双击不行
回复

使用道具 举报

ID:332444 发表于 2021-9-24 19:25 | 显示全部楼层
dcjdcj 发表于 2021-9-24 19:16
状态机的逻辑可以实现吗?我同时检测5颗按键

可以,在一定时间范围内对按键进行计算得值即可实现组合,计数可实现双击,也可实现按次数,这样写起来很方便。
回复

使用道具 举报

ID:566214 发表于 2021-9-24 20:14 | 显示全部楼层
xianfajushi 发表于 2021-9-24 19:25
可以,在一定时间范围内对按键进行计算得值即可实现组合,计数可实现双击,也可实现按次数,这样写起来很 ...

比如,在一段时间内,第一次按下的键值是0x80,第二次按下的键值是0x01,然后将这两个值相或,得到0x81,用0x81区判断是不是组合键吗?
回复

使用道具 举报

ID:332444 发表于 2021-9-24 20:45 | 显示全部楼层
dcjdcj 发表于 2021-9-24 20:14
比如,在一段时间内,第一次按下的键值是0x80,第二次按下的键值是0x01,然后将这两个值相或,得到0x81, ...

回复

使用道具 举报

ID:566214 发表于 2021-9-24 21:13 | 显示全部楼层

还有一个问题,一个无源蜂鸣器,比如4k频率,定时器中断125us,要怎么驱动,可否来个例程,谢谢了
回复

使用道具 举报

ID:624769 发表于 2021-9-24 21:28 | 显示全部楼层
dcjdcj 发表于 2021-9-24 21:13
还有一个问题,一个无源蜂鸣器,比如4k频率,定时器中断125us,要怎么驱动,可否来个例程,谢谢了

中断里面一句话

Beep_IO = !Beep_IO;
回复

使用道具 举报

ID:566214 发表于 2021-9-24 21:52 | 显示全部楼层
188610329 发表于 2021-9-24 21:28
中断里面一句话

Beep_IO = !Beep_IO;

Beep_IO = ~Beep_IO;不是这个吗?那么响一声要怎么写,要多久才算一声
回复

使用道具 举报

ID:624769 发表于 2021-9-24 22:12 | 显示全部楼层
dcjdcj 发表于 2021-9-24 21:52
Beep_IO = ~Beep_IO;不是这个吗?那么响一声要怎么写,要多久才算一声

一般,习惯上,  字节 用  ~ 取反, 位 用 !取反。 至少 别人看到我写的那一行一眼就能知道 Beep_IO 是个位地址。

最后,响一声,看你打算 长响还是短响, 长响一般 1.5 秒, 短响一般 0.3 秒,(习惯上)。
回复

使用道具 举报

ID:566214 发表于 2021-9-24 23:16 | 显示全部楼层
188610329 发表于 2021-9-24 22:12
一般,习惯上,  字节 用  ~ 取反, 位 用 !取反。 至少 别人看到我写的那一行一眼就能知道 Beep_IO 是 ...

好的,谢谢指教,我试一下
回复

使用道具 举报

ID:566214 发表于 2021-9-25 19:50 | 显示全部楼层
bbxyliyang 发表于 2021-9-24 06:48
#include
#include
#define key_state_0 0

触摸按键的,不行,波动按键就可以
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表