单片机按键灵活应用—矩阵式按键-----day7
电路原理图如下所示:
程序设计:
1.矩阵按键如果单独选取一行效果等同于day6中的独立式按键。
使端口P2.4为低则代码如下:P2.0-P2.3为按键使能引脚。
void main()
{
unsigned char keydata;
P0=0xFF; //置P0口
P1=0xFF; //置P1口
delay(10); //延时
ConfigTimer0(1);
EA = 1;
P2_4 = 0;
while(1)
{
if((P2&0X0f) == 0X0e)
{
Dis[0] = ucDataOneTab[1];//显示1
}
else if((P2&0X0f) == 0X0d)
{
Dis[0] = ucDataOneTab[2];
}
else if((P2&0X0f) == 0X0b)
{
Dis[0] = ucDataOneTab[3];
}
else if((P2&0X0f) == 0X07)
{
Dis[0] = ucDataOneTab[4];
}
}
}
1.逐行扫描法
扫描步骤如下:
上图中说明第四行有按键按下,读取f的值即可。
看程序中的注释吧,写的很清晰了。嘎嘎。
void Delay( uint tt )
{
while( tt-- );
}
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp;//临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
}
//逐行扫描法
unsigned char Key_Scan(void)
{
uchar Key_Temp,Scan_Code,Temp_Code;
P2 = 0XF0;
Key_Temp = P2; //取回P2端口的值
if((Key_Temp&0xf0)^0xf0) //0XE0 0b1110 0000 & 0b1111 0000 --0b1110 0000 ^0b1111 0000= 0b0001 0000 判断高4位是否有按键按下
{ //高4位不全是1则说明有按键按下
Scan_Code = 0xfe; //0B1111 1110 开启第一行扫描 按流程图来
while((Scan_Code&0x10) != 0x00) //0XFE = 0B1111 1110 0B1111 0111 //分行查询当前行是否有按键按下。循环判断四次
{
P2 = Scan_Code;
if((P2&0XF0)^0XF0) //如果成立则说明有按键按下,高四位有0
{
Temp_Code = (P2&0XF0)|0X0F;//1110 0111 &0x f0 = 0b1110 0000 |0x0f = 0b1110 1111 ~0b0001 0000 //取回P2口的值
return((~Temp_Code)+(~Scan_Code)); // 0b0001 0000+0000 0001 =0x11 // 取1列1行值
}
else
{
Scan_Code = (Scan_Code<<1)|0X01;// 0b1111 1110 <<1 = 1111 1100 | 0000 0001 = 1111 1101 移动4次后退出循环
}
Delay(10);
}
}
return(0);
}
//
void main()
{
unsigned char keydata;
P0=0xFF; //置P0口
P1=0xFF; //置P1口
delay(10); //延时
ConfigTimer0(1);
EA = 1;
P2_4 = 0;
while(1)
{
keydata = Key_Scan();
if(keydata != 0)
{
switch(keydata)
{
case 0x11:keyvalue = 0;break; //第一行第一列
case 0x21:keyvalue = 1;break;//第一行第二列
case 0x41:keyvalue = 2;break;//第一行第三列
case 0x81:keyvalue = 3;break;//第一行第四列
case 0x12:keyvalue = 4;break;//第二行第一列
case 0x22:keyvalue = 5;break;//第二行第二列
case 0x42:keyvalue = 6;break;//第二行第三列
case 0x82:keyvalue = 7;break;//第二行第四列
case 0x14:keyvalue = 8;break;//第三行第一列
case 0x24:keyvalue = 9;break;//第三行第二列
case 0x44:keyvalue = 10;break;//第三行第三列
case 0x84:keyvalue = 11;break;//第三行第四列
case 0x18:keyvalue = 12;break;//第四行第一列
case 0x28:keyvalue = 13;break;//第四行第二列
case 0x48:keyvalue = 14;break;//第四行第三列
case 0x88:keyvalue = 15;break;//第四行第四列
}
}
keydata = 0;
Dis[0] = ucDataOneTab[keyvalue%16];
}
}
void InterruptTimer0() interrupt 1
{
static unsigned char i;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
SendData(Dis[i], ucDataTwoTab[i]);
if(i<7)
{
i++;
}
else
{
i = 0;
}
}
2.线翻转法
必须要有上拉电阻:
硬件原理如下:
通过程序将低4位送0高四位送1 1111 0000 ---->1101 0000
记录第一次为低电平的行位置,然后通过程序将低四位送高电平,高四位送低电平
0000 1111 ------> 0000 0111
设计思路如下:
通过行与列定位当期按键的键值:
第一行
key-1 (列值-1) 1-1=0 2-1=1 3-1 =2 4-1 =3
第二行(列值+3)
key+3 1+3 =4 2+3 =5 3+3 =6 4+3=7
第三行(列值+7)
key+7 1+7=8 2+7=9 3+7=10 4+7 =11
第四行(列值+11)
key+11 1+11=12 2+11=13 3+11=14 4+11=15
代码如下:
void delay(uchar x)
{
uchar j;
while((x--)!=0) //CPU执行x*12次
{
for(j=0;j<125;j++)
{
;
}
}
}
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8);//定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
}
void InterruptTimer0() interrupt 1
{
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
SendData(Dis[i], ucDataTwoTab[i]);
if(i<7)
{
i++;
}
else
{
i = 0;
}
}
void Key_Scan()
{
temp = 0;
P2=0xF0;
temp=P2;
temp=temp;
temp = ~((temp>>4)|0xF0);
if(temp==1)
{
key=1;
}
else if(temp==2)
{
key=2;
}
else if(temp==4)
{
key=3;
}
else if(temp==8)
{
key=4;
}
else
{
key = 16;
}
P2=0x0F;
temp=P2;
temp=temp&0x0F;
temp=~(temp|0xF0);
if(temp == 1)
{
key = key - 1;
}
else if(temp == 2)
{
key = key + 3;
}
else if(temp == 4)
{
key = key + 7;
}
else if(temp == 8)
{
key = key + 11;
}
else
{
key = 16;
}
dis_buf = key; //键值入显示缓存
dis_buf = dis_buf&0x0f;
}
void Key_Down()
{
P2 = 0XF0;
if(P2 != 0xf0)
{
delay(50); //消抖 50ms是比较稳定的
if(P2 != 0xf0) //防止出现键值不稳定的现象。
{
// if(dis_buf < 10)
// {
// dis_buf++;
// }
// else
// {
// dis_buf = 0;
// }
Key_Scan();
}
}
}
void main()
{
P0=0xFF; //置P0口
P1=0xFF; //置P1口
delay(10); //延时
ConfigTimer0(1);
EA = 1;
while(1)
{
Key_Down();
Dis[0] = ucDataOneTab[dis_buf%16];
}
}
按键去抖:
硬件去抖:
软件去抖:
检测出键闭合后执行一个延时程序, 产生5ms~10ms的延时, 让前沿抖动消失后再一次检测键的状态, 如果仍保持闭合状态电平, 则确认为真正有键按下。