独立按键

独立按键:实现原理是通过轻触按键内部的 金属弹片受力弹动来实现接通和断开

“1,2”和“3,4”管脚之间距离短,初始不导通,“1,3”和“2,4”管脚之间距离长,初始值导通。
当按键按下时, 距离短的会变为导通,距离长的会变为不导通 ,所以就可以利用按键这一特性来控制其他的事物。
例如管脚1接单片机的一个引脚,管脚2接地。当按键被按下时,就会给这个引脚一个 低电平 。如果不按,单片机的这个引脚默认的是 高电平

但是按键一般都会抖动,所以要进行消抖: 硬件消抖和软件消抖
硬件消抖是通过 充放电延时时间 来进行消抖,但成本高,一个按键就需要(一个电阻与一个电源),所以 一般选择软件消抖,软件消抖时间一般为 10ms

代码如下:

# include "reg52.h"
 
 
typedef unsigned int u16;
typedef unsigned char u8;
//定义4个按键管脚
sbit K1=P3^1;
sbit K2=P3^0;
sbit K3=P3^2;
sbit K4=P3^3;
sbit LED1=P2^0;
 
//宏定义如果按下则返回
# define K1_PRESS	1
# define K2_PRESS	2
# define K3_PRESS	3
# define K4_PRESS	4
//没按下
# define K_UNPRESS 0
 
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}
 
u8 Detection_Key(u8 mode)
{
	static u8 key=1;
	if(mode)
		key=1;
	if(key==1 && (K1==0 || K2==0 || K3==0 || K4==0))
	{
		key=0;
	
		delay_10us(1000);//消抖
		if(K1==0)
		return K1_PRESS;
		else if(K2==0)
			return K2_PRESS;
		else if(K3==0)
			return K3_PRESS;
		else if(K4==0)
			return K4_PRESS;
	}
	else if(K1==1 && K2==1 && K3==1 && K4==1)
	{
		key=1;	
	}
	return K_UNPRESS;
	
}
 
void main()
{
	
	while(1)
	{
		u8 key=Detection_Key(0);//1代表长按和0代表只按一次
		if(key==K1_PRESS)
		{
			LED1=!LED1;
		}
	}
	while(1);
}

备注

刚刚还试了加个蜂鸣器,只要按下按键LED1就亮并且蜂鸣器响,结果发现不仅LED1亮了连LED6也随着蜂鸣器响而在闪,问了客服才知道原来蜂鸣器和LED6共用IO口,原本就查查开发手册的事我还搞半天,我还以为是代码还是板子有问题,这件事让我懂得查手册是多么重要!

实验现象

矩阵按键

行列描述法

行列描述法:
先让一列为 低电平,其余几列全为 高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有 低电平,若检测到某一行为 低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次 低电平,再轮流检测一次各行是否变为 低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置 低电平,扫描列是否有 低电平。从而达到整个键盘的检测。

代码如下:

# include "reg52.h"
 
typedef unsigned int u16;
typedef unsigned char u8;
# define KEY_Matrix_PORT		P1//定义矩阵管脚
# define SMG_PORT		P0//数码管管脚
 
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}
 
/****************************************************************************************
* 使用4*4按键
****************************************************************************************/ 
//行列描述法             
u8 key_matrix_ranks(void)
{
	
	u8 key=0;//键值
	KEY_Matrix_PORT=0xf7;//第一列按键
	if(KEY_Matrix_PORT!=0xf7)
	{
		delay_10us(1000);//消抖
		switch(KEY_Matrix_PORT)
		{
			case 0x77:key=1;break;//S1
			case 0xb7:key=5;break;//S5
			case 0xd7:key=9;break;//S9
			case 0xe7:key=13;break;//S13
		}
	}
	KEY_Matrix_PORT=0xfb;//第二列按键
	if(KEY_Matrix_PORT!=0xfb)
	{
		delay_10us(1000);//消抖
		switch(KEY_Matrix_PORT)
		{
			case 0x7b:key=2;break;//S2
			case 0xbb:key=6;break;//S6
			case 0xdb:key=10;break;//S10
			case 0xeb:key=14;break;//S14
		}
	}
	KEY_Matrix_PORT=0xfd;//第三列按键
	if(KEY_Matrix_PORT!=0xfd)
	{
		delay_10us(1000);//消抖
		switch(KEY_Matrix_PORT)
		{
			case 0x7d:key=3;break;//S3
			case 0xbd:key=7;break;//S7
			case 0xdd:key=11;break;//S11
			case 0xed:key=15;break;//S15
		}
	}
	KEY_Matrix_PORT=0xfe;//第四列按键
	if(KEY_Matrix_PORT!=0xfe)
	{
		delay_10us(1000);//消抖
		switch(KEY_Matrix_PORT)
		{
			case 0x7e:key=4;break;//S4
			case 0xbe:key=8;break;//S8
			case 0xde:key=12;break;//S12
			case 0xee:key=16;break;//S16
		}
	}
	return key;
}
 
 
u8 gsmg_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
		        0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//定义数组存放0-F段码,gsmg中g代表全局变量
void main()
{
	u8 key=0;
	while(1)
	{
		 key=key_matrix_ranks();//接收键值
		if(key!=0)//要确保key不能为0
		{
			SMG_PORT=gsmg_code[key-1];//数码管显示
		}
		
	}
	while(1);
}

线翻转法

线翻转法:
使所有行线为 低电平时,检测所有列线是否有 低电平,如果有,就 记录列线值;然后再 翻转,使所有列线都为 低电平,检测所有行线的值, 由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部 按键。

//线翻转法
u8 key_matrix_overturn(void)
{
	u8 key=0;//键值
	KEY_Matrix_PORT=0x0f;//行为低电平,测试列
	if(KEY_Matrix_PORT!=0x0f)
	{
		delay_10us(1000);
		if(KEY_Matrix_PORT!=0x0f)
		{
			switch(KEY_Matrix_PORT)
			{
				case 0x07:key=1;break;//第一列
				case 0x0b:key=2;break;//第二列
				case 0x0d:key=3;break;//第三列
				case 0x0e:key=4;break;//第四列
			}
			KEY_Matrix_PORT=0xf0;//列为低电平,测试行
			if(	KEY_Matrix_PORT!=0xf0)
			{
					switch(KEY_Matrix_PORT)
				{
					case 0x70:key=key;break;//第一行
					case 0xb0:key+=4;break;//第二行
					case 0xd0:key+=8;break;//第三行
					case 0xe0:key+=12;break;//第四行
				}	
			}
		}
	}
	else
	{
		key=0;
	}
	return key;
}

74HC595芯片

74HC595是一个8位串行输入、并行输出的位移缓存器,其中并行输出为三态输出(即 高电平、低电平和高阻抗

注意:74HC595是 先传输字节的高位后传输低位,所以需要 将字节低位移动到高位传输,在传输数据时,要注意 移位寄存器时钟和存储寄存器时钟的先后顺序,将要写入的数据先传输到74HC595寄存器中,即在准备好每位数据时要将 SRCLK进行一个上升沿变化,此时即可将数据传输到寄存器内, 待循环8次即一个字节传输到寄存器中时,就可以来一个 存储时钟上升沿,此时就可以将74HC595寄存器中的数据全部一次 传输到595端口输出。【要注意清除寄存器缓存的数据】

代码如下:

# include "reg52.h"
 
typedef unsigned int u16;
typedef unsigned char u8;
//定义74HC595控制管脚
sbit rclk=P3^5;	   //存储寄存器时钟输入
sbit srclk=P3^6;	//移位寄存器时钟输入
sbit ser=P3^4;	 //串行数据输入
 
# define LEDDZ_COL_PORT	P0 //控制点阵列的端口
u8 ghc595_buf[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
//延时函数
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}
void hc595_write_data(u8 dat)
{
	u8 i=0;
	for(i=0;i<8;i++)
	{
		ser=dat>>7;//优先传输一个字节中的高位
		dat<<=1;//将低位移动到高位
		srclk=0;
		delay_10us(1);
		srclk=1;
		delay_10us(1);//移位寄存器时钟上升沿将端口数据送入寄存器中
	}
	rclk=0;
	delay_10us(1);
	rclk=1;//存储寄存器时钟上升沿将前面写入到寄存器的数据输出
}
 
void main()
{
	u8 i=0;
	LEDDZ_COL_PORT=0x00;//全部IO口给低电平
	
	while(1)
	{
		 for(i=0;i<8;i++)
		{
			hc595_write_data(0x00);//清除上一次数据
			hc595_write_data(ghc595_buf[i]);//写入数据
			delay_10us(50000);//延时
		}
		
	}
	while(1);
}

实验现象

备注

上面是下往上移动,下面代码是右往左边(定时器)

main.c

# include<reg51.h>				 
//宏定义
# define uchar unsigned char 
# define LED P0		

//全局变量
uchar Time;

void main(void)
{
	uchar data1=0xfe;
	TMOD = 0x01; //选择工作方式1
  	TH0 = 0x3C;	 //设置初始值,定时50MS
  	TL0 = 0xB0; 
 	 EA = 1;			 //打开总中断
  	ET0 = 1;		 //打开定时器0中断
  	TR0 = 1;		 //启动定时器0
	while(1)
	{
		LED = data1;
		if(Time==10) 			//0.5s进入if内进行移位
		{
			Time = 0;
			data1 = data1<<1|0x01;	
			if(data1==0xff)data1=0xfe;
		}	
	}	
}

void Timer0() interrupt 1
{
	TH0 = 0x3C;	 //设置初始值
    TL0 = 0xB0;
	Time++;
}

想显示其他图案或者字符的话可以用 取模软件生成行的数据复制 就行了!

软件

显示数字0的代码:

# include "reg52.h"
 
typedef unsigned int u16;
typedef unsigned char u8;
//定义74HC595控制管脚
sbit rclk=P3^5;	   //存储寄存器时钟输入
sbit srclk=P3^6;	//移位寄存器时钟输入
sbit ser=P3^4;	 //串行数据输入
 
# define LEDDZ_COL_PORT	P0 //控制点阵列的端口
u8 gled_row[8]={0x00,0x7E,0x81,0x81,0x81,0x7E,0x00,0x00};//点阵行数据
u8 gled_cols[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//点阵列
//延时函数
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}
void hc595_write_data(u8 dat)
{
	u8 i=0;
	for(i=0;i<8;i++)
	{
		ser=dat>>7;
		dat<<=1;
		srclk=0;
		delay_10us(1);
		srclk=1;
		delay_10us(1);
	}
	rclk=0;
	delay_10us(1);
	rclk=1;
}
 
void main()
{
	
	u8 i=0;
	while(1)
	{
		for(i=0;i<8;i++)
		{
			LEDDZ_COL_PORT=gled_cols[i];
			hc595_write_data(gled_row[i]);
			delay_10us(100);
			hc595_write_data(0x00);
		}
	}
	while(1);
}

备注

爱心同理,改一下数组就行了

爱心的代码:

u8 gled_row[8]={0x38,0x7C,0x7E,0x3F,0x3F,0x7E,0x7C,0x38};//爱心点阵行数据

16*16点阵

4片74HC595芯片,数据传入是 第一个字节最终传到第4片,第二个字节最终传到第3片,第三个字节最终传到第2片,第四个字节最终传到第1片上。(因为第一片8位满了后再传入的就会把前面的挤出去)

  • 汉字取模:横向取模,字节倒序,字体小四
  • 16*16延时函数用: _nop_(); (需要include<intrins.h>)