单片机基础学-按键篇

这篇博客介绍了作者在学习单片机过程中对按键检测的总结,使用状态转移思想和定时器实现独立按键的单击、连击、长击检测。通过定义数据结构KeyData和状态机,实现了按键的各种功能,并提供了程序代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实我现在也是一个菜鸟,但是这是我学习的过程中的总结,我想写出来,可能对初学者有用,在此献丑了。

按键总结篇

                                                 作者:HouZuping

在所有智能产品中,按键是最为常用的,所以按键程序的好坏很重要。以前我们在学校里学的按键检测方法都是不适用,很浪费时间,减少了CPU的效率。在大家的不断努力下,基本上编程都是用状态转移思想。用状态机思想编的按键检测程序也很多,像一些高手就写过很好的按键检测程序,下面我总结了用一个定时器的独立按键检测,能检测单击,连击,长击等。这个按键检测程序中包含了状态转移思想,时间片段思想,对象编程。

关于按键流程图我就不说了,这个大家都清楚。首先对按键进行一个数据结构定义。

struct KeyData

{

       unsigned char KeyValue;                     //按键返回值

       unsigned char LongClickFlag;          //长击标志

       unsigned char RunClickFlag;        //连击标志

       unsigned char JudgeRunFlag;           //软定时器的连击标志

       unsigned int  RunCount;                 //连击间隔时间计数

};

 

//按键常量的定义 ,根据具体的时钟来跟改

#define  KeyLongTime_2s       100  //    1/Fcy*timerValue*1000

#define  KeyRunClick_500Ms    40   //连击间隔时间大约是500ms左右

 

这个数据结构是把按键当成一个对象,这样每次按键按下后都会有一个返回值,其他标志都是按键需要其他功能功能。不管有几个独立按键,每个按键返回值都不同。需要几个按键时,就定义几个按键关键字。

//------------接口区

#define KeyOne   P1_0

#define KeyTwo   P1_1

 

//----------初始状态

#define IdleState   1

 

这里我只用了两个按键,其实本按键最为不好的地方就是按键检测和读取按键的值,每次移植时都需要修改,这里还需要修改,在读取按键值时,我可以采用关键字固定,是来确定按键返回值固定这种方式,但是我觉得没有必要,移植时就改吧,有的单片机直接支持位操作,有的不支持。

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

//键盘扫描接口部分,移植时需要修改

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

unsigned char KeySwap()

{

       if((KeyOne!=IdleState)||(KeyTwo!=IdleState))//按键数目判断状态,这里是位变量

       return 1;

       else

       return 0;

}

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

//读取接口值函数,移植时需要修改

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

unsigned char RdKeyValue()

{

       if(KeyOne!=IdleState)

       return 0x02;

       if(KeyTwo!=IdleState)

       return 0x03;

       else

       return 0;

}

 

下面来看看我主要的按键检测部分,也是最重要的部分,我在定时器内定义每隔12ms就去扫描一次按键,这样每个状态间隔的时间就是12ms,我就是利用这个时间来去抖动,我也是利用这个时间来进行连击和长击的判断。不说了,看程序吧

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

基于软定时器的通用模块的建立之一:独立按键通用模块

创建人  :侯祖平

创建日期:2010.7.01

博客网址:

欢迎大家到我的博客交流..........

*********************************************************************

 

       #include "EvenKey.h"

       #include "KeyBasic.h"

 

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

//状态变量

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

enum 

{

       KeyIdle = 0,

       KeyDownDelay,            //判断按键按下

       KeyWait,            //按下等待每隔一个软定时器,变量就加一

       KeyFunction,         //长击和短击判断

       KeyLongClick,              //若按下时间超出,则为长按

       KeyShortClick,             //

       KeyRunClick,        //连击,可以用一个变量来记录连击的次数

       KeyUp,                        //按键抬起

       KeyUpDelay,             //长击按下延时

       KeyOver

};

 

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

//键值扫描循环

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

void KeyLoop(struct KeyData *KeyNumber)                           

{

       static unsigned char KeyCurValue = 0 , KeyLastValue = 0;

       static unsigned char KeyState = 0;

       static unsigned int  LongCount = 0;

       switch(KeyState)

       {

              case KeyIdle :

                                   if(KeySwap())

                                   {

                                          KeyState = KeyDownDelay;

                                   }

                                   else

                                   {

                                          KeyState = KeyIdle;

                                   }

                                   break;

 

              case KeyDownDelay :

                                   if(KeySwap())

                                   {

                                          KeyState = KeyWait;

                                   }

                                   else

                                   {

                                          KeyState = KeyIdle;

                                   }

                                   break;

 

              case KeyWait :                                                        //有键按下,看是否是连击

                                   KeyCurValue = RdKeyValue();

                                   if((KeyCurValue==KeyLastValue)&&(KeyNumber->JudgeRunFlag))

                                   {                                                             //

                                      if(KeyNumber->RunCount < KeyRunClick_500Ms)

                                          {                                                      //则为连击

                                                 KeyNumber->RunCount = 0;

                                                 KeyNumber->JudgeRunFlag = 0;

                                                 KeyState = KeyRunClick;            //跳转到连击状态

                                          }

                                          else

                                          {

                                                 KeyNumber->RunClickFlag = 0;

                                                 KeyNumber->RunCount = 0;

                                                 KeyNumber->JudgeRunFlag = 0;

                                                 KeyState = KeyFunction;             //若不是继续判断

                                          }

                                   }

                                   else

                                   {

                                          KeyState = KeyFunction;     

                                   }

 

                                   break;

 

              case KeyFunction :

                                   if(KeySwap())

                                   {      

                                           KeyCurValue = RdKeyValue();

                                           LongCount ++;

                                           if(LongCount >= KeyLongTime_2s)

                                           {

                                                LongCount = 0;

                                                KeyState = KeyLongClick;   

                                           }

                                   }

                                   else

                                   {

                                          if(LongCount < KeyLongTime_2s)

                                           {

                                                LongCount = 0;

                                                KeyState = KeyShortClick;  

                                           }

                                   }

                    

                                   break;

 

              case KeyLongClick :                                       //长击

                                   #ifdef  HaveLongKey

                                   KeyNumber->LongClickFlag = 1;

                                   KeyNumber->KeyValue = KeyCurValue|0x80;

                                   #endif

                                   if(!KeySwap())                           //若按键抬起 ,则状态跳转

                                   KeyState = KeyUpDelay;

             

                                   break;

 

 

              case KeyRunClick :                                          //连击

                                   #ifdef  HaveRunKey

                                   KeyNumber->RunClickFlag = 1;

                                   KeyNumber->KeyValue = KeyCurValue|0x40;

                                   #endif

                                   if(!KeySwap())                           //若按键抬起 ,则状态跳转

                                   KeyState = KeyUpDelay;

             

                                   break;

 

              case KeyShortClick :                               //短击和连击的判断

                                   KeyNumber->JudgeRunFlag = 1; //连击判断启动标志位

                                   KeyNumber->KeyValue = KeyCurValue;

                                   KeyState = KeyUp;

                                                       

                                   break;

 

              case KeyUp :

                                   KeyNumber->RunClickFlag  = 0;

                                   KeyNumber->LongClickFlag = 0;

                                   KeyState = KeyOver;

                                   break;

 

             

              case KeyUpDelay:

                                   if(!KeySwap())                           //若按键抬起 ,则状态跳转

                                   KeyState = KeyOver;

 

                                   break;

 

              case KeyOver :                                               //结束

                                   KeyLastValue = KeyCurValue;     

                                   KeyState = KeyIdle;

                                   break;

                    

       }    

}

 

 

 

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

//end of file

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

其中连击的判断也是我写这个按键检测的真正目的,以前我都是用两个定时器来判断连击的,我在以前的程序添加了一个程序。

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

//参数传递函数,用于传递连击的时间间隔

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

void WrRunTime(struct KeyData *KeyNumber)

{    

        if(KeyNumber->JudgeRunFlag)

        {

             KeyNumber->RunCount ++;          //间隔计数启动

              if(KeyNumber->RunCount >= KeyRunClick_500Ms)

              KeyNumber->JudgeRunFlag = 0;  //若大于等待间隔,自动关闭标志

        }

        else

        {

             KeyNumber->RunCount = 0;

        }          

}

 

void WrRunTime(struct KeyData *KeyNumber)这个函数放在定时器中断函数中,在一次短击过后,程序会启动连击检测标志KeyNumber->JudgeRunFlag = 1; //连击判断启动标志位

在中断函数中,若标志KeyNumber->JudgeRunFlag = 1则启动计时。

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

//中断函数非常重要,一切以中断函数为参考

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

void Timer0Interrupt(void) interrupt 1

{

        TH0 = (65536-22000)/256;  //12MS

        TL0 = (65536-22000)%256;

        RunFunction = 1;                   //时间片段标志位

        WrRunTime(&KeyNumber);              //连击时间判断

}

 

一旦检测到if(KeyNumber->RunCount >= KeyRunClick_500Ms)

则连击检测自动关闭。

 

我的长击和连击判断后并没有在返回值中加什么东西,只有返回了一个标志位。

所以在测试程序中,你需要写看标志是否至1

你的测试程序中需要写一下函数:

struct KeyData  KeyNumber;

//时间片段标志位

unsigned char RunFunction = 0;

 

void TimerInit()

{

        TMOD = 0x01;

        TH0 = (65536-22000)/256;  //12MS

        TL0 = (65536-22000)%256;

        ET0 = 1;

        EA = 1;

        TR0 = 1;

}

//测试

void Loop_f(unsigned char KeyValue)

{

       switch(KeyValue)

       {

       }

}

//test

 

//test

 

void main()

{    

       TimerInit();

       while(1)

       {

              if(RunFunction)

              {

                     RunFunction = 0;

                     KeyLoop(&KeyNumber);

                     Loop_f(KeyNumber.KeyValue);

              }    

       }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值