状态机独立按键的实现(转载)

原文地址: https://www.amobbs.com/thread-4308630-1-1.html http://www.openedv.com/thread-22288-1-1.html

#define Key P3 
unsigned char Trg; //触发
unsigned char Cont; //连续按下
void KeyRead( void )
{
    unsigned char ReadData = Key^0xff;        
    Trg = ReadData & (ReadData ^ Cont);     
    Cont = ReadData;                                    
}

1 、没有按键的时候

端口为0xff,ReadData读端口并且取反,很显然,就是 0x00 了。
Trg = ReadData & (ReadData ^ Cont); //(初始状态下,Cont也是为0的)很简单的数学计算,因为ReadData为0,则它和任何数“相与”,结果也是为0的。
Cont = ReadData; 保存Cont 其实就是等于ReadData,为0;
结果就是:
ReadData = 0;
Trg = 0;
Cont = 0;

2、第一次P30按下的情况

端口数据为0xfe,ReadData读端口并且取反,很显然,就是 0x01 了。
Trg = ReadData & (ReadData ^ Cont); 因为这是第一次按下,所以Cont是上次的值,应为为0。那么这个式子的值也不难算,也就是 Trg = 0x01 & (0x01^0x00) = 0x01
Cont = ReadData = 0x01;
结果就是:
ReadData = 0x01;
Trg = 0x01;Trg只会在这个时候对应位的值为1,其它时候都为0
Cont = 0x01;

3、P30按着不松(长按键)的情况

端口数据为0xfe,ReadData读端口并且取反是 0x01 了。
Trg = ReadData & (ReadData ^ Cont); //因为这是连续按下,所以Cont是上次的值,应为为0x01。那么这个式子就变成了 Trg = 0x01 & (0x01^0x01) = 0x00
Cont = ReadData = 0x01;
结果就是:
ReadData = 0x01;
Trg = 0x00;
Cont = 0x01;
因为现在按键是长按着,所以MCU会每个一定时间(20ms左右)不断的执行这个函数,那么下次执行的时候情况会是怎么样的呢?
ReadData = 0x01;这个不会变,因为按键没有松开
Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) = 0 ,只要按键没有松开,这个Trg值永远为 0
Cont = 0x01;只要按键没有松开,这个值永远是0x01

4、按键松开的情况

端口数据为0xff,ReadData读端口并且取反是 0x00 了。
Trg = ReadData & (ReadData ^ Cont) = 0x00 & (0x00^0x01) = 0x00
Cont = ReadData = 0x00;
结果就是:
ReadData = 0x00;
Trg = 0x00;
Cont = 0x00;
很显然,这个回到了初始状态,也就是没有按键按下的状态。

应用1

单次触发的按键处理

bit key_flag=0;
void main (void)
{
   Timer0Init();//定时器1ms 在中断服务函数中计数20次 标志位置1 
   while(1)
  {
    if(key_flag)
    {
	 key_flag=0;
	 Key_Read();
	 if(Trg&0x01) //0x01 可以用使用宏定义
	 {
           //需要执行的语句                          
	 }
    }
  }
}

应用2

长按键处理,短按一次执行功能1 ,长按2s执行功能2,长按3s执行功能3;

#define KEY_MODE 0x01    // 模式按键
unsigned char  Mode;
unsigned char  Mode_Count;
void KeyProc(void)
{
  if (Trg & KEY_MODE) // 如果按下的是KEY_MODE,而且常按这按键没有用,它是不会执行第二次, 必须先松开再按下
    {                   
        //需要执行的语句  
    }
    if (Cont & KEY_MODE) // 按键被按着不放
    {
       Mode_Count++;       //计时
       if (Mode_Count > 100) // 20ms*100 = 2S 如果时间到
       {
          //需要执行的语句  
       }          
    }
}

应用3

组合按键
假如A键连接P30端口,Shift键连接P31端口,则Shift+A

//按下 shift
ReadData = P3^0xff = 0x01^0xff = 0x01;
Trg = ReadData  & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x00)= 0x01;
Cont =  ReadData  = 0x01;

//按着 shift 不放即
ReadData  = 0x01;
Trg = 0x00;
Cont = 0x01;

//接着按下 A 的情况是
ReadData = P3^0xff = 0x03^0xff = 0x03;
Trg = ReadData  & (ReadData ^ Cont) = 0x03 & (0x03 ^ 0x01)= 0x02;
Cont = ReadData   = 0x03;

//所以只要判断Trg和Cont就可以了
//shift+A就是
if(Trg==0x02 && Cont==0x03)

总结

这个三行代码的核心就是
Trg = ReadData & (ReadData ^ Cont);
Cont是上一次的键值 ,Trg 是这一次的键值。
Trg 只有在新按下的那一次会变为1,否则一直为 0 。
所以,处理单次按键的时候只需要判断 Trg ,判断长按的时候,需要判断 Cont 持续的时间 。
而组合按键并不是同时按下,也是有一个先后顺序的,即 Cont先按的键是否释放,Trg是后按下的键。

posted @ 2018-12-08 16:28  年小猫  阅读(1429)  评论(0编辑  收藏  举报