???????? 正如比赛官方所说,按键要灵敏,不要出现按键按一下跳转好几次的情况(比如常见的界面),数码管也要消隐。因为可以说这是单片机最重要的输入和输出了。而输入和输出正如人的体验一样,给人最直接而深刻的感受。
由于之前有疑问,是关于按键驱动程序是按键松开后触发还是按下就触发(都仅触发一次),蓝桥官方老师给出的是若题目只考虑到功能描述,即并未说明是按键松开后触发还是按下就触发,那就是随便~。
不过,大部分赛题都是按下就触发,而我做了这么多套题写的都是按键松开触发(不知道会不会有影响)故我总结几种按下就触发的算法(基于状态机触发的且仅触发一次的简洁算法,正所谓大道至简,道法术器!)
第一种算法思路(优化前):
根据状态机有且仅有一次在按键扫描函数中从case 1变到case 2,通过定义一个临时变量保存按下的值,松开后初始化为0。
按键扫描函数(越简越好)
void key_scan(){
switch(key_state){
case 0:
P3=0x0f;
P42=0;
P44=0;
key_v=0;
if(P3!=0x0f) key_state=1;
break;
case 1:
P3=0x0f;
P42=0;
P44=0;
if(P3!=0x0f){
if(!P30)key_v=7;
else if(!P31)key_v=6;
else if(!P32)key_v=5;
else if(!P33)key_v=4;
P3=0xf0;
P42=1;
P44=1;
if(!P34)key_v+=12;
else if(!P35)key_v+=8;
else if(!P42)key_v+=4;
key_state=2;
}
else
key_state=0;
break;
case 2:
P3=0x0f;
P42=0;
P44=0;
if(P3==0x0f){
key_state=0;
}
break;
default:
break;
}
}
main函数驱动:
if(keytime==20){
keytime=0;
key_scan();
if(key_state==2){ // 第一次扫描,按键状态机由case0 -case1,
if(key_v!=keys){ // 第二次扫描 按键状态机由case1 -case2
switch(key_v){ //第三次扫描 按键状态机case2 -case 0(松开)或保持不变
case 4:
//to do
break;
case 8:
//to do
break;
case 9:
//to do
break;
case 5:
//to do
break;
}
keys=key_v;
}
}
else
keys=0;//松手后
}
第二种算法思路(进一步优化):
直接在状态机(按键的状态扫描函数)里保持按键从case1 -case2 的值,但状态机到case 2,将值初始化为0.
按键扫描(状态机):
void key_scan(){
switch(key_state){
case 0:
P3=0x0f;
P42=0;
P44=0;
key_v=0;
if(P3!=0x0f) key_state=1;
break;
case 1:
P3=0x0f;
P42=0;
P44=0;
if(P3!=0x0f){
if(!P30)key_v=7;
else if(!P31)key_v=6;
else if(!P32)key_v=5;
else if(!P33)key_v=4;
P3=0xf0;
P42=1;
P44=1;
if(!P34)key_v+=12;
else if(!P35)key_v+=8;
else if(!P42)key_v+=4;
key_state=2;
rkey=key_v;//rkey 保存从case 1跳转到case 2的键值
}
else
key_state=0;
break;
case 2:
rkey=0;
P3=0x0f;
P42=0;
P44=0;
if(P3==0x0f){
key_state=0;
}
break;
default:
break;
}
}
main函数驱动:(伪代码)
if(keytime==20){
keytime=0;
switch(rkey){
case 4://to do
break;
case 5://to do
break;
case 6://to do
break;
case 7://to do
break;
}
}
若是用独立键盘扫描,则更为简单,需要注意的是在NE555频率测量的时候,由于用了P34引脚,故需在编写按键状态机函数时做诸如P3==0x0f判断时屏蔽掉P34位用P3&0x0f==0x0f替代。
|