009-独立按键与矩阵按键

一、独立按键

  • 原理图:

  • 基本原理:按下为后,P3^1低电平。松开为高电平。
     

实验一:实现按动一下独立按键1,LED模块第一个灯的状态变化一次

#include<reg52.h>

//按动一下独立按键1,LED模块第一个灯的状态变化一次
//原理:按键按下时,P3^1的电压为低电平

//定义使用的IO口
sbit K1 = P3^1;
sbit LED = P2^0;

unsigned int i;

//延时函数的申明
void delay(int i);
//按键处理函数申明
void keypush();

//主函数
void main()
{
	LED = 1;
	while(1)
	{
		keypush();				
	}	
}


//延时函数
void delay(int i) 
{	
	while(i--);
}

//按键处理函数,判定K1是否按下
void keypush()
{
	if(K1 == 0)	//检测按键是否按下
	{
		delay(1000);	//消除抖动,一般为10ms
		if(K1 == 0)	//再次判断按键是否按下
		{
			LED = ~LED;	//小灯状态改变一次	
		}
		while(!K1);	//检测按键是否松开,如果松开了,则继续下一次检测;如果没松开,保持现有状态
	}
}

 
 

矩阵按键

  • 原理图:
    为了便于理解,修改效果如下图,与原有图的原理相同,其中O1至O4表示按键输入,I1至I4表示按键输出

  • 基本原理:(以S1工作为例)
    P1输入0x7F(0111 1111) => 此时P1 ^ 3为高电平 => 按下S1->VCC、电阻,S1、P1^7形成通路 => P1 ^ 3=P1 ^ 7为低电平。
     

实验二:实现依次按动矩阵按键第一行S1至S4四个,LED模块D1至D4依次点亮

#include<reg52.h>	//头文件

//功能:依次按动矩阵按键第一行S1至S4四个,LED模块D1至D4依次点亮

//定义使用的IO口
sbit S1 = P1^3;
sbit S2 = P1^2;
sbit S3 = P1^1;
sbit S4 = P1^0;

sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;


//主函数
void main()
{
	P1 = 0x7F;	//0111 1111 
	while(1)
	{
		LED1 = S1;	//如果S1按下,P1^3接通P1^7,由1->0,使得P2^0 = 0;
		LED2 = S2;
		LED3 = S3;
		LED4 = S4;
	}
}

 
 

实验三:实现使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始

#include<reg52.h>	//头文件

//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始

//定义使用的IO口
sbit S1 = P1^3;

//数码管的真值表
unsigned char shu_ma_guan[16] = 
	{
		0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,			  
		0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
	};

//主函数
void main()
{
	bit biao_zhi = 1;	//默认为1,表示按键此时为弹起状态
	unsigned char cnt = 0;
	P2 = 0x00;	//选择最低位数码管
	P1 = 0x7F;	//0111 1111 
	
	while(1)	//检测按键
	{
		if(S1 != biao_zhi)	//如果S1状态和上一次检测的状态不同
		{
			if(biao_zhi == 0)	//确认按键被按下
			{
				cnt++;		//数字加1
				if(cnt == 16)	//加到F时
				{
					cnt = 0;	//再次从0开始
				}
				P0 = shu_ma_guan[cnt];	//数码管显示对应数字	
			}
			biao_zhi = S1;	//将上一次的按键状态送到标志位
		}		
	}			
}

以上实验存在问题:按键按下一次,数字增加不一定是1,存在抖动。

  • 按键抖动:对于一般情况下,按动按键并不会立马接通,存在一个抖动时间。

  • 解决方案:

  1. 硬件电容消抖:抖动本身是高频信号,通过电容消除。但是这会增加硬件成本,还需要焊接等。

  2. 软件消抖(常用方法)

实验四:采用软件消抖来解决上面【实验二】中的问题,代码如下:

#include<reg52.h>	//头文件

//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始

//定义使用的IO口
sbit S1 = P1^3;
void delay(int i);

//数码管的真值表
unsigned char shu_ma_guan[16] = 
	{
		0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,			  
		0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
	};

//主函数
void main()
{
	bit biao_zhi = 1;	//默认为1,表示按键此时为弹起状态
	bit keybuf = 1;		//S1的暂存位
	unsigned char cnt = 0;

	P2 = 0x00;	//选择最低位数码管
	P1 = 0x7F;	//0111 1111 
	
	while(1)	//检测按键
	{
		keybuf = S1;	//将S1值存入keybuf中
		if(keybuf != biao_zhi)	//如果S1状态和上一次检测的状态不同
		{
			delay(1000);	//延长一段时间,消除抖动
			if(keybuf == S1)	//再次判断S1值和延时之前是否一致
			{
				if(biao_zhi == 0)	//确认按键被按下
				{
					cnt++;		//数字加1
					if(cnt == 16)	//加到F时
					{
						cnt = 0;	//再次从0开始
					}
					P0 = shu_ma_guan[cnt];	//数码管显示对应数字	
				}
				biao_zhi = S1;	//将上一次的按键状态送到标志位	
			}
		}
	}			
}
	
 
//定义延时函数
void delay(int i)
{
	while(i--);	 	
}

*备注:实际开发中不允许在while中使用delay语句。
 
 

实验五:更好的解决抖动的算法案例

连续读取八个状态,如果相同,表示按下或者开启,然后移位一位,再次读取八个状态,检测是否一样,如果一样,则不在抖动区,去过八位数字不同,则存在抖动,继续这样移位读取判断下去。
例如:
1111 1111 1111 1010 0010 0000 0000 0000 0000 0001 1001 0111 1111 1111 1111
使用本算法解决【实验二】的抖动问题,代码如下:

#include<reg52.h>	//头文件

//功能:使用S1按键控制数码管最低位显示,按动一下数码管值加1,到9后再次由0开始

//定义使用的IO口
sbit S1 = P1^3;

//数码管的真值表
unsigned char shu_ma_guan[16] = 
	{
		0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,			  
		0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
	};
bit keysta = 1;	//按键的状态,默认弹起

//主函数
void main()
{
	bit biao_zhi = 1;	//默认为1,表示按键之前为弹起状态
	unsigned char cnt = 0;

	P2 = 0x00;	//选择最低位数码管
	P1 = 0x7F;	//0111 1111 

	TMOD = 1;	//选择定时器工作模式
	TH0 = 0xF8;	//定时器定时2ms
	TL0 = 0x30;
	TR0 = 1;	//打开定时器

	EA = 1;		//打开中断总开关
	ET0 = 1;	//打开T0中断开关

	while(1)
	{
		if(keysta != biao_zhi)	//如果按键状态和之前状态不同
		{
			if(biao_zhi == 0)	//确认按键被按下
			{
				cnt++;		//数字加1
				if(cnt == 16)	//加到F时
				{
					cnt = 0;	//再次从0开始
				}
				P0 = shu_ma_guan[cnt];	//数码管显示对应数字	
			}
			biao_zhi = keysta;	//将现在的状态存起来以备和瑕疵状态比较	
		}
	}
}

//中断函数
void InterruptTime0() interrupt 1
{
	static unsigned  char keybuf = 0xFF;	//假设开始八位全部为1
	
	TH0 = 0xF8;
	TL0 = 0x30;	

	keybuf = (keybuf << 1) | S1;	
	//XXXX XXX0 | 0000 000S1,如果按键按下,S1为0 ,按位或结果XXXX XXX0;如果按键弹起,按位或结果XXXX XXX1
	if(keybuf == 0x00)
	{
		keysta = 0;	//按键按下
	}
	else if(keybuf == 0xFF)
	{
		keysta = 1;	//按键弹起
	}
	else	//其他状态,都认为是抖动
	{
	//啥也不干
	}
}

 
 

实验六:4×4矩阵按键S1至S16表示0至F这十六个数,按动对应的按键在数码管最低位显示对应数字

#include<reg52.h>	//头文件

//功能:4×4矩阵按键S1至S16表示0至F这十六个数,按动对应的按键在数码管最低位显示对应数字
//定义使用的IO口
sbit KeyOut1 = P1^7; 
sbit KeyOut2 = P1^6; 
sbit KeyOut3 = P1^5; 
sbit KeyOut4 = P1^4; 
sbit KeyIn1 = P1^3; 
sbit KeyIn2 = P1^2; 
sbit KeyIn3 = P1^1; 
sbit KeyIn4 = P1^0; 

//数码管的真值表
unsigned char shu_ma_guan[16] = 
	{
		0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,			  
		0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
	};

//默认全部按键弹起状态
unsigned char Keysta [4][4] = 
	{
		{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
	};

//主函数
void main()
{
	unsigned char i,j;
	//按键值备份,保存前一次的值,默认全部弹起
	unsigned char biao_zhi [4][4] = 
	{
		{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
	};

	TMOD = 0x01;//设置T0为工作模式1
	TH0 = 0xFC;	//定时器定时2ms
	TL0 = 0x18;
	TR0 = 1;	//打开定时器

	EA = 1;		//打开中断总开关
	ET0 = 1;	//打开T0中断开关
	
	P2 = 0x00;	//选择最低位数码管 
	
	P0 = 0x00;	//数码管全灭 
	while(1)
	{
		for(i=0; i<4; i++)	//循环检测4*4的矩阵按键
		{
			for(j=0; j<4; j++)
			{
				if(Keysta[i][j] != biao_zhi[i][j])	//检测按键动作	
				{
					if(biao_zhi[i][j] == 0)	//按键按下时执行动作	
					{
						P0 = shu_ma_guan[i*4+j];	//将按键编号显示到数码管	 	
					}
					biao_zhi[i][j] = Keysta[i][j];	//更新前一次的备份值	
				}
			}
		}
	} 
}			  


//中断函数
void InterruptTime0() interrupt 1
{
	unsigned char i = 0;
	static unsigned char Keyout = 0;	//矩阵按键扫描输出索引
	//矩阵按键扫描缓冲区,假设开始八位全部为1
	static unsigned  char Keybuf[4][4] = 
	{
		{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF},
		{0xFF,0xFF,0xFF,0xFF},{0xFF,0xFF,0xFF,0xFF}	
	};	
	
	TH0 = 0xFC;	//重新加载初值
	TL0 = 0x18;

	//将一行的4个按键值移入缓冲区
	Keybuf[Keyout][0] = (Keybuf[Keyout][0] << 1)| KeyIn1;
	Keybuf[Keyout][1] = (Keybuf[Keyout][1] << 1)| KeyIn2;
	Keybuf[Keyout][2] = (Keybuf[Keyout][2] << 1)| KeyIn3;
	Keybuf[Keyout][3] = (Keybuf[Keyout][3] << 1)| KeyIn4;
	//消抖后更新按键状态
	for(i=0; i<4; i++)	//每行4个按键,所以循环4次
	{
		if((Keybuf[Keyout][i] & 0x0F) == 0x00)
		{	//连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
			Keysta [Keyout][i] = 0;	
		}
		else if((Keybuf[Keyout][i] & 0x0F) == 0x0F)
		{	//连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
			Keysta [Keyout][i] = 1;	
		}
	}
	//执行下一次的扫描输出
	Keyout++;	//输出索引递增
	Keyout = Keyout & 0x03;	//索引值加到4即归零	
	switch(Keyout)	//根据索引,释放当前输出引脚,拉低下次的输出引脚
	{
		case 0: KeyOut4 = 1; KeyOut3 = 1;KeyOut2 = 1;KeyOut1 = 0;break;
		case 1: KeyOut4 = 1; KeyOut3 = 1;KeyOut2 = 0;KeyOut1 = 1;break;
		case 2: KeyOut4 = 1; KeyOut3 = 0;KeyOut2 = 1;KeyOut1 = 1;break;
		case 3: KeyOut4 = 0; KeyOut3 = 1;KeyOut2 = 1;KeyOut1 = 1;break;
		default:break;
	}		
}
posted @ 2021-02-05 21:40  shihao_Yang  阅读(551)  评论(0编辑  收藏  举报