搜索
bottom↓
楼主: AVR-BIN

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

  [复制链接]

出0入0汤圆

发表于 2009-12-18 09:26:09 | 显示全部楼层
看来应该去看一看maochao的按键状态机去!

出0入0汤圆

发表于 2009-12-18 10:05:08 | 显示全部楼层
转自http://bbs.ednchina.com/showtopic.aspx?id=149529
发个状态机方式扫描按键的程序,请高手指教!

结果 程序可以判断按键的长按、短按(又分为短按按下和短按抬起)
     以及按键复用功能(短按实现一个功能、长按实现一个功能)

先说一下我的思路:以什么方式读取按键都无所谓(不管是阵列式还是IO脚直接连)
最后将数据放到一个寄存器中 例如uint32 button_data_all; 32位的寄存器每一位代表一个按键,
然后用状态机的方式写一个扫描按键的程序void scan_button(struct button_module *P),
再为单片机设置一个10ms的定时中断,将扫描按键的程序放到定时中断的服务程序中,而每次进中10ms中断就好像是状态机的时钟CLK,在这个函数scan_button中会修改相应的长按、短按标志,在后面放置按键处理处理程序只需判断相应的标志即可,



文件meun.h

#ifndef __meun_H
#define __meun_H


//**************按键的结结构体************************     
struct button_module
  {
    uint8 short_flag;  //短按的标志 0xFF代表有短按按下 0x0F代表有短按抬起
    uint8 long_flag;   //长按的标志 0xFF代表有长按按下
    uint8 long_num;       //用于放置长按时间的寄存器
    uint8 state;        
    uint8 key_num;     //按键的位置要扫面的是按键寄存器中的哪个按键


  }    ;

#endif



文件meun.c
/*******************************************************
文件名:neun.c
功能  :按键扫面及处理
创建者:ideality0214

*********************************************************/

//定义了两个按键button_enter,button_cannel
//button_enter在按键寄存器中的bit1位置

struct button_module button_enter={0,0,0,0,1};    //总计数器复位
struct button_module button_cannel={0,0,0,0,2};       //总计数器开




//***********************扫描按键*************************************
//short_flag 短按键 0xFF为有短按按下标志
//                    0x0F为有短按抬起标志
//
//
//long_flag  长按键 0xFF为有长按按下标志
//
//
//******************************************************************************************
void scan_button(struct button_module *P)
{
    switch ((*P).state)
       { case 0x00:             //初始状态
             {
                 if(~button_data_all&(uint32)(1<<(*P).key_num))
                 {
                   (*P).state=0x01;   
                 }
               else
                 {
                   (*P).state=0x00;   
                 }         
                 
             }break;
         case 0x01:           
             {
                 if(~button_data_all&(uint32)(1<<(*P).key_num))    //确定有按键按下
                 {
                   (*P).state=0x02;
                   (*P).short_flag=0xFF;   
                 }
               else
                 {
                   (*P).state=0x00;   
                 }         
                 
             }break;           
            case 0x02:
                {   
                    if(~button_data_all&(uint32)(1<<(*P).key_num))
                     {
                        (*P).long_num++;
                        if((*P).long_num==200)
                          {
                              (*P).long_num=0;
                              (*P).long_flag=0xFF;
                              (*P).state=0x04;
                          }                           
                     }
                    else
                 {                  
                   (*P).long_num=0x00;
                   (*P).state=0x03;   
                 }  
              }break;         
            case 0x03:                                 //短按的松开的通道               
                {
                    
                    (*P).short_flag&=0x0F;
                    (*P).state=0x00;
                }break;                  
            case 0x04:                                //长按
                {
                   if(~button_data_all&(uint32)(1<<(*P).key_num)) //长按的持续            
                      {
                          
                      }
                   else
                      {
                        (*P).state=0x00;       //长按的松开
                      }               
                    
                    
                    
                }break;         
            default :;        
                  
                     
       }   
   
   
   
}
//******************************扫描需要按键按键********
void scan_button_all_button(void)
{

    scan_button(&button_enter);  
    scan_button(&button_cannel);   
    //可以任意添加想扫描的案件
}



//*******************按键enter定处理程序*********************************
void process_button_enter_short(void)
{
  if(button_enter.short_flag==0xFF)   //判断短按按下
    {
      
       添加相应的处理程序


      button_enter.short_flag=0;
    }

}
//*****************复用按键cannel的处理程序*********
void process_button_cannel_short(void)
{
  if(button_cannel.short_flag==0x0F)   //判断短按抬起
    {
      
       添加相应的处理程序


      button_cannel.short_flag=0;
    }

}
void process_button_cannel_long(void)
{
  if(button_cannel.long_flag==0xFF)   //判断长按按下
    {
      
       添加相应的处理程序


      button_cannel.long_flag=0;
    }

}

按需要修改if(button_cannel.long_flag==0xFF) 判断的标志即可,退出程序是一定要将标志清零


然后将这些函数添加到10ms中断的服务程序
void service_10ms(void)
{
         read_button();       //读取按键更新按键寄存器button_data_all的值
         scan_button_all_button();    //扫面需要的按键
         process_button_enter_short(); //按键enter处理程序
         process_button_cannel_short();//复用按键cannel短按抬起处理程序
         process_button_cannel_long(); //复用按键cannel长按按下处理程序
}


以上只是举了个两个按键的例子,
如需更多的按键只需定义结构struct button_module,将其加到 scan_button_all_button();函数中,编写相应的按键服务程序

出0入0汤圆

发表于 2009-12-18 11:21:55 | 显示全部楼层
自从学习了马老师按键的状态机,我才知道什么是实用的按键程序。下面是本人用于项目的按键扫描。

其中包括一个按键是单发的,二个是连_发的,连_发的时间SPEED_INC_DEC设定,而按键去抖时间由KEY_DELAY确定(实际即是在主程序的循环次数,根据实际设定)
/*4 key state in key FSM mode*/
enum
{
   KEY_STATE_0=0,  //wait trigger
   KEY_STATE_1,           //key press wait to comfirm
   KEY_STATE_2,           //delay count_1
   KEY_STATE_3           //delay count_2
  
};

//key dely 1s,Sequence key trigger time delay 500ms

#define SPEED_INC_DEC             200
#define KEY_DELAY                500       


/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/




/*******************************************************************************
* Function Name  : KEYS_Init
* Description    : Init GPIOs for joystick/button management
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void KEYS_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
   
  /* EnableGPIOBclock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

  GPIO_StructInit(&GPIO_InitStructure);
  
  /*Key GPIOs configuration*/
  /*Input pull up-mode can save Res*/
  GPIO_InitStructure.GPIO_Pin = KEY_ON_BIT|KEY_PLUS_BIT|KEY_MINUS_BIT;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(KEY_PORT, &GPIO_InitStructure);
  
  
}
  

/*******************************************************************************
* Function Name  : KEYS_Read
* Description    : Reads key from demoboard.FSM detect the key pad
* Input          : None
* Output         : None
* Return         : Return key_value
*******************************************************************************/

u8 KEYS_Read (void)
{
  static u8 key_state=        KEY_STATE_0;
  static u32 bKey_Port_current;
  static u32 bKey_Port_previous=KEY_MASK;
  u8 key_return=NOTHING;
  bKey_Port_current= KEY_MASK&GPIO_ReadInputData(KEY_PORT);
  switch(key_state)
  {
          case KEY_STATE_0:
        //key press waite comfirm
                 if((bKey_Port_current!=KEY_MASK)/*&&(bKey_Port_current==bKey_Port_previous)*/) key_state=KEY_STATE_1;
                 break;
        case KEY_STATE_1:
        //real key press,turn to delay_cout1
                 if(bKey_Port_previous==bKey_Port_current)
                        { //ONOFF not use sequence trigger mode
                         if(!(bKey_Port_current&KEY_ON_BIT)) {key_return=ONOFF;DelayTimer(200);}
                         key_state=KEY_STATE_2;
                         StartTimer(TIMERKEY,KEY_DELAY);
                        }
                else key_state=KEY_STATE_0;
                break;
           case KEY_STATE_2:
        //real key press,and no delay for 1s
                if(bKey_Port_current==KEY_MASK)
                 {        //PLUS,MINUS key 1/1,ONOFF key 1/0,iput(1:0 off:ON)/output       
                  if(!(bKey_Port_previous&KEY_PLUS_BIT))                 key_return=PLUS;
                  else if(!(bKey_Port_previous&KEY_MINUS_BIT))         key_return=MINUS;
                  key_state=KEY_STATE_0;
                 }
           else if(ReadTimer(TIMERKEY)==OK)
                        { //PLUS,MINUS key 0/2,ONOFF key 0/0
                         if(!(bKey_Port_current&KEY_ON_BIT)) break;       
                         if(!(bKey_Port_current&KEY_PLUS_BIT))                  key_return=PLUS;
                     else if(!(bKey_Port_current&KEY_MINUS_BIT)) key_return=MINUS;
                         key_state=KEY_STATE_3;
                           StartTimer(TIMERKEY,SPEED_INC_DEC);
                        }
                break;
        case KEY_STATE_3:
                                //PLUS,MINUS 0/2
                 if((bKey_Port_previous==bKey_Port_current)&&(ReadTimer(TIMERKEY)==OK))
                        {
                          if(!(bKey_Port_current&KEY_PLUS_BIT))                 key_return=PLUS;
                          else if(!(bKey_Port_current&KEY_MINUS_BIT)) key_return=MINUS;
                          StartTimer(TIMERKEY,SPEED_INC_DEC);
                        }
                else if(bKey_Port_previous!=bKey_Port_current)        //if(bKey_Port_previous!=bKey_Port_current) must used
                        {
                         key_state=KEY_STATE_0;
                         StopTimer(TIMERKEY);
                        }
                break;
  }
  bKey_Port_previous=bKey_Port_current;
  return key_return;
         
}

出0入0汤圆

发表于 2009-12-30 22:46:02 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-31 01:01:35 | 显示全部楼层
学习

出0入85汤圆

发表于 2009-12-31 13:02:23 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-31 13:58:17 | 显示全部楼层
Mark

出0入0汤圆

发表于 2010-1-1 14:52:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-1 21:58:03 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-1 23:09:48 | 显示全部楼层
#define key_input        PINC                        // 按键输入口
#define key_mask        0b00001111                // 按键输入屏蔽码
#define key_no                0
#define key_set_1        1               // 日期、时间设置
#define key_up                2               // 加一键
#define key_enter        3               // 确认键
#define key_set_2        4               // 终点日期设置
#define key_state_0        0
#define key_state_1        1
#define key_state_2        2
#define key_state_3        3

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

        key_press = key_input & key_mask;                // 读按键I/O电平
        switch (key_state)
        {
                case key_state_0:                                    // 按键初始态
                        if (key_press != key_mask) key_state = key_state_1;       
                        break;                                                // 键被按下,状态转换到键确认态
                case key_state_1:                                    // 按键确认态
                        if (key_press == (key_input & key_mask))
                        {
                                if (key_press == 0b00001110) key_return = key_set_1;
                                else if (key_press == 0b00001101) key_return = key_up;
                                else if (key_press == 0b00001011) key_return = key_enter;
                                else if (key_press == 0b00000111) key_return = key_set_2;
                               
                                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_3;
                        break;                                //按键已释放,转再次确认释放
                case key_state_3:
                        if (key_press == key_mask) key_state = key_state_0;
                        break;                                //按键已释放,转换到按键初始态
        }
           return key_return;
}

出0入0汤圆

发表于 2010-1-1 23:19:47 | 显示全部楼层
收藏,慢慢学习.谢谢各位!

出0入0汤圆

发表于 2010-1-4 00:31:09 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-3-9 08:42:43 | 显示全部楼层
什么mark和学习之类没什么价值的评论能不能不要写上去啊。浪费时间。

出0入0汤圆

发表于 2010-4-29 08:47:51 | 显示全部楼层
最近我也做了个4*4的按键扫描程序,思路是这样的。每次按键扫描都是返回两个值,一个是按键值还有一个按键状态,即UP,DOWN两个值,这样主程序调用就可以判断

出0入0汤圆

发表于 2010-4-29 09:25:52 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-29 09:44:11 | 显示全部楼层
回复【楼主位】AVR-BIN LED照明/灯饰
-----------------------------------------------------------------------

精妙,非常感谢分享,完全是全新的思路,值得置酷!尊重原创。
  btw:为什么技术人员老是互相贬低呢?如果别人是高手,那么就学习一下;别人是菜鸟,就指导下。技术人员的特点就是严谨,一味的贬低别人并不能显出自己的水平高,相反,只能体现自己的浮躁和层次低。

出0入0汤圆

发表于 2010-4-29 09:54:05 | 显示全部楼层
学习了

出0入0汤圆

发表于 2010-4-29 13:08:08 | 显示全部楼层
mark   学习了

出0入0汤圆

发表于 2010-4-29 13:55:49 | 显示全部楼层
m

出0入0汤圆

发表于 2010-4-29 14:23:22 | 显示全部楼层
我自己做的模板,识别 4个,4*4个的按键,自己定义了两个事件,onKeyDown,onKeyUp,

判断的方法就是取前一个键值与现在的键值相比。当然,放掉的话前一个键值就是空了。

这个百试,又好用又方便 。

出0入0汤圆

发表于 2010-4-29 14:57:00 | 显示全部楼层
收藏一下

出425入0汤圆

发表于 2010-4-29 16:51:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-29 16:54:57 | 显示全部楼层
好东西,正在学习其中精华。

出0入0汤圆

发表于 2010-4-29 22:43:47 | 显示全部楼层
新手报到,又学到一番知识,下次实现以下.

出0入0汤圆

发表于 2010-5-19 19:15:36 | 显示全部楼层
好贴,学习下

出0入0汤圆

发表于 2010-5-19 23:23:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-20 20:32:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-21 22:20:48 | 显示全部楼层
还好,第一次老师教的时候,用的是相当完善的方法;当时还觉得大材小用。

出0入0汤圆

发表于 2010-5-22 09:49:00 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-22 10:32:45 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-22 10:52:00 | 显示全部楼层
按键mark

出0入0汤圆

发表于 2010-5-24 11:47:41 | 显示全部楼层
顶!

出0入0汤圆

发表于 2010-5-24 11:57:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-24 16:59:22 | 显示全部楼层
顶 学习了 晚上接着看

出0入0汤圆

发表于 2010-6-1 16:42:45 | 显示全部楼层
慢慢看。

出0入0汤圆

发表于 2010-6-18 09:29:18 | 显示全部楼层
收藏此帖,慢慢消化!

出0入0汤圆

发表于 2010-6-18 11:39:46 | 显示全部楼层
我也说说我对按键处理吧...

我的按键程序大概有三部分组成:
按键扫描, 按键状态分析, 按键功能处理

我把按键分为三种处理:
1.每按一次都起作用的按键(按着不放,我也不理会它,但是也不会造成程序的死等)
2.长按有效的按键(就是按着超过了2~3秒才有效的按键,就像手机的关机/开机按键,同样继续按着不放,我也不理会它,但是也不会造成程序的死等)
3.长按连续有效的按键(就是按着超过了0.5~1秒,这个按键就连续起作用,相当于用比较快的速度去按这个按键,就像电视遥控器上的音量按键)

按键的扫描只是为了得到按键码...得到正确的按键码方法比较简单...其实使用第一种扫描的方法,我觉得也没有什么不妥的地方,关键是,你怎么去调用这个扫描的程序,加入去抖动 和避免程序的死等(只有小1MS的延时我会用死循环去做,其它的定时,延时,都是通过系统时钟得到的,所以程序觉得不会有死等的东西出现).

对整个按键的处理程序,我的重点基本上是放在按键状态的分析.按键扫描程序是定时的去得到一个按键号,而得到的这个按键号,也不是每次扫描得到了就发出来的...主要是参考红外线遥控器,100~150MS才把这个码发出来,给按键状态分析程序用.(同样的方法,对得到的遥控码或者是其它按键芯片给过来的按键号,也是这样处理的.)

按键状态分析程序 是对得到的按键号进行分析,然后设立两个旗标:新的按键码旗标,相同的按键码旗标,
如果得到的是新按键码,而且按按键是允许短按一次就可以起作用的,(这个可以根据实际需要考虑是否等到按键释放了才起作用的.)转化成按键功能处理程序需要的格式的按键功能码.

如果得到的是相同的按键码,就要区别:
这个按键是不是要常按才起作用的,如果是的,设立一个定时变量(注意这个定时变量不是死等的,并且如果得到的按键码不同了,这个变量也就会失效了),只要时间够了,就可以给出 按键功能码了,并且同时给一个常按的按键旗标...

如果按键是连续按着的也起作用的话(就像一些参数调整的ADD+,DEC-按键,按着不放就连续加减),这时用到两个定时变量,一个是判断按键按下是否超过了连续增减的时间,一般我是做500MS~1S,另一个是,过了这个时间,就按一定的时间(另一个变量,一般我是设在50MS~200MS间,就看对参数调节要求的快慢了.)把 按键功能码 发给按键功能处理程序.并且同时给出一个连续按键的旗标.

接下来就是:按键功能码处理程序了...这个就不多说了...该干什么就干什么去....

另外:我比较反感:有些人自己能悟到了一些东西,想明白了一些事情,就认为很了不起,去鄙视和辱骂其它一时没有把事情弄明白的人.

出0入0汤圆

发表于 2010-6-18 11:57:50 | 显示全部楼层
另外说到这个思路,不妨出个题,看看大伙怎么去处理的:
用1个I/O口把数据发送给164(74HC16)移位寄存器,
思路是:用软件把CLK调制在DATA里面,用一根数据线(当然要和地线形成回路),然后接收那边再解调出CLK和DATA两个信号...接收那边的解调电路非常简单仅三两个普通容阻器件就够了...

呵呵...现在的单片机I/O够多的,可是在10年前...呵呵...并且有成本的压力下,只能用这样的方法去做了...
你可以把这个东西当作不可能或者是I/Q题...但是实际上这个东西在量产的产品上面使用过...并且没有因为这个东西引发的问题...

记得N多年前有位老工程师说的一句话:技术佬,技术佬,告诉你方法就是一句话,不告诉你,你就一辈子都想不明白...
解决问题的方法很多,但是并不一定人家的就是最笨的,自己的是最好的.

出0入0汤圆

发表于 2010-6-27 15:02:54 | 显示全部楼层
百家争鸣!

出0入0汤圆

发表于 2010-7-1 10:03:51 | 显示全部楼层
谢谢楼主!受益非浅

现按楼主的思路写以下的代码(已测试过),请大家多指教.

uint8_t KeyVirtualValue=0;   //键有效
uint8_t KeyState=0;    //键前状态

void KeyProcessorCode(void)
{
        if(KeyEnable)   //按键高电平
        {
                if(KeyVirtualValue)    //键盘曾经按下后变成高电平,键盘已释放,复位键前状态=0---》退出
                {
                        KeyState=0;
                }
                else return;    //未按下---》退出
        }
        else
        {
                if(KeyState)    //键盘曾经按下,现在仍然是低电平--》退出
                {
                        return;
                }
                else    //键盘由高电平变成低电平,置键有效=1,键前状态=1;---》退出
                {
                        KeyVirtualValue=1;
                        KeyState=1;
                }
        }
       
        if (KeyVirtualValue && KeyState)
        {
                  ............
                //加入按键处理代码
        }
       
}

出0入0汤圆

发表于 2010-7-1 10:20:19 | 显示全部楼层
还是用状态机比较好

出0入0汤圆

发表于 2010-7-4 19:05:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-5 08:34:47 | 显示全部楼层
标记下

出0入0汤圆

发表于 2010-7-5 16:51:52 | 显示全部楼层
上一个用汇编写的3个按键的扫描程序,很精干,如果把它分析透,键盘扫描就不用愁了。
用到一个定时一处中断timer2_OVF,每隔200ms定时扫描键盘口PINB3,PINB4,PINB5。键按下为高电平,抬起为低电平。用了一个标志位flag中的key_flag,表示按键是否处理。klast为上次扫描值,kfirst为初次扫描值。key1:,key2:,key3:分别为三个按键的处理程序。

TIM2_OVF:                                push        temp1
                                push        temp       
                                in        temp,sreg
                                push        temp
                                sts        tcnt2,freq;定时200ms
                                rcall        key_scan
TIM2_OVF2:
                                pop        temp
                                out        sreg,temp
                                pop        temp
                                pop        temp1
                                reti
       
key_scan:                                  in        temp,pinb
                                andi        temp,0x38
                                cp        temp,klast
                                brne        key_s1
                                sbrs        flag,key_flag
                                rjmp        key_s2
                                cpi                temp,0x00;kfirst
                                breq        key_s3
                                rcall        key_pres
key_s3:                                 cbr        flag,(1<<key_flag)
key_s2:                                 mov        klast,temp
                                rjmp        key_s4
key_s1:                                 sbrc        flag,key_flag
                                rjmp        key_s2
                                mov        kfirst,klast
                                sbr        flag,(1<<key_flag)
                                rjmp        key_s2
key_s4:                                 ret

key_pres:                                  push        temp
                                push        temp1
                                eor        temp,kfirst
                                and        temp,klast
                                andi        temp,0x38
                                lsl        temp
                                lsl        temp
                                lsl        temp
                                brcs        key1
                                lsl        temp
                                brcs        key2
                                lsl        temp
                                brcs        key3
key_pres1:                                
                                pop        temp1
                                pop        temp
                                ret

key1:                                 ldi        temp,(1<<led1)
                                in        temp1,portc
                                eor        temp1,temp
                                out        portc,temp1
                                rjmp        key_pres1
key2:                                 ldi        temp,(1<<led2)
                                in        temp1,portc
                                eor        temp1,temp
                                out        portc,temp1
                                rjmp        key_pres1
key3:                                 ldi        temp,(1<<led3)
                                in        temp1,portc
                                eor        temp1,temp
                                out        portc,temp1
                                rjmp        key_pres1

出0入0汤圆

发表于 2010-7-6 18:47:08 | 显示全部楼层
不错

出0入0汤圆

发表于 2010-7-6 19:32:04 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-7 13:22:26 | 显示全部楼层
应该具体问题具体对待,如果做DSP+FPGA的嵌入式系统,LZ的两种方法处理起来都复杂的很!!

出0入0汤圆

发表于 2010-7-7 22:02:15 | 显示全部楼层
思路决定出路--键盘扫描详解

出0入0汤圆

发表于 2010-7-8 19:29:47 | 显示全部楼层
思路决定出路--键盘扫描详解

出0入0汤圆

发表于 2010-7-25 17:43:01 | 显示全部楼层
思路决定出路

出0入0汤圆

发表于 2010-7-25 19:11:11 | 显示全部楼层
make

出0入0汤圆

发表于 2010-7-25 19:58:11 | 显示全部楼层
有些东西一经过交流就变得很简单了,谢谢各位高手

出0入9汤圆

发表于 2010-7-25 21:33:55 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-18 13:18:12 | 显示全部楼层
的确是好贴,以前一直以为这个没什么搞头了,现在要搞个长按,短按,组合,连续的键盘处理,想了半天愣是没有很好解决,只要来坛子翻坛了,受益良多……

出0入0汤圆

发表于 2010-8-21 15:34:50 | 显示全部楼层
我也来献一下丑!! 前一些时间琢磨了好几个晚上才折腾出来的.扔砖头的记得捡小的扔! 欢迎大家指正.

以下为代码

//独立按键子程序
//控制器:ATmega16   主时钟频率:12MHz
//子程序功能: 检测4个独立按键是否被近下,并返回相应的键值


#define key_p   PORTD
#define key_d   DDRD
#define key_in  PIND
//#define

#define key_time_bit  2                       //定时器溢出时,置位该位

#define down_key 0
#define up_key   1
#define pro_key  2
#define idle     3                            //键盘状态值
#define key1     BIT(2)
#define key2     BIT(3)
#define key3     BIT(6)
#define key4     BIT(7)

//变量定义
uchar status_value      = idle;
uchar key_buffer1       = 0;
uchar key_value_temp    = 0;
uchar key_300ms         = 0;
uchar key_3s            = 0;

//端口初始化
void key_port_init()
{
     key_p |= 0xcc;                           //设置PD口 为输入,使能内部上拉电阻
     key_d &= 0x33;                           //设置PD口 键盘接口为输入  0b00110011
}

//定时/延时函数
uchar key_delay(uchar *ms_count)
{
     static uchar time_c = 0;
     if(*ms_count & BIT(key_time_bit))       //延时位是否为1 即延时计时到
     {
        *ms_count &= ~BIT(key_time_bit);     //延时位置0
        if(time_c >= 20)
        {
           time_c = 0;                       //计时20毫秒后归零,并返回延时标志1
           return 1;
        }
        else
           time_c ++;                        //1毫秒累计
     }
     return 0;                               //返回标志 0, 延时没到。
}

//键盘按键值处理
uchar key_value_process()
{
     if(!(key_buffer1 & key1))
        return 0xa1;
     else if(!(key_buffer1 & key2))
        return 0xa2;
     else if(!(key_buffer1 & key3))
        return 0xa3;
     else if(!(key_buffer1 & key4))
        return 0xa4;
     else
        return 0xa0;
}

//键盘状态处理进程
//  传入参数说明: *key_temp 键值保存指针,*ptr_count:延时定时标志指针。
//
void keybord_status(uchar *key_temp,uchar *ptr_count)
{
//     static uchar status_value = idle;
//     static uchar key_300ms         = 0;
//     static uchar key_3s            = 0;
     switch(status_value)
     {
           case idle :
                key_buffer1 = key_in & 0xcc;
                if(key_buffer1 != 0xcc)                 //键盘动作检测(是否有按键被按下)
                    status_value = down_key;            //如果有按键被按下,则跳转状态
                            else
                                 *key_temp = 0x00;                   //无按键按下,或按键已经释放
                break;
           case up_key :
                *key_temp = key_value_process();        //输出按键值
                status_value = idle;                    //跳转状态到空闲状态
                break;
           case down_key :
              if(key_delay(ptr_count))                  //延时等待
               {
                   if((key_in & 0xcc) == key_buffer1)   //按键是否还保持按下状态
                       status_value = pro_key;
                   else
                       status_value = idle;             //转到空闲状态
               }
                break;
           case pro_key :
                if((key_in & 0xcc) == 0xcc)             //所有按键均被释放
                {
                  key_300ms = 0;
                  key_3s    = 0;                        //自动键值输出计时清零
                  status_value = up_key;                //状态跳转
                }
               else
                {
                   *key_temp = 0x00;                    //清除缓存中的键值
             if(key_delay(ptr_count))             //1ms 延时查询
                   {
                         if(key_300ms >= 10)               
                   //约300ms  表达式为真(注:偏离程序设计意图,按执行代码分析应该是 10 ms 表达式为真)
                   {
                     key_300ms = 0;
                     if(key_3s >= 15)               // 持续 3 秒 表达式为真
                       *key_temp = key_value_process();
                     else
                        key_3s ++;
                    }
                    else
                           key_300ms ++;
                    }
                }
                break;
           default :
                status_value = idle;                    //无效状态转换为空闲状态
                break;
     }
}

出0入0汤圆

发表于 2010-8-21 17:25:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-21 23:38:16 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-8-22 00:04:11 | 显示全部楼层
好贴, 做个记号.

出0入0汤圆

发表于 2010-8-22 17:24:16 | 显示全部楼层
留个记号慢慢看

出0入0汤圆

发表于 2010-8-22 19:40:44 | 显示全部楼层
好贴子,喜欢这样的讨论

出0入0汤圆

发表于 2010-8-22 19:44:07 | 显示全部楼层
顶235楼,说的我能看的很明白

出0入0汤圆

发表于 2010-8-28 12:25:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-28 13:23:44 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-8-28 17:22:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-28 18:15:05 | 显示全部楼层
都不错

出0入0汤圆

发表于 2010-8-28 18:15:22 | 显示全部楼层
都不错

出0入0汤圆

发表于 2010-8-31 15:15:16 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-9-5 22:40:24 | 显示全部楼层
mark,太精彩了

出0入0汤圆

发表于 2010-9-5 22:40:52 | 显示全部楼层
mark,太精彩了

出0入0汤圆

发表于 2010-9-5 23:23:05 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-9-6 10:35:30 | 显示全部楼层
期待继续讨论

出0入0汤圆

发表于 2010-9-26 19:47:58 | 显示全部楼层
标记学习.

出0入0汤圆

发表于 2010-9-27 11:19:35 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-9-27 13:09:13 | 显示全部楼层
多探讨,多研究。。。。学习

出0入0汤圆

发表于 2010-9-27 17:45:44 | 显示全部楼层
mark 键盘扫描思路

出0入0汤圆

发表于 2010-9-27 23:43:10 | 显示全部楼层
都是强人,小弟佩服!

出0入0汤圆

发表于 2010-9-28 10:57:01 | 显示全部楼层
我说处理案件问题,不适合小题大作,但是无论怎么做,都要做到符合使用习惯就好。去抖怎么去,只要不影响使用,也未尝不可,因为人对操作反应的速度并没有太多的要求。倒是键盘处理的程序如何安置在程序中是一个重要问题。我比较反对那种独占性的程序模块。

出0入0汤圆

发表于 2010-10-8 16:16:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-31 01:08:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-11-1 18:57:45 | 显示全部楼层
好讨论,mark

出0入0汤圆

发表于 2010-11-1 21:24:26 | 显示全部楼层
为什么23楼czzhouyun提供的链接打不开呢?
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=1118130&bbs_page_no=1&search_mode=3&search_text=czzhouyun&bbs_id=9999
很感兴趣 想看看

出0入0汤圆

发表于 2010-11-1 22:43:32 | 显示全部楼层
这人帖子应该收藏-----按键处理。

出0入0汤圆

发表于 2010-11-2 00:00:04 | 显示全部楼层
mark,太精彩了

出0入0汤圆

发表于 2010-11-22 11:28:11 | 显示全部楼层
真精彩。长了许多的知识。谢谢各位

出0入0汤圆

发表于 2010-12-28 09:09:59 | 显示全部楼层
Mark

出0入0汤圆

发表于 2011-2-3 13:25:01 | 显示全部楼层
mmm

出0入0汤圆

发表于 2011-2-3 14:13:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-3 16:35:00 | 显示全部楼层
不要防抖,防连按就是.设一个Delay,按下了如果Delay为0就发送按下的键,如果不为0就忽略.While中根据时钟Delay--.
硬件连接就是直接P1 P2连接成矩阵.不要电阻,不要接地,不要接Vcc...这样程序简单,越简单越可靠.
程序很容易扩展成无数按键或是其他属于设备.
想要判断抬起 在    if ((P1!=0xff))  加个 else 就是....我的应用不需要就没写....
已应用在多个项目中,稳定可靠.
#include <REG52.H>                                      

char KEYBOARD_ArrayX[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
char KEYBOARD_ArrayY[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};

int GLOBAL_COUNT=0;

void UART_Timer1Init(void)
{
     SCON  = 0x50;                       
     TMOD |= 0x20;              
     TMOD |= 0x01;
     TH1   = 0xff;                            //Crystal : 22.1184 MHz , 6 clk ,115200 bps
     TL1   =0xff;
     IE   |=0x90;
     TR1   = 1;                           
     TI    = 1;
}
                 
void UART_Send(int x,int y)
{
    SBUF=(char)(x*10+y);
    while(TI==0);
    TI=0;
}

void KEYBOARD_Send(int x,int y)
{
    if ( GLOBAL_COUNT==0)
    {
       UART_Send(x,y);
       GLOBAL_COUNT=5000;
     }
}

void KEYBOARD_Scan(void)
{
    int i,j;  
    P2=0x00;
    if ((P1!=0xff))
       for(j=0;j<8;j++)
          {
              P2=KEYBOARD_ArrayY[j];                                                                                       
              for(i=0;i<8;i++) if (!(P1&KEYBOARD_ArrayX)) KEYBOARD_Send(i,j);       
          }
}
          
void main (void)
{
    UART_Timer1Init();
    while(1)
    {
        KEYBOARD_Scan();
        if (GLOBAL_COUNT!=0) GLOBAL_COUNT--;
   }       
}

出0入0汤圆

发表于 2011-2-5 12:31:36 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-7 12:05:11 | 显示全部楼层
搞开发重要的是思想,而不是代码,代码只是工具 ,mark

出0入0汤圆

发表于 2011-2-7 13:51:03 | 显示全部楼层
“回”字有四种不同的写法。

出0入0汤圆

发表于 2011-2-7 14:29:05 | 显示全部楼层
cool

出0入0汤圆

发表于 2011-2-7 23:20:21 | 显示全部楼层
好贴!顶

出0入0汤圆

发表于 2011-3-19 13:35:08 | 显示全部楼层
MARK 学习了!!

出0入0汤圆

发表于 2011-3-19 16:25:11 | 显示全部楼层
偶是个新手,碰到一个问题,希望各位高手不要见笑,就是移位寄存器(595d)输出口与单片机2个I/O口接在10个按键上,其中移位寄存器的输出口分别接8个电阻,再接到按键上,2个I/O接二极管(IN4148),再接按键(2个I/O口分为2组,二极管的流向是I/O口到二极管),这样在移位寄存器输出为零的情况下,按键按下在I/O口是读不到低电平(读到的是2.45V),所以用一般的扫描法都不能解决这个问题,请问高手有好的设计思路没有?谢谢。

出0入0汤圆

发表于 2011-3-19 16:38:06 | 显示全部楼层
<a href=./bbs_upload/files_37/ourdev_623835ZZERXE.rar>ourdev_623835ZZERXE.rar

出0入0汤圆

发表于 2011-3-19 16:50:12 | 显示全部楼层
mark~ 3ks 4 the good idea~

出0入0汤圆

发表于 2011-3-20 19:14:02 | 显示全部楼层
mark and thanks

出0入0汤圆

发表于 2011-3-20 19:29:20 | 显示全部楼层
没必要搞得如此繁杂.一直是这样处理,很稳定,很实用.光用防抖延时实在是浪费时间.

if(!KEY_A)
{
    i=250;
    do
    {
        if(KEY_A)
            break;
    }while(--i);
    if(i==0)
    {
        if(!KEY_A_FLAG)
        {
            KEY_A_FLAG=1;
            //键盘处理
        }
        else
            KEY_A_FLAG=0;
    }
    else
        KEY_A_FLAG=0;
}
else
    KEY_A_FLAG=0;

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-5-8 15:29

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

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