51单片机矩阵按键扫描
经典扫描方法:(存在一个弊端:当使用一个按键,按一次变量加1,真实情况是按一次,结果变量加了好多次。)
unsigned char Check_key(void)
{
unsigned char row,col,temp1,temp2,keyvalue;
temp1 = 0x01;
for(row=0;row<4;row++) // 行扫
{
P0 = 0xF0; // 先将P0.4~P0.7置高
P0 = ~temp1; // 使P0.1~P0.3中有一位为0
temp1 *= 2; // temp1左移一位
if((P0 & 0xF0) < 0xF0) // 当按键按下时,(P0 & 0xF0) 高四位不在是F,可能为7或B或D或E。
{ // 这时可以确定按下的是(row+1)行
temp2 = 0x80;
for(col=0;col<4;col++) // 列扫
{
if((P0 & temp2)==0x00) // 当(P0 & temp2)等于0x00时,可以确定按下的位置是(col+1)列
{
keyvalue = row*4+col; // 得到所按下按键的键值
return keyvalue; // 把得到的键值作为返回值
}
temp2 /= 2; // temp2右移一位
}
}
}
return 16; // 因为定义数码管段选表中,16对应的是全灭,故无按键按下时返回16
}
我个人写了一个矩阵按键扫描,可以解决上面那个弊端,代码如下:
#define GPIO_KEY P0 // 重定义矩阵按键端口
/*************************************************
* 函数名:delay_ms
* 描述 :延时函数 (具体情况以自己的板子为准)
* 参数 :xms , xms是几延时几毫秒 。晶振:12MHz
* 返回值:无
* 调用 :内部调用
*************************************************/
void delay_ms(unsigned int xms)
{
unsigned int i, j;
for(i=xms;i>0;i--)
{
for(j=921;j>0;j--);
}
}
/*************************************************
* 函数名:key_scan
* 描述 :矩阵键盘的键值返回(在没有按任何键时,
* 函数返回值为16,只有在按下按键的瞬间返
* 回的键值才是你真正按下按键的键值,正是这样才能实现长按连续加或减)
* 参数 :无
* 返回值:键值
* 调用 :外部调用
*************************************************/
unsigned char key_scan()
{
unsigned char keyvalue, a=0;
GPIO_KEY = 0xf0; // 高四位为1,低四位为0
if(GPIO_KEY != 0xf0)
{
delay_ms(10); // 延时消抖
if(GPIO_KEY != 0xf0)
{
GPIO_KEY=0xf0;
switch(GPIO_KEY)
{
case 0xe0: keyvalue = 3;break; // 确定矩阵按键被按下的位置是第几列
case 0xd0: keyvalue = 2;break; // 0、1、2、3
case 0xb0: keyvalue = 1;break;
case 0x70: keyvalue = 0;break;
}
GPIO_KEY=0x0f;
// 确定矩阵按键被按下位置的键值:列(或0或1或2或3) + 行(或0或4或8或12)
// switch(GPIO_KEY) // 经测试对于我使用的开发板,这条switch语句不能得到想要的结果
// {
// case 0x0e: keyvalue = keyvalue;break;
// case 0x0d: keyvalue = keyvalue+4;break;
// case 0x0b: keyvalue = keyvalue+8;break;
// case 0x07: keyvalue = keyvalue+12;break;
// }
// 使用下面这种形式,则可以达到目的
if((GPIO_KEY != 0x0d)||(GPIO_KEY != 0x0b)||(GPIO_KEY != 0x07))
{
keyvalue = keyvalue;
}
if(GPIO_KEY == 0x0d)
{
keyvalue = keyvalue+4;
}
if(GPIO_KEY == 0x0b)
{
keyvalue = keyvalue+8;
}
if(GPIO_KEY == 0x07)
{
keyvalue = keyvalue+12;
}
while((a<50)&&(GPIO_KEY!=0x0f))
{
delay_ms(10);
a++;
}
}
}
if(GPIO_KEY==0xF0)
{
keyvalue = 16;
}
return keyvalue;
}
行列扫描解析
行:
从上面五幅图片,我们得到:第一行(0x0E)、第二行(0x0D)、第三行(0x0B)、第四行(0x07)。因为每一行有四列,所以定义这四行的起点分别是:0、4、8、12(04、14、24、34)。
列:
从上面的五幅图片中,我们得到:第一列(0x70)、第二列(0xB0)、第三列(0xD0)、第四列(0xE0)。因为每一行有四列,所以定义这四列分别固定为:0、1、2、3。
【最后确定键值:列号(0/1/2/3)+行起点(0/4/8/12)】
0 — 1 — 2 — 3 (列扫0/1/2/3)+(行扫0)
4 — 5 — 6 — 7 (列扫0/1/2/3)+(行扫4)
8 — 9 – 10 – 11 (列扫0/1/2/3)+(行扫8)
12 - 13 - 14 - 15 (列扫0/1/2/3)+(行扫12)
矩阵键盘程序中的扫描原理:首先进行列扫描,确定按下的是第几列(即每一列固定的列号),接着进行行扫描,确定是第几行(即确定行起点)。