51单片机——独立按钮、矩阵按键

目录

[1. 独立按钮](#1. 独立按钮)

[1.1 概述](#1.1 概述)

[1.2. 消抖](#1.2. 消抖)

[1.3 模块电路](#1.3 模块电路)

[1.3 实验](#1.3 实验)

[1.4 思考](#1.4 思考)

[2. 矩阵按键](#2. 矩阵按键)

[2.1 概述](#2.1 概述)

[2.2 模块电路](#2.2 模块电路)

[2.3 行列扫描法](#2.3 行列扫描法)

[2.4 实验设计](#2.4 实验设计)


1. 独立按钮
1.1 概述

按钮是一种电子开关,使用时长按按钮可使开关接通,松开手时,开关断开

按键管脚两端距离长的默认是导通状态,距离短的默认是断开状态

按键按下,初始导通状态变为断开,初始断开状态变为导通

​​​​​​

1.2. 消抖

通常的按键开关为机械弹性开关

由于机械点的弹性作用,按键开关在闭合时不会立刻稳定的接通,在断开时,也不会马上断开

而是在闭合和断开的瞬间伴随着一连串的抖动,抖动时长一般在 5ms 到 10ms

按键抖动会引起按键被误读多次,为了消除影响,必须进行消抖

消抖分为物理消抖、软件消抖

为了使电路简单,一般使用软件消抖

1.3 模块电路

独立按键电路是由各个按键的一个管脚连接在一起接地,按键其他引脚分别连接到单片机 IO 口

独立按键的一端接地,另一端与单片机的 I/O 口相连

开始先给该 IO 口赋一高电平,然后让单片机不断检测该 I/O 口是否为低电平

按键闭合时,相当于该I/O口通过按键与地相连,变成低电平

程序一旦检测到I/O口变成低电平就说明按键按下,然后执行相应功能

1.3 实验

要求:K1 按下并松手,D1 点亮,K1 再次按下并松手,D1 熄灭,如此循环

cpp 复制代码
//独立按键控制灯亮灭;k1------D1
#include <at89c51RC2.h>
#include <intrins.h>

typedef unsigned int u16;
typedef unsigned char u8;

//位定义:
sbit key1 = P3^1;
sbit led = P2^0;

//延时函数
void Delay10us( )	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 25;
	while (--i);
}

void main()
{
	while(1)
	{
		if(key1==0)
		{
			Delay10us( );
			//若按键长按不松手,则在此处死循环
			while(key1==0);
			//松手才会继续往下执行
			Delay10us( );
			
			//之前代码均为消抖
			led = ~led;
		}
	}
}

可能会有人写出如下代码:效果为长按按键灯亮,松开按键灯就熄灭

cpp 复制代码
//独立按键控制灯亮灭;k1------D1
#include <at89c51RC2.h>
#include <intrins.h>

typedef unsigned int u16;
typedef unsigned char u8;

//位定义:
sbit key1 = P3^1;


sbit led = P2^0;

//延时函数
void Delay10us( )	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 25;
	while (--i);
}

void main()
{
	while(1)
	{
		if(key1==0)
		{
			Delay10us( );
			//长按点亮
			while(key1==0)
			{
				led = 0;
			}
			//松手熄灭
			if(key1==1)
			{
				Delay10us( );
				led = 1;
			}
		}
	}
}

一般使用独立按键优先选松手后控制,稳定且易实现

除非有特殊需求才会选长按控制,需额外处理时长检测

无论哪种方式,都必须做按键消抖,否则会出现 LED 乱跳的问题

1.4 思考

通过实验:独立按键一端接地,另一端也要输出低电平,才可以控制LED灯点亮,

这与思维惯性中电器元件一端输入高电平,另一端输入低电平的现象不符,且与 LED灯变亮有什么关系呢?

按键的作用是 "拉低 IO 口电平"------ 未按时 IO 口通过上拉电阻为高,按下时 IO 口接地为低,形成 "高→低" 的电平变化,这是控制的触发信号,通过检测 IO 口的电平变化(结合消抖),判断按键的 "按下 / 松手 / 长按" 状态,再通过翻转或直接控制 LED 对应的 IO 口电平,实现亮灭;

这与前面(1.3部分)说的"

按键闭合时,相当于该I/O口通过按键与地相连,变成低电平

程序一旦检测到I/O口变成低电平就说明按键按下,然后执行相应功能

"不谋而合

2. 矩阵按键
2.1 概述

独立按键与单片机连接时,每一个按键都需要单片机的一个 I/O 口

若单片机需要更多按键,使用独立按键会占用更多的I/O口资源

此时,为减少I/O口引脚,引入了矩阵按键

2.2 模块电路

无论是独立按键还是矩阵按键,单片机检测按键是否被按下的的依据相同:

检测该键对应的I/O口是否为低电平

独立按键一端固定为低电平

而矩阵按键两端都与单片机 I/O 连接,因此检测时须让按键两端 I/O口都送出低电平

2.3 行列扫描法

检测按键两端I/O口是否为低电平的方法有:行列扫描法、线翻转法

行列扫描法:扫描原理:逐列拉低,检测行电平

矩阵按键无法直接读取哪个按键按下,需通过 "扫描" 确定位置:

  1. 先将 某一列拉低(输出低电平),其他列保持高电平;
  2. 检测所有行线的电平:如果某行线为低电平(因按键按下,行 - 列导通,行线被列线拉低),则该 "列 - 行" 交叉点的按键被按下;
  3. 逐列重复上述步骤,完成整个矩阵的扫描。
2.4 实验设计

通过数码管显示按下矩阵按键 S1-S16 后对应的 0-F

cpp 复制代码
/*
矩阵按键控制 LED 数码管:矩阵按键扫描------获取按键值------数码管显示对应内容
准备工作:头文件、类型定义、硬件配置(段选表、延时函数)
核心功能:矩阵按键扫描获取按键值
主循环:  调用扫描函数------>数码管显示对应按键值
*/
//头文件:
#include <at89c51RC2.h>//51单片机寄存器定义头文件
#include <intrins.h>   //用于延时函数

//类型定义:简化代码书写
typedef unsigned char u8;
typedef unsigned int u16;

//共阴极数码管对应段选表:1---F、小数点
u8 gsmg[] = {0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
    0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x80};

//延时函数:按键消抖(机械按键的触电有 5-20ms 抖动)
void Delay1ms(u8 ms)	//@11.0592MHz
{
	unsigned char data i, j;

	while(ms--)
	{
		_nop_();
		_nop_();
		_nop_();
		i = 11;
		j = 190;
		do
		{
			while (--j);
		} while (--i);
	}
}

//定义变量:存储按键值,0表示无按键按下
u8 key_value = 0;

//矩阵按键扫描函数(P1口,高四位:行;低四位:列)
u8 menu()
{
	
		//第一列扫描,其余列为高电平
		P1 = 0xf7;//1111 0111:高四位对应行,低四位对应列,第一列即P13,对应低四位中最高位
		if(P1 != 0xf7)	//检测到第一列有按键被按下(只看第一列)
		{
			Delay1ms(5); //消抖
			switch(P1)
			{
				case 0x77:key_value = 1;break;
				case 0xb7:key_value = 5;break;
				case 0xd7:key_value = 9;break;
				case 0xe7:key_value = 13;break;
			}
			//等待按键释放:若按键长按,则程序一直卡在while死循环中,不向下执行
			while(P1 != 0xf7);
			Delay1ms(5); //消抖
		}
		
		
		P1 = 0xfb;
		if(P1 != 0xfb)	
		{
			Delay1ms(5); //消抖
			switch(P1)
			{
				case 0x7b:key_value = 2;break;
				case 0xbb:key_value = 6;break;
				case 0xdb:key_value = 10;break;
				case 0xeb:key_value = 14;break;
			}
			//等待按键释放
			while(P1 != 0xfb);
			Delay1ms(5); //消抖
		}
			

		
		//第三列
		P1 = 0xfd;  
		if(P1 != 0xfd)	
		{
			Delay1ms(5); //消抖
			switch(P1)
			{
				case 0x7d:key_value = 3;break;
				case 0xbd:key_value = 7;break;
				case 0xdd:key_value = 11;break;
				case 0xed:key_value = 15;break;
			}
			//等待按键释放
			while(P1 != 0xfd);
			Delay1ms(5); //消抖
		}
		
		
		//第四列
		P1 = 0xfe;  
		if(P1 != 0xfe)	
		{
			Delay1ms(5); //消抖
			switch(P1)
			{
				case 0x7e:key_value = 4;break;
				case 0xbe:key_value = 8;break;
				case 0xde:key_value = 12;break;
				case 0xee:key_value = 16;break;
			}
			//等待按键释放
			while(P1 != 0xfe);
			Delay1ms(5); //消抖
		}
		
	//返回按键值:0:无按键按下;0-F:返回对应按键值	
	return key_value;
}

//优化:只扫描一次menu()函数,节省资源
//  u8 ke = menu();  错误:在main()外部定义,属于全局变量,全局变量只能由常量初始化

void main()
{
//	原程序
//	while(1)
//	{	
//		if(menu()!=0)//检测到有按键按下
//		{
//			P0 = gsmg[menu()-1] ;
//		}
//	}
 
//	优化后
	while(1)
	{
		//一次循环只扫描一次,结果存入ke变量,后续直接使用,效率更高,资源占用更低,运行更稳定。
		u8 ke = menu();
		if(ke!=0)
		{
			P0 = gsmg[ke-1] ;
		}
	}
}
相关推荐
云山工作室1 小时前
多传感器融合的办公室智能门禁系统(论文+源码)
stm32·单片机·嵌入式硬件·物联网·毕业设计·课程设计
S***q1922 小时前
JavaScriptWebSocket案例
51单片机·3dsmax·taro
天天爱吃肉82183 小时前
智能网联汽车信息安全深度解析:从UN-R155与GB44495标准到OBD/UDS技术实践
网络·嵌入式硬件·汽车
小曹要微笑6 小时前
STM32H7系列全面解析:嵌入式性能的巅峰之作
c语言·stm32·单片机·嵌入式硬件·算法
小曹要微笑6 小时前
STM32F103ZET6 全面详解
单片机·嵌入式硬件
Python小老六12 小时前
STM32 Flash:扇区、页、块
stm32·单片机·嵌入式硬件
普中科技15 小时前
【普中DSP28335开发攻略】-- 第 16 章 定时器中断实验
单片机·嵌入式硬件·定时器·dsp28335·普中科技
雅欣鱼子酱15 小时前
电流检测的电路设计与选型——分流电阻法
stm32·单片机·嵌入式硬件·芯片·电子元器件·电流检测芯片
DIY机器人工房16 小时前
嵌入式面试题:看你学习了自动控制原理这门课,讲一下欠驱动系统?
stm32·单片机·学习·嵌入式·自动控制原理