本帖最后由 若不俗和inide 于 2016-9-2 20:10 编辑
最近身边的很多同学都在学习原子哥的STM32视频,我发现他们都看不懂原子哥里面按键扫描程序。由于问的人多,所以我也写了个心得,分享一下,老鸟别喷我哈。先贴上代码吧:
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下
//注意此函数有响应优先级,KEY0>KEY1>WK_UP!!
u8 KEY_Scan(u8 mode) //mode:0,不支持连续按; 1,支持连续按;
{
static u8 key_up=1;//按键按松开标志 //flga
if(mode==1) {key_up=1;}
if(key_up==1 &&(KEY0==0||KEY1==0||WK_UP==1)) //如果有一个按键按下
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0) return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(WK_UP==1)return WKUP_PRES;
}
else if(KEY0==1&&KEY1==1&&WK_UP==0)
{key_up=1;}
return 0;// 无按键按下
}首先必须搞懂static这个关键字,用这个关键字定义的变量,其值会保存,下次再执行这个函数的时候。不会被初始化。也就是说有点类似与全局变量,值是会保存上一次赋值过后的值。
下面我们一句一句来分析代码。
首先我们假设将mode =1,设置成支持连续按下。同时假设KEY0按下。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
LZ,支持连续按键的分析不对。
1. static u8 key_up=1;(mode=1,这句不会运行了)
2. if(mode==1) {key_up=1;} 来一个判断语句,如果mode==1 ,那么全程key_up=1。(key_up不会全程等于1)
3.if(key_flag==1 &&(KEY0==0||KEY1==0||WK_UP==1)) ,由于key_up=1,假设按键key0又按下了。所以if语句成立。执行括号里面的内容。并且返回按键按下的值
4. 先消抖, key_up=0; 注意注意 重点来 key_up=0; 由于我们前面有一个判断语句 if(mode==1) {key_up=1;} 所以key_up=0; 之后,瞬间 key_up又等于1了。(这里严重解释错误,应该是:延时消抖后,key_up=0,key_up重新赋值后的确是等于0了,不会发生瞬间 key_up又等于1这样的事情)
5. 假设此时key0按键按着没有松开,所以KEY0==0,由于第4步所说的,key_up=0之后的瞬间 key_up又等于1了。(5.按键按着没有松开,main()程序继续调用KEY_Scan(1)按键扫描程序,由于mode=1,所以if(mode)key_up=1; )
6.所以此时此刻,if(key_up==1 &&(KEY0==0||KEY1==0||WK_UP==1)) 又成立了,又执行括号里面的内容。并且返回按键按下的值。如此循环下去,达到长按效果。
u8 KEY_Scan(u8); //按键扫描函数
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下
//4,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return 1;
else if(KEY1==0)return 2;
else if(KEY2==0)return 3;
else if(KEY3==1)return 4;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1;
return 0;// 无按键按下
}
函数解读:
一、mode=0时,按键不支持连按,程序运行过程,主函数中,按键扫描,首先默认为key_up=1,因为从开机,到函数运行速度是极快的,因此会扫描到无按键按下。当然,有一种特殊的情况是,在起机时,就按下按键,此时key_up=0,估计少有人去做这么蛋疼的事吧!
一切准备就绪,程序开始运行,如有按键按下,执行到if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))
此时,key_up=0,函数返回一个u8型数(0||1||2||3||4),如按键不松开,key_up=0,if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))语句永远不会执行,只有按键松开,执行else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1,因此实现了按键不连按的效果!当然,此时,或许会有人想,如果,我按一下键,赶紧再按一下,不就永远不会执行if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))里的语句了么,我想说的是,你多虑了,我现在所用的是芯片是72MHZ的,代码执行的间距仅仅几毫秒,人的反应速度还没那么快呢,(注:一般条件下,反应时间约为0.1~0.5 s。对于复杂的选择性反应时间达1~3 s,要进行复杂判断和认识的反应时间平均达3~5 s,具体的带有判别的反应时间t可用下式求得: t = k log2 (n+1) 式中,k为常数;n为等概率出现的选择对象数;(n+1)是考虑判明是否出现刺激。)即是低一点的芯片也是没问题的。
二、mode=1时,初始化和mode=0是一样,上面提到,在这里就不再说了,直接看代码,函数在主函数中循环,若有按键按下,运行if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1)),此时key_up=0;不丢开按键,主函数循环到按键扫面函数,执行if(mode)key_up=1;之后,若有按键按下,当然可以运行if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))里的语句了。至此,实现了连续按键。
三、这个函数的封装性极强,换句话说,即是不懂这个函数里的意思,也可以直接拿来运用,当然函数应当这样描述:
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下
//4,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 t;
t=KEY_Scan(0);
switch(t)
{
case 1:
case 2:
case 3:
case 4:
}
函数的整体设计确实很吸引人,如果将函数的扫描放在中断里,将大大节省内存,从而可以提高程序运行速率!
在这儿不得不再次提到一些高级语言,比如C#,VB,Java等等,个人觉得这些高级语言也许正和上面的这个函数有点相像,封装性太强,尤其C#,感觉一种很飘渺的感觉,说实在的起初还可以,时间久了便腻了!我觉得编程人员不是为编程而编程,而是从编程中去寻找乐趣,寻求一种自由!
所以,正如这个函数,如果只是调用,太简单了,但是如果你了解了这个的设计过程,那么你就是成功地!当然,若想获得更好,懂得底层驱动,甚至,芯片的驱动原理,呵呵,便最好不过了!
《易经》:“易有太极,是生两仪,两仪生四象,四象生八卦。”孔颖达疏:“太极谓天地未分之前,元气混而为一,即是太初、太一也。”
由于时间仓促及水平有限,难免有些差错和不足,希望读者多多指教!
高手勿喷,在此谢过代码的设计者!
给楼主一个建议:多花些时间在代码可读性上,尽量让使用代码的人操作傻瓜化。
我也有写过按键的,跟LZ交流。
《STM8L之自定义长短按键》:http://blog.csdn.net/feilusia/article/details/53154346
一周热门 更多>