绝对不能放弃的事!要从“今天”开始!!
【刚刚调字体颜色的时候才发现,原来调颜色和字体大小也有语句在前面,突然发现了什么不得了的事情……】
为啥我在这只说矩阵按键?
因为独立按键、矩阵按键其实本质上控制原理都一样,都是通过检测高低电平来判断按键是否按下。
先看原理图:
独立按键是最左侧的S4-S7四个按键,矩阵按键则为这个4x4的16个按键。
左侧J5,若用跳帽将2、3短接,则为独立按键模式,若用跳帽将1、2短接,则为矩阵按键模式。
矩阵按键的使用,就是检测某个按键对应的行列是否同时置0,若是,则按下,若不是,则没按。
首先,将第一列置0,P3=0x7f。
【注意】:
①P36 - >P42, P37 - >P44;
②并且若使用的头文件为
第一列置低电平后,可检测第一列的按键哪个按下,若为S7按下,则P30为低电平,则 P3 = 0x7e;
另外,判断完了之后,还要确定按键是否释放,要有一个while语句。
其他按键同理。
对常见写法的分析
在我学习矩阵按键的过程中最初见到的写法:
由于按键需要不停的扫描,达到成功检测的目的,则需要进行消抖测试。
若不对抖动进行检测和处理,就会造成误判。
#include
sfr P4 = 0xc0;
sbit P42 = P3^6;
sbit P44 = P3^7;
void Delay10ms() //@11.0592MHz
{
unsigned char i, j;
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
uchar keyscan()
{
uchar key,temp;
P3 = 0x7f; //令第一列为低电平
P42 = 1;P44 = 0;
temp &= 0x0f; //P3与0x0f相与,若结果不为0x0f,说明代表行的P30-P33中有置低电平的部分,说明有键按下
if(temp != 0x0f)
{
delay10ms(); //延时消抖
temp = P3;
temp &= 0X0f; //再次确认是否按下
if(temp != 0x0f) //确实有键按下
{
temp = P3;
swith(temp)
{
case 0x7e:key = 7;break;
case 0x7d:key = 6;break;
case 0x7b:key = 5;break;
case 0x77:key = 4;break;
default:break;
}
while(temp != 0x0f) //判断是否释放按键
{
temp = P3;
temp &= 0x0f;
}
}
}
……
}
就仅举S4-S7来写,其他列同理,只不过若是第二列,则先将P3 = 0xbf,再去判断其他行是否置低电平。
上面的写法是我之前比较喜欢用的,但是使用延时函数会对整个使用造成比较大的影响。 我之前用的时候就经常会误判,延时函数占用了一部分时间。 在此,给大家推荐另一种方法,也是我逐渐适应的一种写法此时按键的扫描不再放在主函数中,而是在中断中扫描,通过判断按键状态的持续时间来确定按键是否按下或者弹起。
隔一段时间扫描一次按键的状态,如果与上一次按键的状态不同,再隔一段时间扫描一次按键状态,如果又改变了,则说明按键完成了一次按下又弹起的动作。
sbit Key_Out_1 = P3^0; //行扫描
sbit Key_Out_2 = P3^1;
sbit Key_Out_3 = P3^2;
sbit Key_Out_4 = P3^3;
sbit Key_In_4 = P3^4; //列扫描
sbit Key_In_3 = P3^5;
sbit Key_In_2 = P4^2;
sbit Key_In_1 = P4^4;
uchar KeySta[4][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}}; //储存按键当前值
uchar code KeyMap[4][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; //规定16个键分别代表的值
void KeyDriver()
{
uchar i,j;
static uchar KeyBack[4][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}}; //矩阵按键初始化
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
if(KeySta[i][j] != KeyBack[i][j]) //按键状态改变
{
if(KeySta[i][j] != 0) //按键松开
{
KeyAction();//按下之后,写入相应语句执行按键操作
}
KeyBack[i][j] = KeySta[i][j]; //将变化后的按键值储存
}
}
}
}
uchar KeyScan()
{
uchar i=0;
static uchar KeyOut = 0;
static uchar KeyBuff[4][4] = {{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff}};
switch(KeyOut)
{
case 0:Key_Out_1=0; Key_Out_4=1; break;
case 1:Key_Out_2=0; Key_Out_1=1; break;
case 2:Key_Out_3=0; Key_Out_2=1; break;
case 3:Key_Out_4=0; Key_Out_3=1; break;
}
KeyBuff[KeyOut][0] = KeyBuff[KeyOut][0]<<1 | Key_In_1;
KeyBuff[KeyOut][1] = KeyBuff[KeyOut][1]<<1 | Key_In_2;
KeyBuff[KeyOut][2] = KeyBuff[KeyOut][2]<<1 | Key_In_3;
KeyBuff[KeyOut][3] = KeyBuff[KeyOut][3]<<1 | Key_In_4;
for(i=0;i<4;i++) //消抖
{
if((KeyBuff[KeyOut][i]&0x0f) == 0x00)
{
KeySta[KeyOut][i] = 0;
}
else if((KeyBuff[KeyOut][i]&0x0f) == 0x0f)
{
KeySta[KeyOut][i] = 1;
}
}
KeyOut ++; //检测下一行
KeyOut &= 0X03; //若超过3,则与0x03相与又变回0x00了
}
函数的使用应该在中断函数中进行每1ms一次,4ms消抖
void Timer0() interrupt 1
{
KeyScan();
}
void main()
{
while(1)
{
KeyDrive();
}
}
通过阅读别人的博客,学习到:
①KeyScan() 放在中断中作扫描作用,运用移位操作、静态变量、循环,判断按下的键值;
②KeyDriver() 放在主函数里,判断按键的按下、松开、按住。
其实一开始从别处看到上面第二种写法,理解了很久才懂。
以前用过的另一种方法似乎比这个好理解,但是我忘记写法了【没办法,每次学到一半都放弃了】
有问题欢迎大家提出,共同交流学习!