纵观现在的智能硬件产品,按键少,功能多。在保证产品外观漂亮的同时,如何用最少的按键,来实现较多的功能。
所以就去网上搜了,按键长按与短按实现的方法。其中精华代码,是我在其他人的博客【http://www.51hei.com/bbs/dpj-41706-1.html】学来的。他的核心代码写的很棒,但是在后边函数调用的时候,要根据自己功能的实现去适当的更改代码。
核心算法
unsigned char Trg; unsigned char Cont; void KeyRead( void ) { unsigned char ReadData =GPIO_ReadInputDataBit(GPIO*,GPIO_Pin)^0xff; // 1 Trg = ReadData & (ReadData ^Cont); // 2 Cont=ReadData; // 3 } 作为一个新手来说,还是要具体其分析分析的: 我们就按键开始为高电平 ,按下为低电平,进行分析。 程序解读: Trg(triger) 代表的是触发,Cont(continue)代表的是连续按下。 1:GPIO_ReadInputDataBit()这个函数,就是读取按键GPIO*按键引脚的输入状态。^0xff,和前面读取到的状态,取反。并将数值储存到ReadData中; 2:用来计算触发变量; 3:用来计算连续变量。 (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) 第一次PB0按下的情况 端口数据为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) PB0按着不松(长按键)的情况 端口数据为0xfe,ReadData读端口并且取反是 0x01 了。 Trg = ReadData & (ReadData ^ Cont);因为这是连续按下,所以Cont是上次的值,应为为0x01。那么这个式子就变成了 Trg = 0x01 &(0x01^0x01) = 0x00 Cont = ReadData = 0x01; 结果就是: ReadData = 0x01; Trg = 0x00; Cont = 0x01; 因为现在按键是长按着,所以MCU会每隔一段时间就会,不断的执行这个函数,那么下次执行的时候情况会是怎么样的呢? 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; 很显然,这个回到了初始状态,也就是没有按键按下的状态。 该代码的精华之处就在于:Trg只有在按一按键的时候才为0x01,只要按键没有放开Trg从第二次进入KeyRead()后,就一直为0x00;Cout,只要按键是按下的,这个值就一直为0x01。 理解基本就是这样了,下面说一下用到的例子: 1:原文中博主的例子,有一个是这样的,类似小时候玩过的电子表调节时间。 按一个键不放手(直到到达指定的功能在放手),从按下,到放手,这个键盘包含了两个功能——切换模式、累计加数(针对他的例子说的功能,意会即可)。其实总的意思就是,从按下,到松手,Trg开始的时候(第一下)为0x01,因为没有放手,故第二次执行KeyRead()的时候,Trg就变为了0x00;故根据原博主的if(Trg & 0x01)和if(Cont & 0x01)这个的判断,从按下,到松手,这个两个函数都会执行过去。[注:原博主的KEY_PLUS的宏定义应该为错误的,若为0x02,这个if永远都不会执行] 上述讲述的这个键盘的功能,做项目可能用得到。 2:在一个就是,我自己做东西遇到的一个问题。按键,短按一个功能,长按一个功能(两个功能不能同时到来)。显然例子1的代码满足不了我的需求。下面看我的代码: void Key_Proc(void)
{
if(Trg&0x01) //短按
{
DelayMs(200); //200ms
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1) //判断读取出的键盘输入状态是否为高电平 {
运行代码; b=0;
}
}
if(Cout&0x01) //长按
{
b++;
if(b>20)
{
运行代码; b=0;
}
}
} int main() { KeyRead(); Key_Proc(); while(1) { DelayMs(10); break; }
} 我改的代码,除了 DelayMs(200); //200ms
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1) //判断读取出的键盘输入状态是否为高电平 其他的基本相同,这段代码,主要解决长按按键,两种功能同时出现的事件。 分析:按键按一下,即按下立马松开。键盘一开始为高电平,Delay,消除键盘按下到放开的反应时间,200MS足以,然后在检测按键输入状态,若为高,说明,已经放开了,判断这是一次短按。 按下不放手,按键开始为高电平,不放手,延迟过后,检测按键依然为低,不进入if,也就屏蔽掉了这个短,按的功能,然后进行后边的if(Cout&0x01),判断为真,进入,b++,后边有10MS的延迟。b一会就会加到20,执行程序,并清除b的值。 为什么短按里面要加一个b=0呢?如果不加的话,你按键盘,若的时间为达到长按的要求,这时候b也会++,可能会出现这样的情况,连续点击键盘,按下松开,按下松开.......虽然你没有长按,但是连续的点击会使b++到20,导致触发功能。故在短按里面加一个b=0. 大致就是这个样子了,还有就是在一个程序中,一个键盘,在不同的地方有不同的功能,这个又该如何实现呢?
我想出来的就是用switch() case 语句实现。switch判断处于程序的哪一个阶段,在找对应的case去执行就好了。今天太累了,具体怎么实现我就不贴代码了。
|