搜索
bottom↓
回复: 355

思路决定出路--键盘扫描详解(只讲解决方法,无代码)

  [复制链接]

出0入0汤圆

发表于 2008-5-29 22:29:12 | 显示全部楼层 |阅读模式
按键扫描是每个搞单片机的都会遇到的问题,也是一个开发人员必须具备的基本功。先从最基本的说起。在此声明,没有代码,也不要向我要代码,也不想穿裤子,如果你看了帖子还写不出代码,那么我只能说你太笨了。。。。。。。。还是那句话,搞开发重要的是思想,而不是代码,代码只是工具
通常的按键扫描程序是这样做的
键盘按下?YES---延时去抖----键盘按下?YES-----确实按下了,按键有效---退出
   NO                           NO
  退出                         退出
很多教材上都是这样写,但这个程序却是误人子第的
问题来了,这个程序不太好用,有时按一下,或按久一点,程序会认为你按了很多次!!为什么???
因为没有判断按键释放,当我们按下键盘时,程序可能已经跑了N个来回了。

好了,我们来个改进版的按键扫描程序
键盘按下?YES---延时去抖----键盘按下?YES-----确实按下了,按键有效---A按键释放?YES--退出
   NO                           NO                                       NO
  退出                         退出                                    返回A
这是大多数人的按键扫描程序,在一般的场合也能用。这个程序比第一个要好点,但也好不到哪里去。为什么?判断按键释放的方法太笨了,如果我们一直按下这个键盘会怎么样?程序在此死等,可能你说我还有中断,但中断处理完了你还得回来傻等!而且其他按键也将被屏蔽。

那么,让我们好好想想该怎么做。。。。。。。(未完待续,欢迎大家跟贴,谈一下自己的解决方法;看有没有同道中人)

继上:
谢谢大家的跟贴,看来同道中人还很多
在这里严重BS一下自以为是老鸟的菜鸟【2楼】 xingcn 星尘  你除了模仿别人的东西还会什么?如果你

有原创的东西就让大家看看。
;=========================================================================================
言归正传,其实楼下很多人都给出了较好的按键扫描程序,但没有人解释一下为什么要那么做。
现在我来谈谈自己的看法 。先假设按键平时是高电平,按下后是低电平。也就是按键低电平有效
那么一个完整的按键过程会发生什么?按键输入脚电平变化顺序是  高----》低----》高
                                                          未按键  按下键   键释放
真正的高手看到这里就恍然大悟,后面的基本不用看了。
好的按键扫描程序不是判断按键是否被按下,而是判断按键电平变化的变化顺序是否符合  高----》低

----》高 ,其他的按键电平变化顺序都是非法的 。这是按键扫描的基本原理。那么该怎么来判断呢?
进入按键扫描后,我们可能会扫到高电平,那么就需要判断到底是未按键导致的高电平还是按下后键释

放导致的高电平,这里用一个位变量(取名为键前状态,1=曾经按下,0=未按下)做为标志就可以了;

也可能会扫到低电平,那么需要判断这个低电平是由未按键到按下键得到的,还是扫描之前本来就是低

电平,这里也用一个位变量(取名为键有效1=有效,0=无效)做为标志。好了,下面给一个简单的流程
主程序初始化操作,键前状态=0,键有效=0
                                       按键扫描流程
     
键盘===高电平----》键有效=1--》A1键盘曾经按下后变成高电平,键盘已释放,复位键前状态=0---》退出
  !           !
  !           --》键有效=0--》未按下---》退出
  !     
  !     
  !     
  !===低电平----》键前状态=1--》键盘曾经按下,现在仍然是低电平--》退出
               !
               --》键前状态=0--》未按下---》A2键盘由高电平变成低电平,置键有效=1,键前状态=1;---》退出

为了突出重点,这个流程没有加去抖处理,想要稳定的效果,必须加延时去抖处理。按这个流程处理按键,响应速度快,
也不会误判。你就是按一天也,不会傻等。按键有效之后,每次只进来看一下就走。最大的好处就是可以实现真正的模块化
按键处理完全由这个程序控制,不必在其他地方判断按键释放没有。
另外再简单谈一下长按,短按的判断。要判断长按,在按键有效之后(A2处)启动定时器,然后在按键释放(A1处)后判断时
间的长短,大于一定时间算长按。至于组合键盘的处理,在上面的流程上稍加改动就可以实现,等有时间再详谈。

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2008-5-29 22:50:40 | 显示全部楼层
我一般用第二种方法。
通常情况下,按键过程中除了中断(多数是显示中断这些直观的东西),你还指望程序干别的吗?

出0入0汤圆

发表于 2008-5-29 22:50:51 | 显示全部楼层
最好是完了,开场白就是菜鸟级的,方案不会有什么价值的。

出0入0汤圆

发表于 2008-5-29 22:50:57 | 显示全部楼层
稍微用代码表示一下更能说明问题
按键处理的事情的确很多,按下后只处理一次,等待一段时间后连续处理,处理的间隔时间随按住的时间增加而减少,按键的组合,长按和短按,单次和双击,密切关注LZ这些功能是不是都能讲到。

出0入0汤圆

发表于 2008-5-29 22:52:01 | 显示全部楼层
我的按键是这样处理的
1.读健状态,与上次健状态比较,不同,更新健状态和当前时间,相同则时间不变(或者加一)
2.当前时间与最后一次更新按键时间比较,超出规定时间,则可以判断健已经稳定。
3.与以前比较,就可以进行健处理了。

缺点,用的RAM多,优点,不等待

#include<reg52.h>
#include<keydefine.h>

void ReadKey(void)
{
        byte i,Temp0;
        bit KeyFlag;
        static byte Q0;       

        P1 = 0xff;
        Temp0 = ~P1;

        if(Temp0 != Qsw0)
        {
               Qsw0 = Temp0;
                KeyTime = Time20ms;
        }
        else
        {
                if(Time20ms - KeyTime > 3)
                        {KeyFlag = 1;}       
        }

        if(KeyFlag)
        {        KeyFlag = 0;

                i = Qsw0 &~Q0 & 0xbb; //健按下
                if(i)
                {
                        if(i & 0x80)        {KeyValue = BryRightKey;}
                        if(i & 0x20)        {KeyValue = AddRightKey;}
                        if(i & 0x10)        {KeyValue = SubbRightKey;}
                        if(i & 0x08)        {KeyValue = ReadPhotoRightKey;}
                        if(i & 0x02)        {KeyValue = LgyRightKey;}
                        if(i & 0x01)        {KeyValue = LightRightKey;}
                }

               i = Q0 &~Qsw0  & 0xbb; //健松开
                if(i)
                {
                   //健松开,处理代码;
                }  
                Q0 = Qsw0;       
        }
}

出0入0汤圆

发表于 2008-5-29 23:07:03 | 显示全部楼层
我方法同楼上,也认为这样最好了。这样按键不放时,程序不会在那死循环,只有效一次后,会当作没按键进行处理

出0入0汤圆

发表于 2008-5-29 23:07:40 | 显示全部楼层
我也贴一段吧,这是我的一个项目中的键盘扫描函数,基于UCOSII的,功能有按下处理一次,连续按住一段时间能连续处理,函数返回键值,键盘扫描是单独一个任务,定时扫描的,在时间上没有大的等待,这样说可以帮助理解


INT8U KeyScan(void)
{
    static INT8U keyrepeat,keyprevalue,times,fastad;
    INT8U KeyOut=0xfb;
    INT8U keyvalue;
    INT8U keyfirst;
    for(;KeyOut!=0x1f;KeyOut=KeyOut>>1)
    {
      KeyOutPort &= 0xf8;
      KeyOutPort |= (KeyOut&0x07);
      __delay_cycles(5);
      keyvalue = KeyInputPort&0x38;
      if(keyvalue!=0x38)
      {
          keyvalue |= (KeyOut&0x07);
          if(keyprevalue==keyvalue)
          {
              if(keyrepeat!=0xff) keyrepeat=0xff;
              else if((keyvalue==KeyLeft)||(keyvalue==KeyRight)||(keyvalue==KeyUp)||(keyvalue==KeyDown))
              {
                  if(fastad==0xff)    //针对调整按键 进行快速按键处理
                  {
                      if(times>=(100/KeyScanDelay)) times=0;
                      else
                      {
                          times++;
                          keyvalue=0xff;
                      }
                  }
                  else if(times>=(1000/KeyScanDelay))
                  {
                      fastad=0xff;
                      times=0;
                  }
                  else
                  {
                      times++;
                      keyvalue=0xff;
                  }
              }
              else keyvalue=0xff;
          }
          else
          {
              keyprevalue = keyvalue;
              keyfirst = 0xff;
          }
          break;
      }
      else keyvalue = 0;
    }
    if(keyvalue==0)
    {
        keyrepeat = 0;
        keyprevalue = 0;
        times = 0;
        fastad = 0;
    }
    else if((keyvalue==0xff)||(keyfirst==0xff)) keyvalue = 0;
    return keyvalue;
}

出0入0汤圆

发表于 2008-5-29 23:20:23 | 显示全部楼层
键盘扫描不用傻等,只要定时地扫描一次,无论有没按下,都把键值记下。连续几次键值相等,证明按键可靠。

出0入0汤圆

发表于 2008-5-30 00:00:54 | 显示全部楼层
唉,看程序挺累的,赞同电子油条只讲思路。
1、用计数器去抖动;
2、检测去抖动后键信号的上升或下降沿。
就这么简单。

出0入0汤圆

发表于 2008-5-30 00:03:13 | 显示全部楼层
状态机,马潮老师书上有很好的解决方案。

出0入0汤圆

发表于 2008-5-30 00:21:24 | 显示全部楼层

出0入0汤圆

发表于 2008-5-30 08:11:34 | 显示全部楼层
记号一下,等着看楼主的思路。楼主继续!!

出0入0汤圆

发表于 2008-5-30 08:14:15 | 显示全部楼层
我目前的做法也和上面说的一样,几次相同后认为有效

出0入42汤圆

发表于 2008-5-30 08:24:41 | 显示全部楼层
我也说一下我一直用的按键扫描的思路:

定义三个变量:

一个变量PreKey为上次读到的按键电平

另一个变量NowKey为当前读到的按键电平

最后一个变量KeyCode记录的是有动作按键

三个变量运算如下:

PreKey = NowKey;

NowKey = PINx;                             //读按键电平

KeyCode = PreKey ^ NowKey ^ KeyCode;       //运算

只要有按键按下,KeyCode对应的位就大为1,按键弹起后对应的位为0,根本就不需要再去进行其他判断.

电路连接:

           PB0 |-------------key0------------|GND
               |
           PB1 |-------------key1------------|GND
               |
           PB2 |-------------key2------------|GND
               |
           PB3 |-------------key3------------|GND
               |
           PB4 |-------------key4------------|GND
               |
           PB5 |-------------key5------------|GND
               |
           PB6 |-------------key6------------|GND
               |
           PB7 |-------------key7------------|GND



修改原因:贴电路图

出0入0汤圆

发表于 2008-5-30 08:45:08 | 显示全部楼层
顶楼上,我也是用类似的方法。

出0入0汤圆

发表于 2008-5-30 09:15:15 | 显示全部楼层
顶一下,思路决定出路,就因为这句话~~~

出0入0汤圆

发表于 2008-5-30 09:46:47 | 显示全部楼层
我的实现,中断,然后处理,随便响应一次按键或连续按键

出0入147汤圆

发表于 2008-5-30 10:06:00 | 显示全部楼层
具体的方法还是要根据按键的功能来。
对于通用的按键来说,要做到不占用中断及系统时间的同时消除抖动,我常用的方法有:多次读取键值后进行与或运算,需要连_发的话用状态机,不管什么办法,都需要一个时钟节拍,如果能用定时器的话当然很简单,如果没有定时器,那就要估算主函数的最小及最长运行时间,然后把按键扫描函数插在合适的地方。
对于一些特殊功能的按键,比如急停,紧急事件响应等我一般通过施密特触发器接到中断口上来处理。

出0入0汤圆

 楼主| 发表于 2008-5-30 22:44:30 | 显示全部楼层
d

出0入0汤圆

 楼主| 发表于 2008-5-30 22:47:46 | 显示全部楼层
继上:
谢谢大家的跟贴,看来同道中人还很多
在这里严重BS一下自以为是老鸟的菜鸟【2楼】 xingcn 星尘  你除了模仿别人的东西还会什么?如果你

有原创的东西就让大家看看。
;=========================================================================================
言归正传,其实楼下很多人都给出了较好的按键扫描程序,但没有人解释一下为什么要那么做。
现在我来谈谈自己的看法 。先假设按键平时是高电平,按下后是低电平。也就是按键低电平有效
那么一个完整的按键过程会发生什么?按键输入脚电平变化顺序是  高----》低----》高
                                                          未按键  按下键   键释放
真正的高手看到这里就恍然大悟,后面的基本不用看了。
好的按键扫描程序不是判断按键是否被按下,而是判断按键电平变化的变化顺序是否符合  高----》低

----》高 ,其他的按键电平变化顺序都是非法的 。这是按键扫描的基本原理。那么该怎么来判断呢?
进入按键扫描后,我们可能会扫到高电平,那么就需要判断到底是未按键导致的高电平还是按下后键释

放导致的高电平,这里用一个位变量(取名为键前状态,1=曾经按下,0=未按下)做为标志就可以了;

也可能会扫到低电平,那么需要判断这个低电平是由未按键到按下键得到的,还是扫描之前本来就是低

电平,这里也用一个位变量(取名为键有效1=有效,0=无效)做为标志。好了,下面给一个简单的流程
主程序初始化操作,键前状态=0,键有效=0
                                       按键扫描流程
     
键盘===高电平----》键有效=1--》A1键盘曾经按下后变成高电平,键盘已释放,复位键前状态=0---》退出
  !           !
  !           --》键有效=0--》未按下---》退出
  !     
  !     
  !     
  !===低电平----》键前状态=1--》键盘曾经按下,现在仍然是低电平--》退出
               !
               --》键前状态=0--》未按下---》A2键盘由高电平变成低电平,置键有效=1,键前状态=1;---》退出

为了突出重点,这个流程没有加去抖处理,想要稳定的效果,必须加延时去抖处理。按这个流程处理按键,响应速度快,
也不会误判。你就是按一天也,不会傻等。按键有效之后,每次只进来看一下就走。最大的好处就是可以实现真正的模块化
按键处理完全由这个程序控制,不必在其他地方判断按键释放没有。
另外再简单谈一下长按,短按的判断。要判断长按,在按键有效之后(A2处)启动定时器,然后在按键释放(A1处)后判断时
间的长短,大于一定时间算长按。至于组合键盘的处理,在上面的流程上稍加改动就可以实现,等有时间再详谈。

出0入0汤圆

发表于 2008-5-30 23:16:58 | 显示全部楼层
楼主,不是打击你

虽然你的共享思想很值得保护

不过,确实你的思路很混沌

在前一段时间,我也仔细的想过按键分析,甚至做梦都在分析流程,基本上也得到你这个HLH状态是一个有效按键

算了,我表达能力不强,你去看看machao的按键状态机吧,思路会很清晰的

-------------------------------------------------------------------------------------------------------

要判断长按,在按键有效之后(A2处)启动定时器,然后在按键释放(A1处)后判断时
间的长短,大于一定时间算长按。

你上面提到的这个关于长按的思路是不正确的:

你的手机长按3秒关机,你一直按着不放,那么它会不会关机呢?

出0入0汤圆

 楼主| 发表于 2008-5-30 23:36:21 | 显示全部楼层
你上面提到的这个关于长按的思路是不正确的:

你的手机长按3秒关机,你一直按着不放,那么它会不会关机呢?
;================================================================================
确实是不对,应该在下面的B1处判断
!===低电平----》键前状态=1--》B1键盘曾经按下,现在仍然是低电平--》退出
               !
               --》键前状态=0--》未按下---》A2键盘由高电平变成低电平,置键有效=1,键前状态=1;---》退出

出0入0汤圆

发表于 2008-5-31 00:04:42 | 显示全部楼层
楼上
你提到的手机关机的情况,确实有时需要考虑,但我觉得不能说楼主的思路是不正确的,其实我觉得楼主的思路挺清楚的啊,我想这里探讨的是一般按键的读取处理
其实按键处理我觉得要按实际情况来处理,不能写一个按键处理模块就应用于不同的应用中去,当然,基于相同的按键实现可以通用.

【2楼】 xingcn 星尘
最好是完了,开场白就是菜鸟级的,方案不会有什么价值的
========================================================
本不想说的,但还是忍不住.
我觉得楼主的这个思想很不错啊,思路决定方向,就算有错误也可以指出,何况我这就觉得这很有价值,至少贴子引来多个人的互相讨论.
你觉得这是菜鸟级,你可以走人不参与,别在这里拉撒,影响大家讨论的热情,方案没价值不是你可以定论的,何况你给大家留下了什么有价值的支言片语了吗?

出0入84汤圆

发表于 2008-5-31 00:26:45 | 显示全部楼层
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=1118130&bbs_page_no=1&search_mode=3&search_text=czzhouyun&bbs_id=9999
这里有键盘程序,不需要刻意的去抖什么的,效率很高,不要和我讨论什么有效不有效,做了n个项目了,都好用,最狠的用这个去检测220V的开关,同样有效,这样写有好处,什么上升下降有效都可以,改下程序就行了,好了不说了,具体好在哪,自己考虑吧

出0入0汤圆

 楼主| 发表于 2008-5-31 21:27:15 | 显示全部楼层
?

出0入0汤圆

发表于 2008-5-31 23:08:05 | 显示全部楼层
都牛得很么/////
有新得

出0入0汤圆

发表于 2008-6-1 09:58:41 | 显示全部楼层
早就知道了 ,书上写的根本不好。

出0入0汤圆

发表于 2008-6-1 22:59:10 | 显示全部楼层
收藏了,好多好东西啊

出0入4汤圆

发表于 2008-6-2 00:01:55 | 显示全部楼层
up

dn

prs

time

出0入4汤圆

发表于 2008-6-2 00:02:23 | 显示全部楼层
看看vb、vc会有收获

出0入0汤圆

发表于 2008-6-2 01:35:21 | 显示全部楼层
呵呵!终于被BS了,无所谓,谁让我先BS别人呢!

我是看不管LZ的“详解”,更看不惯LZ的开场白,要“详解”就说点有用的,整什么“未完待续”,浪费时间。

我不是什么老鸟,AVR刚入门而已,我也不会去模仿别人的东西,那样效率太低,基本都是直接拿来用。

原创!!太天真了!难怪中国发展的这么慢。

出0入0汤圆

 楼主| 发表于 2008-6-3 19:57:41 | 显示全部楼层
xingcn 星尘

我是看不管LZ的“详解”,更看不惯LZ的开场白,要“详解”就说点有用的,整什么“未完待续”,浪费时间。
====================================
LZ更看不惯你!!!!!!你的时间很宝贵吗,那还来坛子干什么?LZ又没有强迫你看

我不是什么老鸟,AVR刚入门而已,我也不会去模仿别人的东西,那样效率太低,基本都是直接拿来用。
====================================
原来你来坛子是来看别人的东西有哪些是可以被你直接拿来用,明白了。这个贴子没有代码,不能直接拿来用让你用,
让你的效率=0了,所以你不爽;你的工作主要就是 ctrl c 和 ctrl v吧,呵呵。。。

原创!!太天真了!难怪中国发展的这么慢。
====================================
原来是我让中国发展的这么慢,真会抬举人哈。。 但是LZ不买帐。如果我真能决定中国发展的快慢,LZ首先把你扔出中国
哪怕中国发展慢一点也值得。。。。呵呵

还是你厉害多用几下ctrl c 和 ctrl v,把老外的东西般过来,中国就会发展的飞快了哈

如果你有什么好的键盘解决方案,就提出来大家讨论啊,怎么尽放些臭P出来呢
不要以为装菜别人就会原谅你!象你这种LJ见多了去了

出0入0汤圆

 楼主| 发表于 2008-6-3 22:35:41 | 显示全部楼层
本来不想写上面与技术无关的哪些话,但是某些人非要和你讨论非技术问题,我也只有奉陪了.
其实搞技术累了,讨论下其他话题也不错

出0入0汤圆

发表于 2008-6-3 23:35:51 | 显示全部楼层
记号~!

出0入0汤圆

发表于 2008-6-4 00:02:01 | 显示全部楼层
观察中~~~~

出0入0汤圆

发表于 2008-6-4 10:18:13 | 显示全部楼层
在程序执行过程中,不断地查询按钮的状态改变与否。

出0入0汤圆

发表于 2008-6-10 22:55:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-6-10 23:14:22 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-6-11 17:14:23 | 显示全部楼层
都是牛人

出0入0汤圆

发表于 2008-6-12 00:21:30 | 显示全部楼层
13楼】 my_avr

三个变量运算如下:

PreKey = NowKey;

NowKey = PINx;                             //读按键电平

KeyCode = PreKey ^ NowKey ^ KeyCode;       //运算

只要有按键按下,KeyCode对应的位就为1,按键弹起后对应的位为0,根本就不需要再去进行其他判断.


KeyCode 的初值为0x00 还是0xff













//思路决定出路
unsigned char GetKeyCode(void)
{
        unsigned char PreKey=0x00,NowKey=0x00,KeyCode=0x00;
       
        DDR_KEY  &= 0x0f;
        PORT_KEY |= 0xf0;                        //PD4..7 设为输入
       
        PreKey  =  NowKey;
        NowKey  =  PIN_KEY;                        //读按键电平
        KeyCode =  PreKey ^ NowKey ^ KeyCode;        //运算
       
        //__delay_cycles(1000);       
       
        PORT_KEY &= 0x0f;                        //PD4..7 设为高阻
       
        return(KeyCode);               
}
想了10多分钟,终于想明白了 感谢    my_avr  的好思路。

最初总是把它当成循环。   总想第2次调用的情况.....       真是越陷越深啊..........



但是感觉如果把  PreKey  定义成局部变量,好象意义不大啊。

出300入0汤圆

发表于 2008-6-12 02:59:27 | 显示全部楼层
这让我明白了,原来我的程序除了等按键什么都没干!

出0入0汤圆

发表于 2008-7-31 22:41:32 | 显示全部楼层
阅下。也来个记号先。
加油!

出0入4汤圆

发表于 2008-7-31 23:14:17 | 显示全部楼层
你的手机长按3秒关机,你一直按着不放,那么它会不会关机呢?
------------------------------

我的判断是
// 检测到当前按键为开关机 发生KeyDown消息 按键状态稳定时间超出3秒
if ( (Keyboard.Code==KEY_ONOFF) && Keyboard.Down && (Keyboard.Timer>=KEY_TMR_SEC(3)) ){
    Keyboard.Down = false;    // 已经使用KeyDown消息,清除之

    // do something

}

出0入0汤圆

发表于 2008-7-31 23:28:31 | 显示全部楼层
我也说一下自己的方法。也不贴代码。
我从来不会让单片机在一个地方死循环,这是一个设计前提(当然测试外部电路的时候例外)。
我的测键用定时中断,在中断中会读取一次外部的按键(我一般用20毫秒)。同时在读取按键的模块里面有个静态变量,这个变量一直在保存上一次的状态,当这次的状态如果与上次不同,则保存这次状态并清除一个时间定时器后直接返回(消抖动)。如果状态相同则进行键盘检测,如果时间定时器是0,则表示是首次按键,我会设置一个按键标志(这个标志是为了主程序或者是其他程序模块使用的),然后会对时间定时器加一(程序中会保证这个定时器不会溢出),如果有必要则在时钟计数达到要求的数字后自动重新发送按键按下(重复按键)。在按键放开的时候也会有一个标志。当然对于不同的使用状态,我会对代码进行一些剪切,不一定全部实现这些功能。
    以前曾经用PIC12CE518利用一个按钮实现多种功能,用这个518控制了32路输出(用了串并行转换芯片)、四位数码管,用这一个按键实现显示切换、参数设置、参数保存等功能,这里面其实就利用了按键时间检测功能。

出0入0汤圆

发表于 2008-8-1 00:17:50 | 显示全部楼层
这个问题上,专门的软件人员有话说,其实,网上流行的键盘输入方法,都是一些硬件人员做出来的,说实话,对于软件,我看到的硬件开发人员基本上是外行,不是说会写代码就是一个合格的软件人员,软件的思维方式不经过长时间的思维训练,很难达到高水准,哈哈,Just Talk。

第一次接触嵌入式那会,C51,没有参考任何的程序,花了很少的时间,很好的实现了键盘功能,表扬自己一个!


==================================
【43楼】 ssyniuej
积分:1328
派别:
等级:------
来自:山东济宁
我也说一下自己的方法。也不贴代码。
我从来不会让单片机在一个地方死循环,这是一个设计前提(当然测试外部电路的时候例外)。
===================================
握手哈,Me too,包括按键,否则程序总是要停顿,总是感觉速度上怎么就让人不爽,不流畅,特别是和人交互的设备。

出0入0汤圆

发表于 2008-8-1 10:26:51 | 显示全部楼层
想了20分钟,画了2页纸,还是没想明白13楼my_avr的思路:“PreKey^NowKey”应该是检测按键变化,无变化时,PreKey^NowKey = 0x00; 但KeyCode = PreKey^NowKey^KeyCode;是做什么的?
假定初始值都为0x00;有键按下时,NowKey = 0x01,=>KeyCode = 0x01; 接着按键保持,NowKey = 0x01,KeyCode = PreKey = 0x01;
按键放开,NowKey = 0x00,KeyCode = 0x00, PreKey = 0x01(此时,等式右边的KeyCode = PreKey = 0x01)..........

也就是 KeyCode = PreKey^NowKey^KeyCode;式子右边的KeyCode总等于PreKey!
那么,式子简化后就是KeyCode = NowKey!

我理解错误了???

出0入42汤圆

发表于 2008-8-1 10:55:47 | 显示全部楼层
我补充一下:

初始化时,三个变量都为0x00.

每次按键扫描程序(程序没事的时候执行一下,随意):
void ReadKey(void)
{
     PreKey  = NowKey;
     NowKey  = PINx;
     KeyCode = PreKey^NowKey^KeyCode;          //注意:等式左边的KeyCode是本次读按键的结果
                                               //     等式右边的KeyCode是上次读按键的结果
}

                  NowKey      PreKey    KeyCode
按键无变化         0x00        0x00      0x00

按键按下           0x01        0x00      0x01

按着不放           0x01        0x01      0x01

按键放开           0x00        0x01      0x00

出0入0汤圆

发表于 2008-8-1 11:11:37 | 显示全部楼层
提问:“等式左边的KeyCode是本次读按键的结果,等式右边的KeyCode是上次读按键的结果”可以理解为 “等式左边的KeyCode = NowKey; 等式右边的KeyCode = PreKey ”吗?
那么,KeyCode存在的意义是什么?只用PreKey 和 NowKey 不就可以了吗?

出0入0汤圆

发表于 2008-8-1 11:14:50 | 显示全部楼层
感谢44楼的认可。因为在学校学习的时候都是让键盘在等待,一旦发现有键按下就等待延时(消抖),都成了一些毕业学生的标准做法了。后来发现有个工业上面用的二次表还就这么用!宏昱表,不知道是那位写的程序,真的不怎么样。里面用了个7135,这个才好,逮个7135连续取样16次,这倒没什么,不过采样的时候就忘了显示了,按键也是按照学校所教的那样下来了,所以一旦有个按键坏了,整块表就死了!!
    对于一个可靠点的设计是不允许这样的,外部设备可能会出现问题。但不能让程序对任何一个设备进行等待,否则会造成整个程序全部死掉!
    另外还有一点,一般情况下我中断的代码并不多,大部分中断其实只是完成必须要紧急完成的任务,大部分任务其实是通过一个标志传递到主程序中,由主程序来完成。

出0入42汤圆

发表于 2008-8-1 11:23:41 | 显示全部楼层
【47楼】 lxx_sea_sky
积分:154
派别:
等级:------
来自:

1、等式左边的KeyCode 不等于 NowKey,式右边的KeyCode 也不等于 PreKey !!!等式左边的KeyCode等于本次
KeyCode = PreKey^NowKey^KeyCode 运算的结果,等式右边的KeyCode是上次 KeyCode = PreKey^NowKey^KeyCode 运算的结果

2、如果没有KeyCode存在,单纯是PreKey 和 NowKey ,分析一下:

                  NowKey      PreKey    按键结果
按键无变化         0x00        0x00      0x00

按键按下           0x01        0x00      0x01

按着不放           0x01        0x01      0x00

按键放开           0x00        0x01      0x01


可以看到,在"按着不放"和按键放开"的时候按键结果就不对了

出0入0汤圆

发表于 2008-8-1 11:33:32 | 显示全部楼层
我没表达好。我的意思是“像46楼的表那样”每次运算后,NowKey的值都和KeyCode都是一样的,直接 return(NowKey)不是不也一样吗?

出0入0汤圆

发表于 2008-8-1 11:38:52 | 显示全部楼层
void keyScan(void)
{
        uchar a;   
        a=keyIn;
        a&=0x3f;
        a^=0x3f;
        if(a!=0)  // 当前有键按下
        {
                if((keyFlag&keyNullity)==0)  // 不是无效键
                {          
                        keyCount++;
                        if((keyCount&1)==0) {keyFlag|=keyHold;keyCode=a;}//去抖动  + 做有键标志
                        if(keyCount&0x80)  // long key transact 长键处理 +连续长键处理
                        {
                                keyCount-=10;
                                keyFlag|=keyLong;
                                keyTransact();  // 长按键处理
                        }               
                }
        }
        else      // 当前无键按下
        {
                if(keyFlag&keyHold) // 但上次有键按下
                {
                        if((keyFlag&(keyNullity+keyLong))==0) // 上次键不是长键和无效健
                        {
                                keyTransact(); // short key transact 短按键处理
                        }       
                }
                keyFlag=0; // 清标志
                keyCount=0;
                keyCode=0;       
        }               
}

用了很多年代码又少ram也少

出0入0汤圆

发表于 2008-8-1 11:45:19 | 显示全部楼层
mark

出0入42汤圆

发表于 2008-8-1 11:51:40 | 显示全部楼层
回【51楼】 lxx_sea_sky

如果按键有效电平有的是高,有的是低就不一样了

出0入0汤圆

发表于 2008-8-1 11:57:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-8-1 12:52:07 | 显示全部楼层
来个记号先

出0入0汤圆

发表于 2008-8-1 14:00:06 | 显示全部楼层
终于明白它的妙处了,感谢my_avr的耐心解答。
头像被屏蔽

出0入0汤圆

发表于 2008-8-1 14:09:37 | 显示全部楼层
cool !

出0入0汤圆

发表于 2008-8-1 14:15:49 | 显示全部楼层
【52楼】z4057的代码好漂亮!比用状态机还简洁明了,崇拜啊。

出0入0汤圆

发表于 2008-8-1 14:16:27 | 显示全部楼层
今天思路相当的混乱, 不过感觉是个好贴, 先MARK一下, 改天思路清晰了,再来整整!

出0入0汤圆

发表于 2008-8-1 14:26:29 | 显示全部楼层
記號,以後用得着了.再细读....

出0入0汤圆

发表于 2008-8-1 15:01:47 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-8-1 19:04:29 | 显示全部楼层
我的做法是
64矩阵按键8个口做输入(上拉),8个口做输出,有按键按下时候读到的是低电平,无按键读到的是高电平
1.IO初始化
2.读输入口,判断是否 等于FF,如果等于FF,直接退出按键扫描
3.如果不相等,说明有按键
4.延时20ms
5.循环判断一次 是哪 行按键上有按键,如果多行有按键,说明至少有两个按键按下,甚至更多。记住行号
6.把8个IO输出口,循环输出0,不用输出0的IO口,暂时转换成输入态 判断是哪列 按下的,并记住列号,当然一列上也会有多个按键的可能
7 ,如果按键是有效的 用 按键编号 = 行号 × 列数 + 列号  返回

出0入0汤圆

发表于 2008-8-1 19:18:39 | 显示全部楼层
稍微搞过一点有限状态机的都不会把这样的问题当问题吧? 太easy了。

出0入0汤圆

发表于 2008-8-2 08:21:51 | 显示全部楼层
我的思路,按下后計數寄存器加1----如果計數達30~80(根據程序大小,程序走一遍也相當於延時了,再次進入按鍵程序時就起到了防抖的作用)----清計數器.確認按鍵值和按鍵未鬆開標志,否則就跳出.
下一循環---按鍵如果未鬆開-----跳出 ,如果已鬆開----清未鬆開標志,重新計數確認按鍵.
   設計思想,要求程序不會在某段停留,進入子程序后,相關語句執行完畢馬上跳出,既使你長按按鍵,程序也不會呆在這里死循環,這樣效率才會高.
   書本上和學校介紹的都是延時消抖的方法,我試都沒試過,真搞不懂這些老師是真不懂還是假不懂, 不要誤人子弟.

出0入0汤圆

发表于 2008-8-2 11:37:15 | 显示全部楼层
//*******************************************************************
//函数名字; KeyState();
//输入参数; 无
//输出参数; 无
//功能描述; 读取按键当前状态
//建造日期; 2008年07月25日
//*******************************************************************
void KeyState(void)
{
       static unsigned char count = 0;                       //限时记数
        static unsigned char valid = 0;                       //有效标志
        static unsigned char reach = 0;                       //长按标志
         
        if (valid == True)                                    //是否有效
         {
          if (KeyDown)                                        //扫描按键
           {
          if (reach == False)                               //长按无效
          {
           if (++count > 5)                                //防误记数
           {
            reach = True;                                 //长按置位
            Respond(1);                                   //功能处理
           }
           }
          }
           else
            {                 
           valid = False;                                    //有效清零
           reach = False;                                    //长按清零
           count = 0;                                        //记数清零
          }       
          }
         
         else if (KeyDown) valid = True;                       //有效置位   
}

这是我自己写的单个按键处理函数,每10MS调用一次此函数,长按1S有效,执行长按功能,每按一次只执行一次.已用量产的产品中.

出0入0汤圆

 楼主| 发表于 2008-8-2 23:19:11 | 显示全部楼层
没想到我的一块破砖引出这么多精华哈,一个字--顶

出0入0汤圆

发表于 2008-8-2 23:32:18 | 显示全部楼层
记号,想问一下,如果按下一个按键,当时间长于3S,则按键连按,既每0.3秒加一,这样的功能怎么实现呢,不想傻等!

出0入10汤圆

发表于 2008-8-3 02:19:24 | 显示全部楼层
做记号学习

出0入0汤圆

发表于 2008-8-3 12:02:32 | 显示全部楼层
52楼的  
keyTransact();  // 长按键处理
keyTransact();  // 短按键处理
是不是写具体的按键任务啊
希望52楼的详细解释下。

出0入0汤圆

发表于 2008-8-3 12:12:52 | 显示全部楼层
68楼所说的功能,我所说过的里面已经实现了。仔细看一下里面就有这部分功能。

出0入0汤圆

发表于 2008-8-3 12:16:41 | 显示全部楼层
d

出0入0汤圆

发表于 2008-8-3 23:15:45 | 显示全部楼层
受教了。

出0入0汤圆

发表于 2008-8-4 01:57:24 | 显示全部楼层
作为一个初学者,被“按键不放程序在那死等”困扰了很久,从个贴学到很多

出0入0汤圆

发表于 2008-8-4 08:29:26 | 显示全部楼层
回70楼,是处理具体按键功能,这里如是菜单按层处理,不是菜单 按产品的功能处理

出0入0汤圆

发表于 2008-8-4 16:43:41 | 显示全部楼层
我也把我的按键程序贴上来
和 【65楼】 jeoge  、【52楼】 z4057 思路差不多,感觉用计数延时是理想的按键扫描,程序不会有任何的等待。
在以前也发过这个程序的

把这些代码放入主程序中就可以了
// 查询方式 按键处理
// 实现连续快速 加减 功能
        if (iskeydown)
        {
           if (keyhaveup)
           {// goto theend;             
             keydowncnt++;
             if (keydowncnt >= max_kdcnt)
             {  
                keydowncnt=0;
                if (iskey_config)
                    {                    
                      configcnt++;
                          if ( configcnt>=max_cfgcnt )
                          {  
                             keyhaveup=false;
                             dealkey(_key);
                          }
                    }
                    else
                    {
                      keyhaveup=false;
                      dealkey(_key);                  
                    }                  
             }
                 fastadd=false;
           }
           else if ( allowfastadd &&  (( _key == key_down ) || ( _key == key_up )) )
           {
             keydowncnt++;
                 if (keydowncnt >= max_fastaddcnt) fastadd=true;
                 if  (fastadd)
                 {
                        
                if ( (_key == key_down) && (keydowncnt>=fastdelaycnt) )
                    {  
                           keydowncnt=0;                    
                           dealkey(_key);
                    }
                    else if(keydowncnt >= fastdelaycnt)
                    {  
                           keydowncnt=0;
                       dealkey(_key);                  
                    }       
                 }                 
           }   
        }
        else
        {
          keyhaveup=true;
          fastadd=false;
          keydowncnt=0;
          configcnt =0;
        }

出0入0汤圆

发表于 2008-8-4 17:51:43 | 显示全部楼层
做个MARK

出0入0汤圆

发表于 2008-8-4 19:01:05 | 显示全部楼层
顶 学习了

出0入0汤圆

发表于 2008-8-4 22:12:07 | 显示全部楼层
我也发一个,程序简单、好移植,容易实现各种功能,0等待
****************************
key     变量,记忆按键状态,一个按键对应一个变量,具有多种状态,也就是说可以根据不同状态可以运行相应的程序
sw_0    按键按下
sw_1    按键松开
k_time  按键计时(用中断计时,如果不用中断,可以程序循环一次,k_time + = 1 )
****************************
功能:能判断短按、长按、短按LED闪一下、长按x秒后LED常亮、再按解除LED常亮
****************************
        if(key==0) LED_0;                        //指示灯灭
        
        if(key==0 && sw_0)  { k_time=0; key=1; } //空闲时按下就开始计时
        if(key==1 && sw_0 && k_time>=3) key=2;   //长按到时后(执行相应的程序后再执行key=4)
        if(key==1 && sw_1)              key=3;   //短按松开后(执行相应的程序后再执行key=0)
        if(key==4 && sw_1)              key=5;   //运行长按程序后并且按键松开时
        if(key==5 && sw_0)              key=6;   //长按程序运行完毕再按下时
        if(key==6 && sw_1)              key=0;   //解除长按进入空闲状态
        
        if(key==2)                               //长按程序
        {                                                      
          请在这里添加你的代码
          LED_1;                                 //指示灯亮
          key=4;                                 //请看上面的注释
        }
        
        if(key==3)                               //短按程序
        {                                                                           
          请在这里添加你的代码
          LED_1;                                 //指示灯亮
          key=0;
        }

出0入0汤圆

发表于 2008-8-5 00:39:18 | 显示全部楼层
具体例子

//www.avrdiy.com panxiaoyi
//程序功能:每按键一次,LED闪一下,长按3s后,LED长亮,再按,LED熄灭

#include <mega48.h>                         //时钟 1 MHz   

#define  sw_0  PIND.2==0                    //PD2
#define  sw_1  PIND.2==1  
#define  LED_0 PORTD.3=0
#define  LED_1 PORTD.3=1

unsigned char key;                          //记忆按键状态
unsigned char k_time;                       //按下后开始计时

interrupt [TIM2_OVF] void TC2_ovf_isr(void) //时间=1MHz/128/255=31ms
{
  if(k_time<255) k_time+=1;
  if(key==0) LED_0;                         //指示灯灭
  if(key==0 && sw_0) { k_time=0;  key=1; }  //空闲时按下就开始计时
  if(key==1 && sw_0 && k_time>97) key=2;    //长按3s后(执行相应的程序后再执行key=4)  
  if(key==1 && sw_1)              key=3;    //短按松开后(执行相应的程序后再执行key=0)  
  if(key==4 && sw_1)              key=5;    //运行长按程序后并且按键松开时
  if(key==5 && sw_0)              key=6;    //长按程序运行完毕再按下时
  if(key==6 && sw_1)              key=0;    //解除长按进入空闲状态
}

void main(void)
{         
  DDRD.3=1;                                 //PD3输出
  PORTD.2=1;                                //PD2上拉
  TCCR2B=5;                                 //TC2时钟128分频/默认是单斜率
  TIMSK2=1;                                 //溢出中断使能
  #asm("sei");                              //全局中断使能
  while (1)
  {   
    if(key==2)                              //长按程序
    {                                                      
      LED_1;                                //指示灯亮
      key=4;                                //请看上面的注释
    }
         
    if(key==3)                              //短按程序
    {                                                                           
      LED_1;                                //指示灯亮
      key=0;
    }
  }
}

出0入0汤圆

发表于 2008-8-5 13:11:32 | 显示全部楼层
我一般常用的按键函数:
#define  _KEY_GLOBAL_
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include"Includes.h"

/*
2003输出控制
JD1     PD5
JD2     PD7
JD3     PC0
JD4     PC1
JD5     PC2
JD6     PC6
JD7     PC7
*/
/*
按键控制输入
KEY1---KEY5   PB2   PB4---PB7
KEY6---KEY7   PD3---PD4
*/


// 0------有新的按键输入 (1:有按键按下 0: 无按键按下  )
// 1------再次进入按键非零键
// 2------
// 3------
// 4------
// 5------
// 6------
// 7------
uchar keyflag=0;
uchar keydata=0;
uchar keydatatemp=0;
/*
keytype 定义此按键是否允许按下之后再次进入
0----单次进入
1----多次进入
keyntercntflag 0---未进入 1---已经进入
*/
// 0----单次
// 1----多次
uchar keytype=0;
// 0----未进入
// 1----已进入
uchar keyntercntflag=0;
uchar keyntercnt=0;

//延长 多长时间进入按键
unit   delaytimekeycnt=0;


uchar keyvaluecnt=0;

/*
0----除湿1
1----加热1
2----除湿2
3----加热2
*/
uchar jd_sys=0;
uchar jd_sys_temp=0;
void init_JD(void)
{
    DDRD|=0x80|0x20;PORTD&=~(0x80|0x20);
    DDRC|=0x07|0xc0;PORTC&=~(0x07|0xc0);
}

void init_key(void)
{
    DDRB&=~0XF4;PORTB|=0xf4;
    DDRD&=~0x18;PORTD|=0x18;
   
    keyflag = 0;keydata=0;keydatatemp=0;delaytimekeycnt=DELAY_200MS_1;
    keytype=0X00;
    keyntercntflag=0;
    keytype&=~KEYSET;//允许单次进入       
    keytype&=~KEYUP;//允许单次进入
    keytype&=~KEYDOWN;//允许单次进入       
    keytype&=~KEYLEFT;//允许单次进入       
    keytype&=~KEYRIGHT;//允许单次进入       
    keytype|=KEYINCREASE;//允许多次进入
    keytype|=KEYREDUCE;//允许多次进入
}

#define BEEPON      PORTC|=0x80
#define BEEPOFF     PORTC&=~0x80
void dealwithout(uchar data)
{
    if((autowork&0x01)==0x00){if(data&0x01)PORTD|=0x20;else PORTD&=~0x20;}
    if((autowork&0x02)==0x00){if(data&0x02)PORTD|=0x80;else PORTD&=~0x80;}
    if((autowork&0x04)==0x00){if(data&0x04)PORTC|=0x01;else PORTC&=~0x01;}
    if((autowork&0x08)==0x00){if(data&0x08)PORTC|=0x02;else PORTC&=~0x02;}
    if(data&0x10)PORTC|=0x04;else PORTC&=~0x04;
    if(data&0x20)PORTC|=0x40;else PORTC&=~0x40;
    if(data&0x40)PORTC|=0x80;else PORTC&=~0x80;
}

void dealwithout2(uchar data)
{
    if(autowork&0x01){if(data&0x01)PORTD|=0x20;else PORTD&=~0x20;}
    if(autowork&0x02){if(data&0x02)PORTD|=0x80;else PORTD&=~0x80;}
    if(autowork&0x04){if(data&0x04)PORTC|=0x01;else PORTC&=~0x01;}
    if(autowork&0x08){if(data&0x08)PORTC|=0x02;else PORTC&=~0x02;}
}
uchar read_key(void)
{
    uchar keyrd=0;
    if(PINB&0x04) keyrd|=0x01;
    keyrd |= (PINB&0xf0)>>3;
    keyrd|=(PIND&0x18)<<2;
    return keyrd^0x7f;
}
/*
keyflag
0x01----新的按键按下
0x02----再次进入按键

按键值存储在keydata当中。
*/
uchar keyisvalue(uchar div)
{
    //允许多次进入的情况
    if((keytype&div)==div)return 1;
    else if((keytype&div)==0x00)
            {
            if((keyntercntflag&div)==0x00)
                        {
                    keyntercntflag|=div;
                    BEEPON; BEEPDELAY = DELAY_100MS;
                        return 1;
                        }
                else
                        {
                        return 0;
                        }

            }
    return 0;
}
void readkey(void)
{
        //按键发生了变动
        if(keydatatemp!=read_key())
            {
            keydatatemp= read_key();
            delaytimekeycnt =DELAY_100MS_1;
            }
        if(delaytimekeycnt==0)
            {
            delaytimekeycnt =DELAY_300MS_1;
                //如果新键值与旧键值不同
            if(keydata != keydatatemp)
                    {
                    // 按键
                keyflag&=~0x02;
                //如果新键值为非0 按键
                if(keydatatemp!=0){
                        // 0------有新的按键(非0 按键)输入跟以前的按键不一样
                             keyflag|=0x01;
                        keydata = keydatatemp;
                                }
                 //如果新键值为0按键
                        else
                                {
                             // 无按键按下
                        keyflag&=~0x01;       
                            keydata=0x00;
                        keyntercntflag =0x00;
                        keyntercnt = 0;
                            }                               
                 }
            //如果新键值与旧键值相同
                else
                    {
                //  4------再次进入按键非零键
                if(keydata!=0)         
                        {
                    if(keyntercnt<200)keyntercnt++;
                    keyflag|=0x03;
                        }
            }                                  
            }
}

void dealkeyup(void)
{

}
void dealkeydown(void)
{

}
void dealkeyleft(void)
{

}
void dealkeyright(void)
{


}


void dealkeyset(void)
{


}
void dealkeyincrease(void)
{

}
void dealkeyreduce(void)
{

}
void dealkey(void)
{
    //读取按键值
    /*
        按键值存储在keydata 当中
        按键状态存储于keyflag当中                
    */
     if((keyflag&0x01)==0x01)
     {         
        keyflag&=~0x01;
        TimeDelayCnt[4] = DELAY_1MIN;
        //uart0putc(keydata);
            /*
           uart0putc(keydata);
           可以用这个检测按键值。
            */
        TimeDelayCnt[3] = 0;
        if(keydata==KEYUP)
        {                       
            if(keyisvalue(KEYUP))
                {               
                dealkeyup();
                    }
            }
        else if(keydata==KEYDOWN)
            {
            if(keyisvalue(KEYDOWN))
                    {
                dealkeydown();
                }       
            }
        else if(keydata==KEYLEFT)
            {
            if(keyisvalue(KEYLEFT))
                    {
                dealkeyleft();
                    }
            }
        else if(keydata==KEYRIGHT)
            {
            if(keyisvalue(KEYRIGHT))
                    {
                dealkeyright();
                    }
            }
           
        else if(keydata==KEYSET)
            {
            if(keyisvalue(KEYSET))
                {               
                dealkeyset();
                    }

            }
        else if(keydata==KEYINCREASE)
            {
            if(keyisvalue(KEYINCREASE))
                    {
                dealkeyincrease();
                    }
            }
        else if(keydata==KEYREDUCE)
            {
            if(keyisvalue(KEYREDUCE))
                {               
                dealkeyreduce();
                    }
            }
     }


}

void dealwithkey(void)
{
    delaytimekeycnt =  (delaytimekeycnt<=0)?0:(delaytimekeycnt-1);
    readkey();
    dealkey();
    WDR();
}

出0入0汤圆

发表于 2008-8-5 13:15:00 | 显示全部楼层
在10ms的中断服务后台工作,
有按键时间长度,
按键响应时间,
长按,

我一般是觉得够用了。

不过函数名,和参数 取得不怎么样了,犯了大忌。

出0入0汤圆

发表于 2008-8-5 16:47:33 | 显示全部楼层
不错!

出0入0汤圆

发表于 2008-8-5 17:07:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-8-5 17:25:21 | 显示全部楼层
今天比较空闲,把【52楼】z4057的代码改成自己习惯的。试了试,很顺手。

//--Key_count--                        单位: 定时器调用间隔
#define SHORT_DELAY                2
#define LONG_DELAY                        80
#define REPEAT_DELAY                8
//--Key_flg--
#define SHORT_FLG                        _BV(0)
#define LONG_FLG                        _BV(1)
#define RELEASE_FLG                _BV(2)                        //长按和短按松开后的处理标志
#define REPEAT_FLG                        _BV(3)                        //连续按键处理标志

//公有变量
volatile uchar Key_flg = 0;         //在每次处理按键后清零
volatile uchar Key_value = 0;
//私有变量
static volatile uchar Key_count = 0;

void Scan_Key(void)
{
        uchar tmp_key;
        tmp_key = Read_Key();        //读取键值函数,视具体电路而变
        if(tmp_key != NO_KEY)
        {
                if(Key_count == SHORT_DELAY)
                {
                        Key_flg = SHORT_FLG;
                        Key_value = tmp_key;
                }
                else if(Key_count == LONG_DELAY)
                {
                        if(Key_value == tmp_key)        Key_flg = LONG_FLG;
                        else                         {Key_count = 0; Key_flg |= RELEASE_FLG;}
                }
                else if(Key_count > LONG_DELAY+REPEAT_DELAY)
                {
                        if(Key_value == tmp_key)        {Key_count = LONG_DELAY+1; Key_flg |= REPEAT_FLG;}
                        else                        {Key_count = 0; Key_flg |= RELEASE_FLG;}
                       
                }
                Key_count++;
        }
        else
        {
                Key_count = 0;
                if((Key_flg == SHORT_FLG) || (Key_flg == LONG_FLG))
                {
                        Key_flg |= RELEASE_FLG;
                }
        }
        return;
}

出0入46汤圆

发表于 2008-8-27 12:10:38 | 显示全部楼层
好贴!

出0入0汤圆

发表于 2008-8-28 20:52:00 | 显示全部楼层
加入OS后用无阻塞的OSTimeDly( )就方便多了,
凑凑热闹,贴一个UCOS下的键盘(4*4)扫描任务

///**************************键盘任务************************///较低优先级
void Key_Scan_Task(void *pdata)
{
INT8U Key,Key_Old;
pdata = pdata;
while(1)
        {       
                OSTimeDly(1);
                Key = KeyBoardGeyKey(); //读键值
                if ( ( Key == 16) || (Key != Key_Old) )      //没有键按下  KeyBoardGeyKey()返回16
                                                             //或  读到的键值与上次不一样(按键未稳定)

                        {
                                Key_Old = Key;//记录本次按键
                                continue;   //返回再次扫描
                        }
                //按键处理代码
                。。。。。。。       
                        while(1)
                                {
                                Key = KeyBoardGeyKey();                 //读键值
                                if(Key == 16) break;                        //等待键释放
                                OSTimeDly(1);
                                }
        }
}

出0入0汤圆

发表于 2008-8-28 21:29:04 | 显示全部楼层
众位高手贴了这么多代码,借鉴学习一下。

出0入0汤圆

发表于 2008-8-28 22:03:26 | 显示全部楼层
好帖子,标记一下。好好学习各家之长。

出0入0汤圆

发表于 2008-8-28 22:34:44 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-8-29 17:19:17 | 显示全部楼层
历害

出0入0汤圆

发表于 2008-8-29 18:51:37 | 显示全部楼层
标记一下

出0入0汤圆

发表于 2008-8-30 10:42:07 | 显示全部楼层
标记一下咯~~~

出0入0汤圆

发表于 2008-8-30 11:31:34 | 显示全部楼层
标记下~~~

出0入0汤圆

发表于 2008-8-30 12:30:56 | 显示全部楼层
ding

出0入0汤圆

发表于 2008-8-30 20:44:20 | 显示全部楼层
今天比较空闲,把【85楼】的代码改了一下。

//--Key_count--                        单位: 定时器调用间隔
#define SHORT_DELAY                2
#define LONG_DELAY                        80
#define REPEAT_DELAY                8
//--Key_flg--
#define SHORT_FLG                        _BV(0)
#define LONG_FLG                        _BV(1)
#define RELEASE_FLG                _BV(2)                        //长按和短按松开后的处理标志
#define REPEAT_FLG                        _BV(3)                        //连续按键处理标志

//公有变量
volatile uchar Key_flg = 0;      ////在每次处理按键后不需要清零    ,和85楼的区别
volatile uchar Key_value = 0;
//私有变量
static volatile uchar Key_count = 0;

void Scan_Key(void)
{
        uchar tmp_key;
        static  uchar iskey=0;  //增加一个变量来判断按键释放
        tmp_key = Read_Key();      
        if(tmp_key != NO_KEY)
        {
                if(Key_count == SHORT_DELAY)
                {
                        iskey=1;
                        Key_flg = SHORT_FLG;
                        Key_value = tmp_key;
                }
                else if(Key_count == LONG_DELAY)
                {
                        if(Key_value == tmp_key)        Key_flg = LONG_FLG;
                        else                         {Key_count = 0; Key_flg |= RELEASE_FLG;}
                }
                else if(Key_count > LONG_DELAY+REPEAT_DELAY)
                {
                        if(Key_value == tmp_key)        {Key_count = LONG_DELAY+1; Key_flg |= REPEAT_FLG;}
                        else                        {Key_count = 0; Key_flg |= RELEASE_FLG;}
                        
                }
               else  Key_flg=0;
                Key_count++;
        }
        else
        {
                Key_count = 0;
                 if(iskey==1)
                {
                    iskey=0;
                   Key_flg |= RELEASE_FLG;
                       
                 }
                 else  
                {
                        Key_value=0;
                        Key_flg=0;
                }
               
        }
        return;
}
  该程序能分别执行一个按键按下和释放时的程序

出0入0汤圆

发表于 2008-8-30 21:10:21 | 显示全部楼层
举个例子
define SHORT_DELAY                20000//安实际情况调整
#define LONG_DELAY                40000
#define REPEAT_DELAY           
static volatile uint Key_count = 0;               
void main()
{
      while(1)
     {
         Scan_Key();
         if((Key_value==SET)&&(Key_flg = =SHORT_FLG))
         {
             set_en();                               //SET键按下时执行,紧执行一次

          }

        if((Key_value==SET)&&(Key_flg = =RELEASE_FLG))
         {
             set_cl();                              // SET键放开时执行,紧执行一次

          }
     }
}

出0入0汤圆

发表于 2008-11-30 22:53:21 | 显示全部楼层
mark



~~~~~~~~~

出330入0汤圆

发表于 2008-12-1 11:29:04 | 显示全部楼层
思路决定出路,跟一下

出0入0汤圆

发表于 2008-12-1 14:10:29 | 显示全部楼层
marrrrrrk!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-25 23:20

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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