搜索
bottom↓
楼主: AVR-BIN

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

  [复制链接]

出0入0汤圆

发表于 2008-12-1 14:48:20 | 显示全部楼层
学习

出0入0汤圆

发表于 2008-12-1 15:11:54 | 显示全部楼层
学习

出0入0汤圆

发表于 2008-12-1 19:25:17 | 显示全部楼层
不知道在这里上传资料还有没有意义,不管了,传吧!!是《匠人手记》之一,讲键盘的!

点击此处下载 ourdev_524776.pdf(文件大小:192K) (原文件名:键盘漫谈.pdf) 

出0入0汤圆

发表于 2008-12-4 08:45:42 | 显示全部楼层
一直关注

出0入0汤圆

发表于 2008-12-4 11:43:49 | 显示全部楼层
谢谢诶分享。

出0入0汤圆

发表于 2008-12-4 16:11:41 | 显示全部楼层
赚取积分,贴上我的项目中对键盘的处理,需要识别短按键,双击,长时间按键,和非常长时间按键。

 该函数由scheduler调度,10ms执行一次。





void vCOMTOutTimerCnt(void)

{

    /* Key input level changed */

    if (u8OPMMflSymbol != u8OPMMflLastSymbol)

    {

        if(u8OPMMflSymbol)

        {

            /* key pressed, so (re-)start timer */

            u16COMTOutTimer = (U16) 0x00;

            u8COMTOutActive = 0x01;

        }

        else

        {

            /* key released => evaluate timer and send keypress info to BT */

            if(u8COMTOutActive)

            {

                u8COMTOutActive = 0x00;

                

                if( u16COMTOutTimer>= COM_TIMER_TEN_SECONDS)

                {

                    /* was a very long press */

                    u8COMMflButton |= COM_MFL_BTN_VLONG; 

                    

                    u8COMMflKeyQualified = 0x01;

                }

                else if(    (u16COMTOutTimer < COM_TIMER_TEN_SECONDS)

                         && (u16COMTOutTimer>= COM_TIMER_TWO_SECONDS)

                       )

                {

                    /* was a long press*/

                    u8COMMflButton |= COM_MFL_BTN_LONG;

                    

                    u8COMMflKeyQualified = 0x01;

                }

                else if(    (u16COMTOutTimer < COM_TIMER_FIVE_SECONDS)

                         && ( 0x01 == u8COMMflKeyPressed)

                        )

                {

                    /* already one short press seen, so make it a double press*/

                    u8COMMflButton &= ~(COM_MFL_BTN_SHORT);

                    u8COMMflButton |= COM_MFL_BTN_DOUBLE; 

                    

                    u8COMMflKeyQualified = 0x01;

                }

                else if(   (u16COMTOutTimer <= COM_TIMER_TWO_SECONDS)

                         &&( 0x00 == u8COMMflKeyPressed)

                       )

                {

                    // was a short press

                    u8COMMflButton |= COM_MFL_BTN_SHORT;

                    u8COMMflKeyPressed = 0x01;

                }

                else

                {

                    /*do nothing */

                }



            }

        }

        /* store new state*/

        u8OPMMflLastSymbol = u8OPMMflSymbol;

    }

    

    /* check if a key press a qualified */

    /* when time exceed two secend, not possible for double envent*/

    /* so release short presee event */

    if (    ( 0x01 == u8COMMflKeyQualified)

         || (    (u16COMTOutTimer>= COM_TIMER_TWO_SECONDS)

              && (u8COMMflButton & COM_MFL_BTN_SHORT)

            )

       ) 

    {

        u8COMNewCANMsg |= COM_MFL_MSG;

        u8COMTOutActive = 0x00;

             

        u8COMMflKeyQualified = 0x00;

        u8COMMflKeyPressed = 0x00;

    }            

}

本贴被 modelfly 编辑过,最后修改时间:2008-12-04,16:26:02.

出0入0汤圆

发表于 2008-12-5 23:28:53 | 显示全部楼层
新手来学习了,谢谢!

出0入0汤圆

发表于 2009-5-10 14:51:01 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-5-10 15:14:08 | 显示全部楼层
学习了,

出0入0汤圆

发表于 2009-5-10 16:37:53 | 显示全部楼层
看过

出0入0汤圆

发表于 2009-5-14 15:05:33 | 显示全部楼层
mark!

出0入0汤圆

发表于 2009-5-14 20:08:44 | 显示全部楼层
我写程序的方法和结构没有超过10uS的延时子程序:

用一个定时器每100uS中断一次产生一个100uS的标志。

main_loop:
       setb     ie.7                            ;;开启总中断,即EA
main_1ms_work:                                  ;;1ms做一次的事情
       jnb      f_1ms_work,main_10ms_work       ;;有没有1ms的标示
       acall    tim_pross                       ;;时间处理,产生其它时间标示
       acall    led_scan
       acall    toubi_test
       acall    ir_in_test
       clr      f_1ms_work                      ;;做完1ms的事情清掉标志
main_10ms_work:                                 ;;10ms做一次的事情
       jnb      f_10ms_work,main_1s_work        ;;有没有10ms的标示
       acall    key_scan_pross
       acall    key_code_pross

       acall    led_updata_pross
       acall    eeprom_write_pross
       acall    lanquan_run_pross
       acall    music_pross
       clr      f_10ms_work

main_1s_work:
       jnb      f_1s_work,main_loop
       acall    game_pross
       clr      f_1s_work
       ajmp     main_loop

出0入0汤圆

发表于 2009-5-14 20:27:18 | 显示全部楼层
顶到飞高

出675入8汤圆

发表于 2009-5-15 01:48:41 | 显示全部楼层
好贴,学习了,谢谢楼主引出

出675入8汤圆

发表于 2009-5-15 01:49:32 | 显示全部楼层
不赞成有不同的意见就打击别人,可以不发言走人啊

出0入0汤圆

发表于 2009-5-15 08:51:35 | 显示全部楼层
只用一个变量记录按下时间就可以了,不用什么标志变量

只读一个按键的:
unsigned  char  cnt_key;
if(!(PIND&(1<<0)))  //--低电平有效,PD0是否为低电平
{
  if(cnt_key<50)  cnt_key += 1;
  if(cnt_key==50)
  {
   .
   .
   .
   按键有效
   cnt_key = 55;  //--cnt_key只要大于按下按键有效值,连按时只判cnt_key是否为55
   }
}

出0入0汤圆

发表于 2009-5-15 09:43:30 | 显示全部楼层
85楼 程序最简单明了 佩服!
只是在最后 if((Key_flg == SHORT_FLG) || (Key_flg == LONG_FLG))  是不是该加个重复标志位 改成
if((Key_flg == SHORT_FLG) || (Key_flg == LONG_FLG)||(Key_flg==REPEAT_FLG))

出0入0汤圆

发表于 2009-5-15 12:30:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-15 12:45:24 | 显示全部楼层
看资源和具体情况而定,无定法!

出0入0汤圆

发表于 2009-5-15 15:14:03 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-5-15 16:29:24 | 显示全部楼层
好东西,MARK
感谢各位网友... ...

出0入0汤圆

发表于 2009-5-15 18:57:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-16 10:54:39 | 显示全部楼层
如果有键盘中断的就更简单了。比如非利普和98系列单片机,在IO口为1时设其为0产生中断,当其按下后设其为1产生中断。我有个东西就是这样做的,不要说长按,就是按几天也没问题,还可以做到低功耗,不管按不按,电流都不会超下10UA。其实AVR的外部中断也可以做。

出0入0汤圆

发表于 2009-5-17 07:23:33 | 显示全部楼层
mark.

出0入0汤圆

发表于 2009-5-18 22:15:19 | 显示全部楼层
学习学习。

出0入0汤圆

发表于 2009-5-19 10:45:46 | 显示全部楼层
学习学习

出0入0汤圆

发表于 2009-5-19 11:28:13 | 显示全部楼层
具体情况具体分析,哪有什么固定的方法.

出0入0汤圆

发表于 2009-5-22 13:03:01 | 显示全部楼层
这么好的帖子怎么能够沉下去呢。我不想发表什么,顶上去再说。

出0入0汤圆

发表于 2009-5-22 13:31:23 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-5-22 23:02:02 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-22 23:33:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-22 23:52:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-23 13:04:19 | 显示全部楼层
留个记号,以后来学习

出0入0汤圆

发表于 2009-5-23 22:19:31 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2009-5-23 22:58:41 | 显示全部楼层
代码来了。主程序采用定时器,1ms扫描一次。如果一个键按下不放1S钟后就以3次每秒的速度返回该键值,相当于快进。
原理图稍后上传,用的是8位LED和键盘共用的。

void KEYBOARD_INT(void)
{
//键盘端口初始化
_PA4=0;
DDR_PA4=1;

_PA0=1;
_PA1=1;
_PA2=1;
_PA3=1;

DDR_PA0=0;
DDR_PA1=0;
DDR_PA2=0;
DDR_PA3=0;

_PC2=1;
_PC3=1;
_PC4=1;
_PC5=1;
DDR_PC2=0;
DDR_PC3=0;
DDR_PC4=0;
DDR_PC5=0;
}




//键盘读取       
unsigned char KEYBOARD_ONE(void)
{
unsigned char temp;
KEYBOARD_INT();

asm("nop");
asm("nop");
asm("nop");
    temp=PINA&0X0F;
    temp=temp|(0XF0&((PINC&0b00111100)<<2));
    temp=~temp;
       
_PA4=1;


return(temp);
}




//键盘值处理
//DATA[0]        键盘实际返回值
//DATA[1]   键盘按住时连续返回值
//DATA[2]        键盘中间存储用
//DATA[3]   键盘延时计数
//DATA[4]   键盘按住时延时一秒用

void KEYBOARD(unsigned char  *DATA)
{
unsigned char temp;
temp=KEYBOARD_ONE();
if(temp==0)
   {
    DATA[0]=0;
        DATA[1]=0;
        DATA[2]=0;
    DATA[3]=0;
        DATA[4]=0;
        DATA[5]=0;
        }
else
  {
   if(DATA[2]!=temp)  {DATA[2]=temp; DATA[3]=0;}
   if((DATA[2]==temp)&(DATA[3]<11))      
     {
      DATA[3]++;         
       if(DATA[3]==10) {DATA[0]=temp;}
       if(DATA[3]==11) {DATA[0]=0;}     
      }  
        if((DATA[2]!=0)&(DATA[3]==11))
          {
            DATA[4]++;
                 if((DATA[4]>254)&(DATA[5]<6)){DATA[5]++;DATA[4]=0;}
                 if(DATA[5]>5)
             {
             if(DATA[0]!=0) DATA[0]=0;
             DATA[1]++;
             if(DATA[1]>150) {DATA[0]=DATA[2]; DATA[1]=0;}
                }
          }
   }
}

出0入0汤圆

发表于 2009-5-25 15:51:57 | 显示全部楼层
mark一下,有时间慢慢看

出0入0汤圆

发表于 2009-5-25 22:08:34 | 显示全部楼层
MARK
按键的学问也蛮多的!!!

出0入0汤圆

发表于 2009-5-26 12:24:09 | 显示全部楼层
先记下,有时间再看,学习了

出0入0汤圆

发表于 2009-5-26 15:33:54 | 显示全部楼层
正在做个东西,要24~25个键,不知道怎么处理好.

出0入0汤圆

发表于 2009-6-1 11:07:14 | 显示全部楼层
不错的贴,看看

出0入0汤圆

发表于 2009-6-1 15:17:11 | 显示全部楼层
呵呵~做个记号,有些和我不一样的思路呢。。

出0入0汤圆

发表于 2009-6-1 17:13:26 | 显示全部楼层
再来几张在os中应用的帖子,基本键盘方面上此帖无敌。

出0入0汤圆

发表于 2009-6-2 10:58:42 | 显示全部楼层
学习学习,谢谢分享!

出0入0汤圆

发表于 2009-6-3 14:01:28 | 显示全部楼层
自己的思路,而且一直在用,基于中断和OS的。   



开键盘中断--> 中断程序 --> YES(有效)启动键盘分析任务 --> 启动键盘事件处理任务 --> 键盘分析任务延时--> 开键盘中断并挂起
                                                                                  (相当于防抖)

出0入0汤圆

发表于 2009-7-12 19:36:31 | 显示全部楼层
很好很强大

出0入0汤圆

发表于 2009-7-13 10:41:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-13 14:06:32 | 显示全部楼层
52楼的比较简洁,一般的键盘应用够了。

出0入0汤圆

发表于 2009-7-13 19:05:17 | 显示全部楼层
好帖

出0入0汤圆

发表于 2009-7-14 10:29:59 | 显示全部楼层
好贴啊,顶上去啊

出0入0汤圆

发表于 2009-8-9 13:49:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-8-9 14:03:21 | 显示全部楼层
這樣基本的東西也要大費周章討論?

出0入0汤圆

发表于 2009-8-9 14:08:32 | 显示全部楼层

出0入0汤圆

发表于 2009-8-9 23:14:35 | 显示全部楼层
我觉得,键盘扫描可以参考PS/2键盘的做法,
PS/2键盘是:按一下立马就返回通码,在等一段时间以后,如果还是键按着的,就以一定频率连续返回键值,直道按键松开返回一个断码。
对于单片机扫描的键盘可以这样:按键按下立即开定时器,在消抖动之后立马返回键值,一段时间以后,如果还是按着键,就连续返回键值,直道按键断开,断开后关定时器。整个过程以定时器作为时间参考!

出0入0汤圆

发表于 2009-8-9 23:19:35 | 显示全部楼层
很好的东东

出0入0汤圆

发表于 2009-8-10 01:11:33 | 显示全部楼层
void keydeal()
{             if(keyflag)//keyflag==1表明先前的是高电平,才能进入到下面的处理
             {if(key==0){.........;   keyflag=0;}}//由高到低的时候就进行一些处理,这只是一个变化的时候生效,其它 时候没有效果,不会让CPU苦等
        else if(key!=0)keyflag=1;//高电平的时候把标志置一
}

出10入10汤圆

发表于 2009-8-10 16:20:13 | 显示全部楼层
本人菜鸟,只能看看各位高手解惑!留个爪印

出0入0汤圆

发表于 2009-8-10 19:18:21 | 显示全部楼层
受益匪浅,记号

出0入0汤圆

发表于 2009-8-10 19:18:41 | 显示全部楼层
好帖,顶起

出0入0汤圆

发表于 2009-8-11 10:58:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-8-14 11:12:56 | 显示全部楼层
太深了,慢慢学习

出0入0汤圆

发表于 2009-8-18 12:36:22 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-8-18 13:30:14 | 显示全部楼层
我觉得的楼主这个贴子对我们初学这来说是很不错的,
不管提出来的问题有多么菜,能把问题提出来加以讨论是值得我们学习的

我反对2楼和20楼这样的等等此类的所谓的"高手",去攻击别人,去贬低别人,说别人怎么混乱,说别人怎么混乱
这算什么呢,与其把时间浪费在攻击别人上
还不如把时间用在你的更好的方法写出来让大家共同拜读一下.

我说明一下,我跟楼主根本不认识,我只不过是看不惯论坛中总有那么一些人,老是去损人,贬低人.
我不知道他们这样做是不是来显示他们的水平很高,
这些损人,贬人的词汇对初学者来说一点帮助都没有,而是浪费了我们的阅读时间.


我发自内心的建议在论坛中能做一些有意义的讨论
也能让我们这些初学者
能多多学到点实用的东西
我们只想学习有意义的东西我们不想学习如何损人!!

出0入0汤圆

发表于 2009-8-18 17:38:11 | 显示全部楼层
好东西,先记下,回头慢慢看大家的方法,也把自己的方法分享给大家。

出0入0汤圆

发表于 2009-9-14 16:46:44 | 显示全部楼层
mark~~~

出0入0汤圆

发表于 2009-9-14 18:46:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-9-16 17:26:18 | 显示全部楼层
贴一个偶最近用的一个代码,这是用的RTOS(RTX)的代码。

主要功能是读取单击或者按着不动(按着不动,就500ms发送一次KEY_CHANGE消息)。

检测到按钮事件之后,就把按钮事件发送到消息队列里面了。

__task void KeyProcessTask (void)
{
        u8 vk_code, temp_key;
        u8 last_vk_code = 0;
        for(;;)
        {
                //wait 50ms to recheck KeyValue
                os_dly_wait(50);       
                vk_code = GetKeyValue();
                if(vk_code != last_vk_code)
                {
                        //Wait a moment to recheck
                        os_dly_wait(50);
                        temp_key = GetKeyValue();
                        if(temp_key == vk_code)
                        {
                                //Send a new Key Change Message
                                //if The KeyMailBox is Full , then TimeOut
                                os_PostMessage(KeyMailBox, MSG_KEY_CHANGE, vk_code, 0);                                   
                        }
                }
                else
                {
                        if(vk_code )
                        {       
                                //Wait a moment to resend Key
                                os_dly_wait(500);
                                os_PostMessage(KeyMailBox, MSG_KEY_CHANGE, vk_code, 0);   
                        }
                }               
        }
}

出0入0汤圆

发表于 2009-9-20 14:03:52 | 显示全部楼层
m1

出0入0汤圆

发表于 2009-9-24 01:22:18 | 显示全部楼层
谢谢

出0入0汤圆

发表于 2009-9-30 10:28:59 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-9-30 13:29:26 | 显示全部楼层
技术人员都这样,相互不服气。至少口头上是这样。
其实解决一个问题,不要去批评|打击|评论别人的方案,每个人把自己的方案写出来,各位网友自己会判断优劣的,或者根据需要选取自己所要的方案。

出0入0汤圆

发表于 2009-9-30 14:22:06 | 显示全部楼层
typedef struct
{
    BTN_CODE code;
    BTN_AGE  age;
    BTN_STATUS status;
} BUTTON;


长短按触发age可配置.... 不要浪费定时器,更不要死等去抖!

出0入0汤圆

发表于 2009-9-30 14:30:17 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-9-30 16:41:35 | 显示全部楼层
标。。。。。个记

出0入0汤圆

发表于 2009-9-30 17:01:11 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-1 17:16:11 | 显示全部楼层
非常感谢,学习了

出0入0汤圆

发表于 2009-10-7 17:15:35 | 显示全部楼层
學習了,謝謝!

出0入0汤圆

发表于 2009-10-7 17:32:31 | 显示全部楼层
不得不顶,受教了……

出0入0汤圆

发表于 2009-10-7 19:24:04 | 显示全部楼层
学习
标记

出0入0汤圆

发表于 2009-10-7 20:31:02 | 显示全部楼层
这个讨论值得收藏

出0入0汤圆

发表于 2009-10-7 21:08:51 | 显示全部楼层
关注

出0入0汤圆

发表于 2009-10-8 00:12:27 | 显示全部楼层
我用的也是7楼的做法:“键盘扫描不用傻等,只要定时地扫描一次,无论有没按下,都把键值记下。连续几次键值相等,证明按键可靠。”

之外还需要一个历史有效键值记忆。一旦得到了可靠的新键值,则与历史有效键值比较,如果相等,则返回空键值;如果不相等,则返回新键值,并更新历史有效键值记忆。

这样即使是由于按键释放得到的新键值,也照样返回该新键值。反正该新键值应该与空键值相等,在按键响应时自然会放弃。

出0入0汤圆

发表于 2009-10-8 04:28:28 | 显示全部楼层
又通看了全部帖子。支持85楼。

出0入0汤圆

发表于 2009-10-10 11:48:35 | 显示全部楼层

出0入0汤圆

发表于 2009-10-10 13:07:48 | 显示全部楼层
现在只会简单的,把它看完先

出0入0汤圆

发表于 2009-12-8 23:08:32 | 显示全部楼层
收藏

出0入0汤圆

发表于 2009-12-14 14:19:19 | 显示全部楼层
还得多读即便学习学习

出0入0汤圆

发表于 2009-12-14 15:14:18 | 显示全部楼层
刚写的一段,还没验证。 先贴上来。很耗内存。 还在想好的按键处理方法…… 真是一门大学问

static UINT8      last_button_state = 0;
static UINT8      state = 0;
static UINT8      button_state[NUMBER_OF_BUTTONS];

static UINT8 debounce(UINT8 ButtonStat,UINT8 ButtonFlagMask)
{
       
    UINT8 debounce_tmp = ButtonStat;
    UINT8 Button_save  = ButtonStat;
   
    ButtonStat >>= BIN_INDEX_RC;      // ButtonStat / (2 ^ BIN_INDEX_RC)
   
        // Debounce_tmp = (2 ^ BIN_INDEX_RC - 1) / (2 ^ BIN_INDEX_RC) * ButtonStat
    debounce_tmp -= ButtonStat;      
   
    if(ButtonFlagMask)
    {
            // Debounce_tmp += 1/ (2 ^ BIN_INDEX_RC) * now button state
        debounce_tmp += 0xFF >> BIN_INDEX_RC;  
    }

    debounce_tmp &= 0xFE;             // clear the last bit
   
    Button_save &= 0x01;
   
    debounce_tmp |= Button_save;      // keep the last bit status
   
    if(debounce_tmp >= SCHMITT_HIGH_THRESH)
    {
        debounce_tmp |= 0x01;         // The button is triggle
    }
   
    if(debounce_tmp <= SCHMITT_LOW_THRESH)
    {
       debounce_tmp &= ~0x01;        // The button is disable
    }
   
        // else keep the last state
   
    return debounce_tmp;
}


static UINT8 get_button_state(void)
{
    UINT8 bs_reading;
    UINT8 bs = 0;
   
   
    if(PW_SW_Data_ADDR & PW_SW_MASK)
        bs |= PW_SW_MASK;
     
    if(RST_SW_Data_ADDR & RST_SW_MASK)
        bs |= RST_SW_MASK;

    return bs;
}

UINT8 buttons_get_trig(void)
{
    // Read button state
    UINT8 button_stat = get_button_state();
    UINT8 bs = 0;
       
    // Debounce buttons
    button_state[0] = debounce( button_state[0], button_stat & PW_SW_MASK );
    state |= (button_state[0] & 0x01) << 0;

    button_state[1] = debounce( button_state[1], button_stat & RST_SW_MASK );
    state |= (button_state[0] & 0x01) << 1;

    // Check for button state change
    if( last_button_state != state )
    {
        bs = ((last_button_state ^ state) & state);

        last_button_state = state;
    }
       
        return bs;
}

出0入0汤圆

发表于 2009-12-14 15:18:41 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-17 17:25:26 | 显示全部楼层
获益匪浅,学学!请问一下,双击按键的程序改如何实现

出0入0汤圆

发表于 2009-12-17 17:37:20 | 显示全部楼层
MARK 按键处理

出0入0汤圆

发表于 2009-12-17 18:26:03 | 显示全部楼层
MARK,

出0入0汤圆

发表于 2009-12-17 18:49:12 | 显示全部楼层
一直都有考虑按键处理的程序思路。MARK

出0入0汤圆

发表于 2009-12-17 23:33:29 | 显示全部楼层
这个可以研究一下

出0入0汤圆

发表于 2009-12-18 03:19:05 | 显示全部楼层
mark

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

本版积分规则

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

GMT+8, 2024-5-9 09:48

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

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