按键是一种常开型按钮开关,与单片机的常见接法如图所示。按键未按下时两个触点断开,单片机I/O口输入高电平;当按键闭合时,I/O口输入低电平。
当I/O口为P0口时,由于没有内部上拉电阻,所以必须外接上拉电阻,当I/O口为P1、P2和P3口时,由于内部已有上拉电阻,因此可省略外接的上拉电阻。
单片机系统中所使用的键盘都是机械式的弹性按键,因为存在机械触点的弹性作用,在按键闭合和弹起的瞬间都会出现抖动,如图所示。按键抖动一般会持续5~10ms,为使一次按键仅被处理一次,必须消除按键抖动。消除按键抖动可以采用软件消抖或硬件消抖。
软件消抖指在检测到有按键闭合时,延时一小段时间之后再次检测,如果仍然检测到按键闭合,则认为按键真正闭合。
硬件消抖方法
1.R-S触发器
一旦有键按下,触发器立即反转,触点的抖动便不会再对输出产生影响,按键释放时也一样。
2.电容滤波
将电容并联在按键的两端,利用电容放电的延时特性,将产生抖动的电平通过电容吸收掉,从而达到消抖的作用;
独立键盘指每个按键都占据一个独立的引脚。
同一行上的所有按键都连接到一个引脚,同一列上的所有按键都连接到一个引脚。
(1)列扫描方式
所有行线都输出高电平,列线依次输出低电平;查询该列上行的值,若为0,则说明该行上的按键被按下;若该列上所有行的值都为1,则进行下一列行值的查询;循环往复。
(2)反转法
所有行线都输出0,查询列线上的值,若某列线上的值为0,则说明该列线上的按键被按下;然后所有列线都输出0,查询行线上的值,若某行线上的值为0,则说明该行线上的按键被按下;从而确定某行某列的按键被按下。
#include <reg51.h>
#define uint unsigned int
#define uchar unsigned char
uint key,key_num;//键盘扫描值,键盘键值
/********************************************************************/
//函数名:delay_1ms(uint x)
//功能:利用定时器0精确定时1ms;自加 time_t的值为后面时间调整函数服务
//调用函数:
//输入参数:x,1ms计数
//输出参数:
//说明:延时的时间为1ms乘以x
/********************************************************************/
void delay_1ms(uint x)
{
TMOD=0X01;//开定时器0,工作方式为1
TR0=1;//启动定时器0;
while(x--)
{
TH0=0Xfc;//定时1ms初值的高8位装入TH0
TL0=0X18;//定时1ms初值的低8位装入TL0
while(!TF0);//等待,直到TF0为1
TF0=0;
}
TR0=0;//停止定时器0;
}
/**********************************************************/
//函数名:keyscan()
//功能:得出4x4键盘的行列扫描值
//调用函数:delay_1ms(uint x)
//输入参数:
//输出参数:
//说明:通过P1口的扫描得出扫描值key,无键按下key为16
/**********************************************************/
uchar keyscan()
{
uchar code_h; //行扫描值
uchar code_l; //列扫描值
P1=0XF0; //P1.0-P1.3全为0,(行状态全为低电平)
if((P1&0xF0)!=0XF0) //如果P1.4-P1.7不全为1,可能有键按下(为准确识别按键动作)
{
delay_1ms(5); //廷时去抖动,为准确识别按键动作
if((P1&0xF0)!=0XF0)//重读高4位(列状态),若还是不全为1,定有键按下
{
code_h=0xfe; //开始行扫描 (0xfe:11111110)
while((code_h&0x10)!=0x00)//判断是否扫描四行(X0~X3)完毕,若不是,继续扫描(0x10:00010000)
{
P1=code_h; //第1次P1.0置为0,其余高电平;第2次P1.1置为0,其余高电平 ;第3次P1.2置为0,其余高电平;第4次P1.3置为0,其余高电平
if((P1&0xF0)!=0XF0) //如果P1.4-P1.7不全为1,该行有键按下(0xF0:11110000)
{
code_l=(P1&0xF0|0x0F);//保留P1口高4位,低4位变为1,作为列值
return((~code_h)+(~code_l));//键盘编码=行扫描值+列扫描值 (取反后用,相当于改为按下按键对应的行、列标识为1,其它为0)
}
else //左移后补0,但是我们需要将其他位置1,所以+1
code_h=(code_h<<1)|0x01; //若该行无键按下,行扫描值左移+1,扫描下一行
}
}
}
return(16); //无键按下,返回16
}
/**********************************************************/
//函数名:keynum()
//功能:得出4x4按键的键值
//调用函数:keyscan()
//输入参数:
//输出参数:
//说明:通过key的值确定按键键值
/**********************************************************/
void keynum()
{
uchar i,j;
uchar code tab[4][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};
//4x4键盘各键值标注
key=16;
key = keyscan();//引入key值
if((key&0x01)!=0) i=0; //判断出按下按键的行列号 (即找出低电平的行、列)
if((key&0x02)!=0) i=1;
if((key&0x04)!=0) i=2;
if((key&0x08)!=0) i=3;
if((key&0x10)!=0) j=0;
if((key&0x20)!=0) j=1;
if((key&0x40)!=0) j=2;
if((key&0x80)!=0) j=3;
if(key!=16)
{
key_num = tab[i][j];//通过比较得出4x4键盘的键值
}
}
void main()
{
uint shi,ge;
shi = 0;
ge = 0;
P2 = 0x16;//初始值 为16
while(1)
{
keynum(); //获取到键值
if(key!=16)
{
shi = key_num/10;//获得十位
ge = key_num%10;//获得个位
if(shi)
{
P2 = 0x10|ge;
}
else
{
P2 = ge;
}
}
delay_1ms(10);//延时
}
}
全部0条评论
快来发表一下你的评论吧 !