jacky82512 发表于 2011-8-17 12:46:53

马老师能否编一个 一个按键支持单击,双击的程序?

马老师能否编一个 一个按键支持单击,双击的,N击的程序
论坛上面貌似没有这样的。

bill 发表于 2011-8-17 12:51:53

你是什么鸟?

110112110 发表于 2011-8-17 12:55:15

你是什么鸟?

xiaobendan 发表于 2011-8-17 12:56:25

在检测到一次单击后,设定一个定时器,再次单击时检测这个定时器,如果时间没有到,就算双击,否则算单击。N击有意义吗?

whhityang 发表于 2011-8-17 13:17:26

估计实际做过单片机程序的都会写,不至于找专人。。

jacky82512 发表于 2011-8-17 14:28:44

回复【2楼】110112110
---------------------------------------------------------------------
多少钱啊
你没有见过钱啊

jacky82512 发表于 2011-8-17 14:29:16

回复【1楼】bill
-----------------------------------------------------------------------

什么话吗

jacky82512 发表于 2011-8-17 14:30:11

回复【4楼】whhityang小样一
-----------------------------------------------------------------------

老兄能帮下小弟吗

jacky82512 发表于 2011-8-17 14:31:10

回复【3楼】xiaobendan仲跻东
-----------------------------------------------------------------------

你好。能说详细点吗,

avr-qq 发表于 2011-8-17 14:49:20

回复【楼主位】jacky82512
马老师能否编一个 一个按键支持单击,双击的,n击的程序
论坛上面貌似没有这样的。
-----------------------------------------------------------------------

这个值得让马老师给你写吗 ?

自己动动脑子吧

jacky82512 发表于 2011-8-17 14:55:09

回复【9楼】avr-qq高级工程叁
-----------------------------------------------------------------------

写不出来,否则也不必请教。弱势群体啊

USACH 发表于 2011-8-17 14:57:25

前排围观!!!此帖必火。

cock 发表于 2011-8-17 15:03:40

这里虽号称电子桃花源,但仍是武林风俗,哪有徒子徒孙求师祖代扫院子的啊。

jacky82512 发表于 2011-8-17 15:05:27

//4。***342页,按键扫描函数代码
//原代码不理想,请改成如下:
#define key_input   PIND       // 按键输入口
#define key_mask    0b11000000 // 按键输入屏蔽码
#define key_no      0
#define key_k1      1
#define key_k2      2

#define key_state_0 0
#define key_state_1 1
#define key_state_2 2

unsigned char read_key(void)
{
    static unsigned char key_state = 0,key_old,key_time,key_cnt,key_time_start=0;
    unsigned char key_press,key_return = key_no;

    key_press = key_input & key_mask;         // 读按键I/O电平
   
    if(key_time_start)                        //双击判断计时开始
      {
             key_time++;//
      }
   
    switch (key_state)
    {
      case key_state_0:                     // 按键初始态
            if (key_press != key_mask)
            {
               key_old = key_press;         // 记录原电平                  
               key_state = key_state_1;       // 键被按下,状态转换到键确认态   
            }               
            break;   
      case key_state_1:                     // 按键确认态
            if (key_press == key_old)         // 与原电平比较(消抖处理)
            {
                if      (key_press == 0b01000000)
                        {
                                key_time_start=0xff;      //计时标志置位
                                if(key_time>50)             //>500ms做什么
                                        {
                                                //key_cnt=0;
                                                key_time=0;
                                                key_time_start=0x00;    //计时停止
                                                key_return = key_k1;
                                        }
                                else//<=500ms做什么
                                        {
                                                key_cnt++;
                                                if(key_cnt>=8)//
                                                        {
                                                                key_cnt=0;
                                                                key_time=0;
                                                                key_time_start=0x00;//计时停止
                                                                key_return = key_double_k1;
                                                        }
                                        }
                               
                                //key_return = key_k1;
                        }
                       
                else if (key_press == 0b10000000) key_return = key_k2;
                          
                key_state = key_state_2;      // 状态转换到键释放态
            }
            else
                key_state = key_state_0;      // 按键已抬起,转换到按键初始态
            break;
      case key_state_2:
            if (key_press == key_mask) key_state = key_state_0;//按键已释放,转换到按键初始态
            break;
    }
    return key_return;
}

//在此附上我在马老师程序基础上面改的程序。
但是和我要实现的东西不一样。

必须按2下才可以实现单击的目的。

摆脱大家看看怎么才能实现 一个按键 单击   双击 的目的。
被这个问题困扰了很久。

希望大侠给出自己的意见,怎么样才可以实现单击,双击在一个按键上面 的目的。
想骂人的请闭嘴。

nome 发表于 2011-8-17 15:27:33

别说让马老师 随便叫个人都没人给你写 闲的蛋疼了?你说大家讨论下思路 还差不多 LZ 典型自我为中心。。

dongzhiqing 发表于 2011-8-17 15:30:58

我以前实现过的,长按,短按,单击,双击,等,理解好那个状态机就行了。

tiancaigao7 发表于 2011-8-17 15:42:29

双击只不过是两次相隔时间很短的单击,因此利用定时器记录两次单击之间的时间,如果超过一个限度就算是两次单击,否则算是一次双击,这个应该不难。最简单的按键接入捕捉的引脚,中断里面读取定时器数值。

jacky82512 发表于 2011-8-17 15:45:24

回复【14楼】nome物净
-----------------------------------------------------------------------

以自我为中心,你何不站在苦难中的我考虑下。

jacky82512 发表于 2011-8-17 15:54:10

回复【16楼】tiancaigao7天才杨威利
-----------------------------------------------------------------------

你好,天才

Int_Main 发表于 2011-8-17 17:26:04

看了一下程序,感觉这程序你只是对单击和双击的时间上做了处理,对于单击和双击的点击数没有处理,不知道你调用后的程序上有没有处理好,因为你说只有击两次才实现单击功能,所以我觉得你的点击数上没处理好,都需要双击,只是时间延迟长了是单击,延时短就是双击

xingguangyouxi 发表于 2011-8-17 17:37:33

师父领进门,修行在个人!说句不好听得,就你这样的学习态度,劝你不要搞技术,没多大前途

p4s5j6 发表于 2011-8-17 18:15:24

./emotion/em084.gif

xiaobendan 发表于 2011-8-17 18:25:14

这种按键双击有意义吗?要单击之后是不是要等到定时过后才执行双击的功能呢?那样反应也太慢了点吧。

linghu2 发表于 2011-8-17 18:39:19

回复【22楼】xiaobendan仲跻东
这种按键双击有意义吗?要单击之后是不是要等到定时过后才执行双击的功能呢?那样反应也太慢了点吧。
-----------------------------------------------------------------------

你天天都在用这个双击!

nome 发表于 2011-8-17 18:43:18

回复【23楼】linghu2令狐二中
回复【22楼】xiaobendan仲跻东
这种按键双击有意义吗?要单击之后是不是要等到定时过后才执行双击的功能呢?那样反应也太慢了点吧。
-----------------------------------------------------------------------
你天天都在用这个双击!
-----------------------------------------------------------------------

hetiger 发表于 2011-8-17 18:59:28

1.先理解马老师这个使用状态机的按键程序,再把马老师书里这个章节的题目做出来。。。

2.分析一下你的需求,单击和双击----其实是多了第一次按键释放至第二次按下弹起的延时时间。。。

3.结合状态机,在写程序测试。。。尤其要理解的是状态机,是一次干一件事情的。。。

hnzhy870215 发表于 2011-8-17 20:18:28

给个程序自己移值一下,之前在网上找的,调通OK的(没有太多的注示)。

点击此处下载 ourdev_668590AHCNHE.txt(文件大小:12K) (原文件名:老化测试治具程序(12864显示按键长短功能).txt)

machao 发表于 2011-8-18 01:16:43

回复【楼主位】jacky82512
马老师能否编一个 一个按键支持单击,双击的,n击的程序
论坛上面貌似没有这样的。
-----------------------------------------------------------------------
你自己连按键的过程都不清楚,编什么程序?已经说过首先是理解多功能按件的操作过程和判别,分析出有几个稳定的状态,相互之间的转换条件,然后才是程序。

对于一个多功能按键,在操作中,只能出现:单击、长按(属于连_发类)、双击(属于N击类,但当N>2时,需要额外规定。比如5次连击,那么你击的次数大于1,但小于5,此时算什么?)三个复用情况。定义有双击动作,就不存在什么N击动作了。

你自己先在脑子里搞清楚,对着PC的一个按键多体会,把这个多功能按键的过程定义用文字表述出来(包括重要的时间定义),然后才是考虑编写程序的问题。

在实际的产品中,可能会用到n击的情况。我设计的一个控制器上,就是在正常检测工作状态下,连续短促的按一个键10次,就把设定检测时间间隔的功能锁掉/打开,防止一般人员误操作,进入设置状态,把参数搞乱。这样的程序并不复杂,关键是你的理解。

machao 发表于 2011-8-18 01:28:44

这个DD就作为一个训练的题目吧,不是马上要“全国大学生电子设计大赛”吗,看看这些“精英”们有谁能给出漂亮的答案。

题目就是一个I/O口,接一个按键,实现多功能操作:

功能为:单击+双击+长按。

用在时钟时间设置上。比如设置时钟时间有时、分、秒。

假定开始设置秒位:单击一次,秒的个位加1,长按(按下时间超过1秒)一次,秒的十位加1。如果是双击,表示秒设置完,转到分位设置了。。。。。

只要分析按键的操作过程和区分判别的条件,给出一个读一个按键的低层代码,能返回:

1。无按键
2。单击
3。长按
4。双击

xiaobendan 发表于 2011-8-18 07:20:05

首先要保证精英们都来这里看到这个题目

Forest_liu 发表于 2011-8-18 07:49:52

晕,参加电设的也有很多强人,虽然不一定是精英,但还是很有实力的,这种题目很多人都很早就会做了,没必要当作电设的训练题。

plc_avr 发表于 2011-8-18 08:16:51

眼高手低的人,大把存在.......

duxingkei 发表于 2011-8-18 08:18:54

回复【26楼】hnzhy870215
-----------------------------------------------------------------------

看了下 ,代码很长。
需要的时候参考下!

jacky82512 发表于 2011-8-18 08:25:57

回复【28楼】machao
-----------------------------------------------------------------------

“全国大学生电子设计大赛”在哪里举办?在网上公布情况吗?
那就静静的期待吧,一边在继续我的工作。

jacky82512 发表于 2011-8-18 08:27:54

回复【20楼】xingguangyouxi
-----------------------------------------------------------------------

怎么样才算好的学习态度?不防说说看看。看看你境界有多高。随随便便指指点点态度,态度还是不要指指点点好。

jacky82512 发表于 2011-8-18 08:31:06

回复【25楼】hetiger
-----------------------------------------------------------------------

感谢你,你说的很到点。
不想论坛上面的某些人,要不骂人,要不就装,要不---。
大家,就应该有大家的风度。

jacky82512 发表于 2011-8-18 08:31:53

回复【23楼】linghu2令狐二中
-----------------------------------------------------------------------

你想说什么,表达什么。
你说说天天都在用双击?是意思

jacky82512 发表于 2011-8-18 08:38:11

回复【27楼】machao
-----------------------------------------------------------------------

试问当1个写软件的人,写不出来,也没有什么好的思路的时候,该做点什么。
或者是遇到困难的时候。在此感谢马老师。真的感谢了。

sunnyhook 发表于 2011-8-18 08:56:06

回复【17楼】jacky82512
-----------------------------------------------------------------------
以自我为中心,你何不站在苦难中的我考虑下。
-----------------------------------------------------------------------
写不出程序来,说明你对这个按键本身没有太多的了解,甚至不了解它的工作原理,别说就一个开关,如果你把它工作在单击、双击、N连击、长按的各种状态下的分解动作好好理解一番,那么程序自然就出来了。除非你连写代码都成问题,那就没法帮你了。建议你练练太极,好好把浮躁的性子克服一下,然后找只按键,用慢动作按几次,这个事情是需要悟的。

本来想给你贴个程序来着,罢了罢了,写了这么多字,也啰嗦得差不多了,唉……

igoal 发表于 2011-8-18 09:26:57

楼主需要看一看一篇很多论坛上都有的帖子《提问的智慧》

在这个论坛里谁都不欠谁的,所以首先要自己努力去解决,然后才是提问,在提问前也要多问自己几次,“真的就没办法了吗?”


解答马老师的问题,不一定对,还请大家拍砖。
http://cache.amobbs.com/bbs_upload782111/files_44/ourdev_668999BSZ2LD.jpg
(原文件名:key.jpg)

jacky82512 发表于 2011-8-18 13:39:52

今天上午想了办法总算实现了目的。虽然不是很好的状态机。目的实现了。
还是感谢各位了。特别是给出很多建议的朋友。

jacky82512 发表于 2011-8-18 16:51:57

回复【39楼】igoal
-----------------------------------------------------------------------

你这个状态图用什么【软件】分析的,那么好的软件可否分享下。

packer 发表于 2011-8-18 19:28:46

分析【软件】叫大脑
实现软件很多,word,visio,matlab,fpga
我最喜欢用纸和铅笔

hnzhy870215 发表于 2011-8-18 20:55:00

【32楼】 duxingkei 独行客
-------------------------------
下面是单双击程序,上面的是带12864串口驱动,很好用的程序

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int

#include<intrins.h>
#include<stdlib.h>


#define LCD_data P0


sbit cs=P2^4;
sbit sid=P2^5;
sbit sclk=P2^6;
sbit PSB=P2^1;

//根据按键硬件连接定义按键键值
#define KEY_VALUE_1          0x0e
#define KEY_VALUE_2          0x0d
#define KEY_VALUE_3          0x0b
#define KEY_VALUE_4          0x07
#define KEY_NULL             0x0f


//定义长按键的TICK数,以及连_发间隔的TICK数
#define KEY_LONG_PERIOD         300
#define KEY_CONTINUE_PERIOD   150

//定义按键返回值状态(按下,长按,连_发,释放)
#define KEY_DOWN               0x80
#define KEY_LONG               0x40
#define KEY_CONTINUE             0x20
#define KEY_UP                   0x10

//定义按键状态
#define KEY_STATE_INIT             0
#define KEY_STATE_WOBBLE         1
#define KEY_STATE_PRESS            2
#define KEY_STATE_LONG             3
#define KEY_STATE_CONTINUE         4
#define KEY_STATE_RELEASE          5

//其中io_key_1等是我们按键端口的定义,如下所示:
sbit io_key_1 = P3^0 ;
sbit io_key_2 = P3^1 ;
sbit io_key_3 = P3^2 ;
sbit io_key_4 = P3^3 ;

sbit led1=P1^0;
sbit led2=P1^1;
sbit led3=P1^2;
sbit led4=P1^3;
static uint KeyScan(void)
{
   if(io_key_1 == 0)return KEY_VALUE_1 ;
   if(io_key_2 == 0)return KEY_VALUE_2 ;
   if(io_key_3 == 0)return KEY_VALUE_3 ;
   if(io_key_4 == 0)return KEY_VALUE_4 ;
   return KEY_NULL ;
}

uint GetKey()
{
   static uint s_u8KeyState = KEY_STATE_INIT ;
   static uint s_u8KeyTimeCount = 0 ;
   static uint s_u8LastKey = KEY_NULL ;   //保存按键释放时候的键值
   uint KeyTemp = KEY_NULL ;

   KeyTemp = KeyScan() ;         //获取键值

   switch(s_u8KeyState)
   {
         case KEY_STATE_INIT :
               {
                     if(KEY_NULL != (KeyTemp))
                     {
                         s_u8KeyState = KEY_STATE_WOBBLE ;
                     }
               }
         break ;

         case KEY_STATE_WOBBLE :       //消抖
               {
                     s_u8KeyState = KEY_STATE_PRESS ;   
               }
         break ;

         case KEY_STATE_PRESS :
               {
                     if(KEY_NULL != (KeyTemp))
                     {
                         s_u8LastKey = KeyTemp ; //保存键值,以便在释放按键状态返回键值
                         KeyTemp |= KEY_DOWN ;   //按键按下
                         s_u8KeyState = KEY_STATE_LONG ;
                     }
                     else
                     {
                         s_u8KeyState = KEY_STATE_INIT ;
                     }
               }
         break ;

         case KEY_STATE_LONG :
               {
                     if(KEY_NULL != (KeyTemp))
                     {
                         if(++s_u8KeyTimeCount > KEY_LONG_PERIOD)
                         {
                           s_u8KeyTimeCount = 0 ;
                           KeyTemp |= KEY_LONG ;   //长按键事件发生
                           s_u8KeyState = KEY_STATE_CONTINUE ;
                         }
                     }
                     else
                     {
                         s_u8KeyState = KEY_STATE_RELEASE ;
                     }
               }
         break ;

         case KEY_STATE_CONTINUE :
               {
                     if(KEY_NULL != (KeyTemp))
                     {
                         if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)
                         {
                           s_u8KeyTimeCount = 0 ;
                           KeyTemp |= KEY_CONTINUE ;
                         }
                     }
                     else
                     {
                         s_u8KeyState = KEY_STATE_RELEASE ;
                     }
               }
         break ;

         case KEY_STATE_RELEASE :
               {
                     s_u8LastKey |= KEY_UP ;
                     KeyTemp = s_u8LastKey ;
                     s_u8KeyState = KEY_STATE_INIT ;
               }
         break ;

         default : break ;
   }
   return KeyTemp ; //返回键值   
}
delay(uint a)
{
uint x,y;
for(x=a;x>0;x--)
   for(y=110;y>0;y--);
}

void main(void)
{   
   uint KeyValue = KEY_NULL;
   uint temp = 0 ;
   while(1)
   {
      delay(50);
         KeyValue=GetKey();
         KeyValue&=KeyValue;
         
         if(KeyValue == (KEY_VALUE_1 | KEY_DOWN)) P1=0xfe ;
         if(KeyValue == (KEY_VALUE_1 | KEY_LONG))P1=0xef ;
         if(KeyValue == (KEY_VALUE_1 | KEY_CONTINUE)) P1=0xf0;
         //if(KeyValue == (KEY_VALUE_1 | KEY_UP))P1=0x0f;
       KeyValue=0;
   }

}

packer 发表于 2011-8-19 00:55:36

ls的,没看出能实现双击啊,只不过持续按能响应2次
今天太晚,先给出波形图

http://cache.amobbs.com/bbs_upload782111/files_44/ourdev_668984G9JD5Y.jpg
3种按键情况 (原文件名:按键.jpg)

packer 发表于 2011-8-19 01:01:21

简单说明下
1、长按:1秒以上,且只响应一次,除非松手
2、双击:1秒内(要小于长按时间,否则容易引起混乱)按下2次,有且仅有2次
3、可以看出,第一次按下时并不能知道是单击还是双击,必须等到1秒才能做出判断

madara 发表于 2011-8-19 03:48:56

我给你个思路:

记录4个时间量
T3:上次按下
T2:上次抬起
T1:本次按下
T0:本次抬起

设置4个时间长度判断值
1:按键最长有效间隔时间
2:按键最短有效间隔时间
3:最长按键时间长度
4:最短按键时间长度

这样通过T3~T0的各种比较可以判断出
1 ___-___________ 单击

2 ___---_________ 长单击

3 ___-_-_________ 短间隔 短双击

4 ___-___-_______ 长间隔 短双击

5 ___---_---_____ 短间隔 长双击

6 ___---___---___ 长间隔 长双击

7 ___-_---_______ 短间隔 短长击

8 ___-___---_____ 长间隔 短长击

9 ___---_-_______ 短间隔 长短击

10___---___-_____ 长间隔 长短击

11_______________ 没按

12--------------- 一直在按,可以连_发
一共12种状态


楼下的再多记录次按键时间,看看一共能拼出来几种状态

xiaobendan 发表于 2011-8-19 07:31:57

哇,一个按键出这么多状态,那么再加上扫键法,可以用极少的IO实现无限的功能了,厉害厉害!

hetiger 发表于 2011-8-19 07:55:39

嗯。。。。

不止。。。

LS,我想要是算上上升沿和下降沿的判断,估计组合还多。。。

igoal 发表于 2011-8-19 08:07:56

搞这么多组合有意义吗?用户能搞清楚哪个是长按哪个是短按?这要是在产品上用估计要被客户骂死。

jacky82512 发表于 2011-8-19 08:59:51

回复【46楼】madara
-----------------------------------------------------------------------

强悍啊,慢慢去实现。

jacky82512 发表于 2011-8-19 09:04:51

回复【44楼】packer
-----------------------------------------------------------------------

用啥剪的这个图?示波器测引脚的电平吗?

packer 发表于 2011-8-19 09:34:04

回复【47楼】xiaobendan 仲跻东
哇,一个按键出这么多状态,那么再加上扫键法,可以用极少的io实现无限的功能了,厉害厉害!
-----------------------------------------------------------------------

他那是莫尔斯电码

xcodes 发表于 2011-8-19 09:42:05

楼主是个狗东西,大家不要理他

packer 发表于 2011-8-19 09:54:52

回复【51楼】jacky82512
回复【44楼】packer   
-----------------------------------------------------------------------
用啥剪的这个图?示波器测引脚的电平吗?
-----------------------------------------------------------------------

用示波器自带上位机软件截图,测的按键引脚
+5
|
--
||
|| 10K
--
|------->示波器
|
\
   \ 按键
|
GND

jacky82512 发表于 2011-8-19 13:40:02

回复【53楼】xcodes
-----------------------------------------------------------------------

不想理你,你还做什么

jacky82512 发表于 2011-8-19 13:40:42

回复【53楼】xcodes
-----------------------------------------------------------------------

别污染马老师论坛的空气

jacky82512 发表于 2011-8-19 13:41:57

回复【54楼】packer
-----------------------------------------------------------------------

示波器这个功能不错,我还没有用过。有机会试试

nome 发表于 2011-8-19 17:23:00

回复【46楼】madara
我给你个思路:
记录4个时间量
t3:上次按下
t2:上次抬起
t1:本次按下
t0:本次抬起
设置4个时间长度判断值
1:按键最长有效间隔时间
2:按键最短有效间隔时间
3:最长按键时间长度
4:最短按键时间长度
这样通过t3~t0的各种比较可以判断出
1 ___-___________ 单击
2 ___---_________ 长单击
3 ___-_-_________ 短间隔 短双击
4 ___-___-_______ 长间隔 短双击
5 ___---_---_____ 短间隔 长双击
6 ___---___---___ 长间隔 长双击
7 ___-_---_______ 短间隔 短长击
8 ___-___---_____ 长间隔 短长击
9 ___---_-_______ 短间隔 长短击
10___---___-_____ 长间隔 长短击
11_______________ 没按
......
-----------------------------------------------------------------------
按的人估计也很累一会就晕了我到底要按啥了?

machao 发表于 2011-8-19 18:42:32

浮躁、浮夸、纸上谈兵、好高务远。。。。。

当前电子工程师和学校学生所具备的毛病在这里曝露出来了。

以上所有对于这个三功能“简单”按键的回帖,目前没有一个能到60分。

劝那些想真正提高自己本事的朋友,要静下心,仔细全面做练习设计,贴上点有价值的东西,大家讨论。

测试环境如下:

main(viod)
{
    .........

    while
    {
      if (time_10ms_ok)            //每10ms执行一次,
      {
             time_10ms_ok =0;
             key = read_key();       //《====== 10ms读按键程序,根据返回键值,点亮不同的LED灯,测试按键操作是否正常
             if (key == k1)
               //点亮A_LED,关闭C_LED和D_LED
             else if(key == k2)
               //点亮A_LED,关闭C_LED和D_LED
             else if(key == k3)
               //点亮A_LED,关闭C_LED和D_LED
         }
   }
}

有兴趣的朋友,补上read_key()函数。该函数读一个按键,返回单击、双击、长按、无按键4种情况。



}

machao 发表于 2011-8-19 18:42:43

浮躁、浮夸、纸上谈兵、好高务远。。。。。

当前电子工程师和学校学生所具备的毛病在这里曝露出来了。

以上所有对于这个三功能“简单”按键的回帖,目前没有一个能到60分。

劝那些想真正提高自己本事的朋友,要静下心,仔细全面做练习设计,贴上点有价值的东西,大家讨论。

测试环境如下:

main(viod)
{
    .........

    while
    {
      if (time_10ms_ok)            //每10ms执行一次,
      {
             time_10ms_ok =0;
             key = read_key();       //《====== 10ms读按键程序,根据返回键值,点亮不同的LED灯,测试按键操作是否正常
             if (key == k1)
               //点亮A_LED,关闭C_LED和D_LED
             else if(key == k2)
               //点亮A_LED,关闭C_LED和D_LED
             else if(key == k3)
               //点亮A_LED,关闭C_LED和D_LED
         }
   }
}

有兴趣的朋友,补上read_key()函数。该函数读一个按键,返回单击、双击、长按、无按键4种情况。



}

machao 发表于 2011-8-19 18:50:42

首先请考虑和定义:单击、双击、长按、无按键4种情况的分类。比如,第一次短按后,马上按下键不放,这个情况算什么按键?

另外,每次按键过程中,按键按下和释放,都要考虑消抖,否则你的按键就会经常出现误动(读)。

最后,程序要简捷,合理,优化,方便调试。

igoal 发表于 2011-8-20 07:59:04

“第一次短按后,马上按下键不放,这个情况算什么按键?”按我的理解应该算按两次键,第一次算单击,第二次算长按。

jacky82512 发表于 2011-8-20 08:17:54

回复【61楼】machao
-----------------------------------------------------------------------
小小按键,竟有那么多道道。可见---我深思去

nome 发表于 2011-8-20 15:28:34

回复【62楼】igoal
“第一次短按后,马上按下键不放,这个情况算什么按键?”按我的理解应该算按两次键,第一次算单击,第二次算长按。
-----------------------------------------------------------------------

短按被取消了执行长按啊呵呵

yuzr 发表于 2011-8-20 15:39:19

mark

packer 发表于 2011-8-20 16:44:34

回复【62楼】igoal
“第一次短按后,马上按下键不放,这个情况算什么按键?”按我的理解应该算按两次键,第一次算单击,第二次算长按。
-----------------------------------------------------------------------

我认为是次双击
不过,做乙方习惯了,在现实中我会说
“甲方,你说算什么我就怎么做”

nome 发表于 2011-8-20 16:56:14

#define LONG_KEY_UP   1//长按键松开500ms
#define LONG_KEY_DOWN 2//长按键进行中
#define SINGLE_KEY   3   //单击完成(500ms无按键)
#define DOUBLE_KEY4    //双击完成(可扩展N击)
#define NO_KEY   5      //无按键

#define KEYDOWN 1
#define KEYUP 0
//每10ms调用一次该函数
unsigned char ReadKey()
{
        static unsigned char flag=0;
        static unsigned int keydowncount=0;
        static unsigned int keyupcount=0;
        unsigned char keystat;
        keystat=ReadKey(); //读按键状态
        if(keystat==KEYDOWN)   //按下状态
        {
                if(keyupcount!=0)            //        松开有计数 说明正在等待的时候按下
                {
                        keyupcount=0;
                        flag=1;      //等待连击
                }
                keydowncount++;
                if(keydowncount>100)    //长按状态
                        return LONG_KEY_DOWN;
        }
        else    //松开状态
        {
                if(keydowncount>=5)   //有按下计数的时候才会启动
                        keyupcount++;
                if(keydowncount<5)//防抖
                {
                        keydowncount=0;
                        return NO_KEY;
                }
                else if (keydowncount>=5&&keydowncount<100) //防抖成功而且 按下小于1s
                {
                        if(keyupcount>50)   //松开大于500ms
                        {
                                keydowncount=0;
                                keyupcount=0;
                                if(flag)    //等待双击标志位
                                        return DOUBLE_KEY;   //双击
                                else
                                {
                                        flag=0;//等待双击失败
                                        return SINGLE_KEY;   //单击
                                }
                        }
                        else    //松开不到500ms
                        {
                                return NO_KEY;
                        }
                }
                else if (keydowncount>=100)//按下大于1S
                {
                        if(keyupcount>50)   //松开500ms
                        {
                                keydowncount=0;
                                keyupcount=0;                       
                                flag=0;
                                return LONG_KEY;
                        }
                        else
                        {
                                return NO_KEY;                       
                        }

                       
                }
        }

       
               
}
readkey.courdev_669474TM6GRU.txt(文件大小:1K) (原文件名:ReadKey.txt)


粗略的写了一下 上来接受板砖“当前电子工程师和学校学生所具备的毛病在这里曝露出来了。”受教了

igoal 发表于 2011-8-21 08:20:26

“第一次短按后,马上按下键不放,这个情况算什么按键?”这里面其实隐含了什么情况才算一次按键触发事件的判断,有些人把按键按下去作为一次按键事件发生,有些人把按键抬起作为一次按键事件发生。我一般都是把按键抬起当做一次按键发生的。

jacky82512 发表于 2011-8-22 08:56:30

回复【67楼】nome物净
-----------------------------------------------------------------------

我试验下。谢了朋友

max232 发表于 2011-8-22 10:59:35

围观一下

楼主是坛子里为数不多的牛人,想必是过多了衣来伸手,饭来张口的生活

jacky82512 发表于 2011-8-23 08:20:10

回复【70楼】max232X工
-----------------------------------------------------------------------

什么意思这个是?

machao 发表于 2011-8-23 13:04:15

回复【68楼】igoal
“第一次短按后,马上按下键不放,这个情况算什么按键?”这里面其实隐含了什么情况才算一次按键触发事件的判断,有些人把按键按下去作为一次按键事件发生,有些人把按键抬起作为一次按键事件发生。我一般都是把按键抬起当做一次按键发生的。
-----------------------------------------------------------------------

一次按键的事件发生在按下还是释放,应该考虑实际应用的需要。但不关如何选择,按键事件的发生,在整个按键过程中(包括按下和释放削抖的过程)只能定义出现一次。

对于一个普通的单次按键,还是定义在按下为好,通常在削抖后就可以作为按键事件发生了。
这样系统的反映及时,一旦系统有反映了,操作者会马上释放,而不会按更长的时间。在一些特殊的应用中,比如测试人的反映时间(8个LED随机亮一个,测试者马上按下对应的按键,测试反映的时间),如果是释放按键才算按键发生的话,测试时间就不准确了,因为包含了按下去的时间长短,而不是马上按下的时间。在此类的需求时,按键发生可以定义在按下削抖之前的。

而对于多功能的按键操作,比如长/短按键、连击等,第一次的按键发生只能定义在释放,但第2次的按键发生也需要根据情况定。比如一个只支持单次和双击的按键,第1次按键发生必需在按键释放,而第2次双击按键发生,则放在按键按下为好。

以上采用状态机的按键设计方法,都是很方便的。

========================================================
    把按键抬起当做一次按键发生,也不是不可以。但对于有些操作者,会造成系统不必要的延误,如果系统本身编写不完善会导致崩溃出问题。
    这些操作者往往是按下键后,看到反映(比如是设置时间过程,时间位加了1了),或听到按键提示音后,才释放按键。如果按键发生定义在释放,那么操作者就看(听)不到按键的反映,可能会一直按着按键不放,此时你的系统就惨了!

jacky82512 发表于 2011-8-23 13:18:01

回复【72楼】machao
-----------------------------------------------------------------------

受教了。

machao 发表于 2011-8-23 14:36:37

题目:多功能按键设计。利用一个I/O口,接一个按键,实现3功能操作:单击 + 双击 + 长按。
============================================================================
用户基本操作定义:
    1。短按操作:按键按下,按下时间<1s,属于一次短按操作
    2。长按操作:按键按下,按下时间>1s,属于一次长按操作

在正常0.5s内无按键操作为启始按键扫描条件下,扫描按键将产生以下3种按键事件:
    1。长按事件:任何1次出现的长按操作都属于长按事件
    2。单击事件:1次短按操作后,间隔0.5内没有短按操作
    3。双击事件:2次短按操作间隔时间<0.5s,则2次短按操作为1次双击事件,且2次短按都取消

特别操作情况定义:
    1。短按操作和长按操作间隔<0.5s,以及,长按操作和短按操作间隔<0.5s,均不产生双击事件
    2。连续n次(n为奇数)短按操作,且间隔均<0.5s,产生(n-1)/2次双击事件+1次单击事件
    3。连续n次(n为偶数)短按操作,且间隔均<0.5s,产生n/2次双击事件

对按键操作者的建议:   
    由于按键的多功能性质,建议操作者每次在单击/长按/双击按键事件发生后,隔0.5s后再进行下一次的按键操作。因为在特别操作情况下,程序是保证按定义进行判断和处理的,主要是怕操作者自己记不清楚导致操作失误。

对软件设计者的要求:
    1。应该全面进行分析,给出严格定义和判断条件,如上所示。如果自己都不清楚,你的设计出的系统就不稳定,不可靠。
    2。在1的基础上,编写出符合要求的程序,并进行全面测试。

/*=============
低层按键(I/0)扫描函数,即低层按键设备驱动,只返回无键、短按和长按。具体双击不在此处判断。参考本人教材的例9-1,稍微有变化。教材中为连_发。
===============*/

#define key_input    PIND.7    // 按键输入口

#define N_key    0             //无键
#define S_key    1             //单键
#define D_key    2             //双键
#define L_key    3             //长键

#define key_state_0        0
#define key_state_1        1
#define key_state_2        2

unsigned char key_driver(void)
{
    static unsigned char key_state = key_state_0, key_time = 0;
    unsigned char key_press, key_return = N_key;

    key_press = key_input;                  // 读按键I/O电平

    switch (key_state)
    {
      case key_state_0:                              // 按键初始态
      if (!key_press) key_state = key_state_1;           // 键被按下,状态转换到按键消抖和确认状态
      break;
      
      case key_state_1:                      // 按键消抖与确认态
      if (!key_press)
      {
             key_time = 0;                   //
             key_state = key_state_2;          // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
      }
      else
             key_state = key_state_0;          // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
      break;
      
      case key_state_2:
      if(key_press)
      {
             key_return = S_key;      // 此时按键释放,说明是产生一次短操作,回送S_key
             key_state = key_state_0;   // 转换到按键初始态
      }
      else if (++key_time >= 100)   // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
      {
             key_return = L_key;      // 按下时间>1000ms,此按键为长按操作,返回长键事件
             key_state = key_state_3;   // 转换到等待按键释放状态
      }
      break;

      case key_state_3:               // 等待按键释放状态,此状态只返回无按键事件
      if (key_press) key_state = key_state_0;        //按键已释放,转换到按键初始态
      break;
    }       
    return key_return;
}

/*=============
中间层按键处理函数,调用低层函数一次,处理双击事件的判断,返回上层正确的无键、单键、双键、长键4个按键事件。
本函数由上层循环调用,间隔10ms
===============*/

unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
   
    key_temp = key_driver();
   
    switch(key_m)
    {
      case key_state_0:
            if (key_temp == S_key )
            {
               key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
               key_m = key_state_1;
            }
            else
               key_return = key_temp;      // 对于无键、长键,返回原事件
            break;

      case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
               key_return = D_key;         // 返回双击键事件,回初始状态
               key_m = key_state_0;
            }
            else                              
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
               if(++key_time_1 >= 50)
               {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;   // 返回初始状态
               }
             }
             break;
    }
    return key_return;
}   

下面,根据程序分析按键事件的反映时间:
1。对于长键,按下超过1s马上响应,反映最快
2。对于双键,第2次按键释放后马上得到反映。
3。对于单键,释放后延时拖后500ms才能响应,反映最慢。这个与需要判断后面是否有双击操作有关,只能这样。实际应用中,可以调整两次单击间隔时间定义,比如为300ms,这样单击的响应回快一点,单按键操作人员需要加快按键的操作过程。如果产品是针对老年人的,这个时间不易太短,因为年纪大的人,反映和动作都比较慢。

   当然,上面两段可以合在一起。我这样做的目的,是为了可以方便的扩展为N击(当然,需要做修改)。可是最底层的就是最基本的操作处理短按和长按,不用改动的。至于双击,还是N击,在中间层处理。这就是程序设计中分层结构的优点。

测试代码环境如下:


interrupt void timer0_comp_isr(void)       // 定时器10ms中断服务
{
       time_10ms_ok = 1;
}


main(viod)
{
    .........

    while
    {
      if (time_10ms_ok)            //每10ms执行一次,
      {
             time_10ms_ok =0;
             key = key_read();       //《====== 10ms一次调用按键中间层函数,根据返回键值,点亮不同的LED灯,全面测试按键操作是否正常
             if (key == L_key)
               ........//点亮A_LED,关闭B_LED和C_LED
             else if(key == D_key)
               ........//点亮B_LED,关闭A_LED和C_LED
             else if(key == S_key)
               ........//点亮C_LED,关闭A_LED和B_LED
         }
   }
}

=================================================

通过以上这个看似简单的按键,看在应用中如何变化,以及如何在实际产品中全面、可靠的进行设计。

=========================================================

lz,我这个老家伙完成你的作业,还行吗?

大家给打个分数,100分太满了,90分有吧?

=========================================================

jacky82512 发表于 2011-8-24 09:46:09

回复【74楼】machao
-----------------------------------------------------------------------

多谢马老师了。仔细研读下。

jacky82512 发表于 2011-8-24 11:29:21

是不是要加这个?return key_return;我在琢磨下
/*=============
中间层按键处理函数,调用低层函数一次,处理双击事件的判断,返回上层正确的无键、单键、双键、长键4个按键事件。
本函数由上层循环调用,间隔10ms
===============*/

unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
   
    key_temp = key_driver();
   
    switch(key_m)
    {
      case key_state_0:
            if (key_temp == S_key )
            {
               key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
               key_m = key_state_1;
            }
            else
               key_return = key_temp;      // 对于无键、长键,返回原事件
            break;

      case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
               key_return = D_key;         // 返回双击键事件,回初始状态
               key_m = key_state_0;
            }
            else                              
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
               if(++key_time_1 >= 50)
               {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;   // 返回初始状态
               }
             }
             break;
    }
return key_return; //这里是不是要加上这句,因为不加这个,key = key_read();没有返回值吧,是不是这样

}   


==================================================================
是漏掉了返回语句,谢谢提醒。
已经补上了。                        __machao__
==================================================================

linghu2 发表于 2011-8-24 11:58:13

回复【36楼】jacky82512
回复【23楼】linghu2  令狐二中
-----------------------------------------------------------------------

你想说什么,表达什么。
你说说天天都在用双击?是意思
-----------------------------------------------------------------------

鼠标双击,你没有用?

servingapple 发表于 2011-12-23 09:02:59

慢慢的理解“真谛”

fazhi123 发表于 2012-7-8 14:15:50

machao 发表于 2011-8-23 14:36 static/image/common/back.gif
题目:多功能按键设计。利用一个I/O口,接一个按键,实现3功能操作:单击 + 双击 + 长按。
============== ...

这个如何改成软件延时扫描,不用定时器中断?

and001 发表于 2012-7-11 19:14:31

楼主好象是不不爱动脑筋的人!

i_Hug 发表于 2013-3-31 16:36:25

对象分析很重要啊!!!

liuyongliuyong 发表于 2013-7-4 09:40:40

machao 发表于 2011-8-18 01:28 static/image/common/back.gif
这个DD就作为一个训练的题目吧,不是马上要“全国大学生电子设计大赛”吗,看看这些“精英”们有谁能给出漂 ...

马老师,您好,在百度文库看到你写的一个按键的单击、双击和长按功能的程序,觉得写的很好,用状态机的思想以前没用过。这个按键扫描程序是不是只可以实现扫描一个按键?如果要扫描两个按键,应该怎么写呢?

xzq1019@163.com 发表于 2013-7-4 09:47:45

jacky82512 发表于 2011-8-18 08:31 static/image/common/back.gif
回复【23楼】linghu2令狐二中
---------------------------------------------------------------------- ...

左右互搏,击小弟弟,猜的,应该是这个意思吧

panhai0101 发表于 2015-1-21 19:18:33

machao 发表于 2011-8-19 18:42
浮躁、浮夸、纸上谈兵、好高务远。。。。。

当前电子工程师和学校学生所具备的毛病在这里曝露出来了。


马老师,若是把按键处理放在外部中断里,那么,利用定时器定时10ms来延时就做不到了吧
页: [1]
查看完整版本: 马老师能否编一个 一个按键支持单击,双击的程序?