单片机第一季:零基础6——按键

news2024/11/17 11:31:31

目录

1,独立按键

2,矩阵按键


(注意:文章中的代码仅供参考学习,实际使用时要根据需要修改)

1,独立按键

按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态, 如果按键按下,初始导通状态变为断开,初始断开状态变为导通。

由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为5ms 到10ms。按键稳定闭合时间的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起按键被误读多次。为了确保CPU 对按键的一次闭合仅作一次处理,必须进行消抖。

按键消抖有两种方式,一种是硬件消抖,另一种是软件消抖。为了使电路更加简单,通常采用软件消抖。我们开发板也是采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状态,如果得到按键按下之后,延时10ms,再次读取按键的状态,如果按键还是按下状态,那么说明按键已经按下。

单片机常用的软件去抖动方法:

1,先设置IO 口为高电平(由于开发板IO 都有上拉电阻,所以默认IO 为高电平)。

2,读取IO 口电平确认是否有按键按下。

3,如有IO 电平为低电平后,延时几个毫秒。

4,再读取该IO 电平,如果仍然为低电平,说明按键按下。

5,执行按键控制程序。

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

我们知道单片机的IO 口既可作为输出也可作为输入使用,当检测按键时用的是它的输入功能,独立按键的一端接地, 另一端与单片机的I/O 口相连,开始时先给该IO 口赋一高电平,然后让单片机不断地检测该I/O 口是否变为低电平,当按键闭合时,即相当于该I/O 口通过按键与地相连,变成低电平,程序一旦检测到I/O 口变为低电平则说明按键被按下,然后执行相应的指令。

 CPU如何处理按键
(1)轮询式。所谓轮询式就是CPU不断的隔很小时间去查看有没有按键被按下,如果按下就处理按键,如果没按下就过一会再来查看。(按键什么时候被按下CPU是无法预知的)
(2)中断式

轮询式处理按键代码:

#include <reg51.h>

// 当前要处理的是K1,对应P1.0IO口,操控的LED是LED1,对应P0.0

/*********************变量定义************************************/
sbit key1 = P1^0;
sbit key2 = P1^1;
sbit key8 = P1^7;



// 独立数码管的段码表
unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};



/*********************************** 函数声明 ***********************/
void AddDisplay(void);
void delay(void);
void delay10ms(void);

/******************************全局变量定义*************************/
unsigned char dnum = 0;

void main(void)
{
	unsigned char flag = 0;		// 默认状态等于0

	while (1)
	{
		if (key1 == 0)
		{
			// 发现1次低电平,有可能是按键按下,也有可能是抖动,软件消抖
			delay10ms();
			if (key1 == 0)
			{
				 // 10ms后还是低电平,说明真的是按键按下了,不是抖动
				 // 这里说明发现了一个按下事件
				//flag = 1;
				if (flag == 0)
				{
					AddDisplay();
					flag = 1;
				}
			}
		}
		else
		{
			// 电平 == 1
			delay10ms();
			if (key1 == 1)
			{
				// 说明弹起了
				if (flag == 1)
				{
					//AddDisplay();
					flag = 0;
				}
			}
		}

		delay(); 
	}
}


// 该函数将num数字送到独立数码管去显示
void AddDisplay(void)
{

	dnum = dnum + 1;
	if (dnum > 15)
	{
		dnum = 0;
	}

	P0 = val[dnum];
}

// 延时函数
void delay(void)
{
	unsigned char i, j;

	for (i=0; i<100; i++)
		for (j=0; j<200; j++);
}

void delay10ms(void)   //误差 0us
{
    unsigned char a,b,c;
    for(c=5;c>0;c--)
        for(b=4;b>0;b--)
            for(a=248;a>0;a--);
}

中断式处理按键源代码:

#include <reg51.h>

// 当前要处理的是K1,对应P1.0IO口,操控的LED是LED1,对应P0.0

/*********************变量定义************************************/
sbit key1 = P1^0;
sbit key2 = P1^1;

sbit key4 = P3^4;

sbit led1 = P2^7;
sbit led2 = P2^6;


// 独立数码管的段码表
unsigned char val[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};



/*********************************** 函数声明 ***********************/
void delay10ms(void);

/*************中断处理程序*************/

void Eint0Isr() interrupt 0
{
    // 任务2:按键监测及led控制
	// 进入中断后LED状态转换。原来亮则灭,原来灭就亮
	if (led1 == 1)
		led1 = 0;
	else
		led1 = 1;
}


void main(void)
{
	unsigned char i = 0;

	// 中断初始化
	IT0 = 1;
	EX0 = 1;
	EA = 1;

	// 主线任务
	while (1)
	{
		// 任务1:数码管显示
		for (i=0; i<16; i++)
		{
			P0 = val[i];
			delay10ms();
		}
	}
}



// 延时函数
void delay10ms(void)   //误差 0us
{
    unsigned char a,b,c;
    for(c=5;c>0;c--)
        for(b=4;b>0;b--)
            for(a=248;a>0;a--);
}

上述代码中主线任务是使用数码显示0-F,利用中断功能去检测按键是否按下,当检测到按键按下时中断主线任务去执行中断程序点亮或熄灭LED灯。

中断的思路:
(1)“主线任务”为常规任务,默认运行;
(2)中断发生后CPU暂停主线任务转去处理中断任务,完成后再回来接着执行主线任务;

中断的意义:
(1)中断处理能力让CPU可以全力处理主线任务而不用担心会错过中断任务(举例:看电影和收快递);
(2)中断式比轮询式更适合处理异步事件,效率更高;
(3)中断中处理的事件的特点是:无法预料、处理时间短、响应要求急。

中断任务的处理时间要远远小于主线程序的处理时间才能通过中断来处理。

IT0:这一位用来设置中断的触发模式:下降沿触发(Falling)或者低电平触发(low level);
EX0:这一位是INT0的开关。如果EX0等于0则外部中断在单片机内部被关闭,此时CPU无法收到INT0的中断信息所以不会处理INT0,如果需要使用INT0就一定要设置为1。
EA:是全局的中断开关。EA如果关掉则整个CPU不能响应中断,所有中断都被关了。光EA打开也不一定能响应中断,还得具体的中断开关打开才行。

总结:
(1)中断能力是CPU本身设计时支持的,并不是编程制造出来的;
(2)程序员只要负责2件事即可:主程序中初始化中断、定义中断处理程序;
(3)当中断条件发生时,硬件会自动检测到并且通知CPU,CPU会自动去执行中断处理程序,这一切都是CPU设计时定下的,不需要编程干预。

2,矩阵按键

电路中的ARRAY_H1表示矩阵键盘第1 行,ARRAY_L1 表示矩阵键盘第1 列。

需要注意的是,按键两端如果分别接单片机的两个端口,一个端口置高电平,一个端口置低电平,按下按键时会把高电平的端口拉低,端口置低可以认为是在单片机内部将这个端口接地。

矩阵按键实验程序,按下矩阵按键显示对应的0-F:

第一种方法:

/********************************************************************
******************
实验名称:矩阵按键实验
接线说明:
实验现象:下载程序后,按下“矩阵按键”模块中S1-S16 键,对应静态数码管显示0-F
注意事项:
*********************************************************************
******************/
#include "reg52.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
#define KEY_MATRIX_PORT P1 //使用宏定义矩阵按键控制口
#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口
//共阴极数码管显示0~F 的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
/********************************************************************
***********
* 函数名: delay_10us
* 函数功能: 延时函数,ten_us=1 时,大约延时10us
* 输入: ten_us
* 输出: 无
*********************************************************************
**********/
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}
/********************************************************************
***********
* 函数名: key_matrix_ranks_scan
* 函数功能: 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输入: 无
* 输出: key_value:1-16,对应S1-S16 键,0:按键未按下
*********************************************************************
**********/
u8 key_matrix_ranks_scan(void)
{
	u8 key_value=0;
	KEY_MATRIX_PORT=0xf7;//给第一列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xf7)//判断第一列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值
		{
			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(KEY_MATRIX_PORT!=0xf7);//等待按键松开

	KEY_MATRIX_PORT=0xfb;//给第二列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfb)//判断第二列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值
		{
			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(KEY_MATRIX_PORT!=0xfb);//等待按键松开

	KEY_MATRIX_PORT=0xfd;//给第三列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfd)//判断第三列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值
	{
		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(KEY_MATRIX_PORT!=0xfd);//等待按键松开

	KEY_MATRIX_PORT=0xfe;//给第四列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfe)//判断第四列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值
		{
			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(KEY_MATRIX_PORT!=0xfe);//等待按键松开
	return key_value;
}


void main()
{
	u8 key=0;
	while(1)
	{
		key=key_matrix_ranks_scan();
		if(key!=0)
		SMG_A_DP_PORT=~gsmg_code[key-1];//得到的按键值减1 换算成数组下标对应0-F 段码
	}
}

第二种方法:

#include <reg51.h>

typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;

#define KEY_MATRIX_PORT P1 //使用宏定义矩阵按键控制口

#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口

//共阳极数码管显示0~F 的段码数据
u8 gsmg_code[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}

u8 key_matrix_flip_scan(void)
{
   	static u8 key_value=0;

   	KEY_MATRIX_PORT=0xf0;//给所有行赋值0,列全为1
	if(KEY_MATRIX_PORT != 0xf0)
	{
	   delay_10us(1000);
	   if(KEY_MATRIX_PORT != 0xf0)
	   {
	   	   switch(KEY_MATRIX_PORT)
		   {
			   case 0xe0: key_value = 1;break;
			   case 0xd0: key_value = 2;break;
			   case 0xb0: key_value = 3;break;
			   case 0x70: key_value = 4;break;
		   }
	   }

	
		KEY_MATRIX_PORT=0x0f;//给所有行赋值1,列全为0
		if(KEY_MATRIX_PORT != 0x0f)
		{
		   delay_10us(1000);
		   if(KEY_MATRIX_PORT != 0x0f)
		   {
		   	  switch(KEY_MATRIX_PORT)
			 {
				  case 0x0e: key_value = key_value;break;	 //代表第一行的按键
				  case 0x0d: key_value = key_value+4;break;  //代表第二行的按键
				  case 0x0b: key_value = key_value+8;break;	 //代表第三行的按键
				  case 0x07: key_value = key_value+12;break; //代表第四行的按键 
			 }
		   }
		   while(KEY_MATRIX_PORT!=0x0f);//等待按键松开

	
	}

	   else
	   {
	   		key_value = 0;	
	   }

	}

	return 	key_value;

}

void main()
{
	u8 key = 0;

   while(1)
   {
   		key = key_matrix_flip_scan();

		if (key != 0)
		{
			SMG_A_DP_PORT =  gsmg_code[key-1];
		}
   }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/757008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用PuTTY作为telnet客户端

PuTTY工具可以作为telnet客户端使用。 打开PuTTY&#xff0c;输入telnet服务器的ip地址、端口号&#xff0c;选择Other&#xff0c;然后点击Open&#xff0c;就可以了&#xff1a;

C# Modbus通信从入门到精通(2)——Modbus RTU协议原理

Modbus RTU是串行链路上的协议,也就是说Modbus RTU是通过串口通信来实现的,它可以通过RS232、RS485物理层的接口来实现,同时它也是一个主从协议,在同一时间总线上只能有一个主站和一个或多个(最多247)个从站。Modbus通信总是由主站发起,从站没有接收到主站的请求时不会发…

【Maven三】——maven生命周期和插件

系列文章目录 Maven之POM介绍 maven命令上传jar包到nexus 【Maven二】——maven仓库 maven生命周期和插件 系列文章目录前言一、什么是生命周期&why1.三套生命周期2.clean生命周期3.default生命周期4.site生命周期5.命令行与生命周期 二、插件目标三、插件绑定1.内置绑定2…

大华智慧园区综合管理平台RCE漏洞复现(0day)

0x01 产品简介 “大华智慧园区综合管理平台”是一款综合管理平台&#xff0c;具备园区运营、资源调配和智能服务等功能。平台意在协助优化园区资源分配&#xff0c;满足多元化的管理需求&#xff0c;同时通过提供智能服务&#xff0c;增强使用体验。 0x02 漏洞概述 大华智慧园…

基于PyTorch实战权重衰减——L2范数正则化方法(附代码)

文章目录 0. 前言1. 权重衰减方法作用2. 权重衰减方法原理介绍3. 验证权重衰减法实例说明3.1 训练数据样本3.2 网络模型3.3 损失函数3.4 训练参数 4. 结果对比5. 源码 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人…

Java 设计模式——迭代器模式

目录 1.概述2.结构3.案例实现3.1.抽象迭代器3.2.具体迭代器3.3.抽象聚合3.4.具体聚合3.5.测试 4.优缺点5.使用场景6.JDK 源码解析——Iterator 1.概述 迭代器模式 (Iterator Pattern) 是一种行为型设计模式&#xff0c;它提供一种顺序访问聚合对象&#xff08;如列表、集合等&…

JVM学习之内存与垃圾回收篇1

文章目录 1 JVM与Java体系结构1.0 Java发展重大事件1.1 虚拟机和Java虚拟机1.3 JVM整体结构1.4 Java代码执行流程1.5 JVM架构模型1.6 JVM的生命周期1.7 JVM发展历程 2 类加载子系统2.1 ClassLoader2.2 用户自定义类加载器2.2.1 为什么需要自定义类加载器2.2.2 自定义类加载器的…

【框架篇】对象注入的三种实现方式

对象注入的实现 一&#xff0c;实现方式的使用 对象注入也可被称为对象装配&#xff0c;是把Bean对象获取出来放到某个类中。 对象注入的实现方式有3种&#xff0c;分别为属性注入&#xff0c;Setter注入和构造方法注入。 为了更好地理解对象注入的实现方式&#xff0c;搞个…

24 MFC文档串行化和单文档应用程序

文章目录 文档串行化全部代码 单文档应用程序搭建原理搭建框架Win32 过度到MFC 三部曲设置ID资源全部代码 单文档应用程序设置标题绘图 简单的管理系统部分代码 文档串行化 ui 设计 保存 void CfileDemoDlg::OnBnClickedBtnSave() {UpdateData();//CFile file(L"Demo.dat…

python+pytest接口自动化(9)-cookie绕过登录(保持登录状态)

目录 cookie工作原理 cookie绕过登录 总结 在编写接口自动化测试用例或其他脚本的过程中&#xff0c;经常会遇到需要绕过用户名/密码或验证码登录&#xff0c;去请求接口的情况&#xff0c;一是因为有时验证码会比较复杂&#xff0c;比如有些图形验证码&#xff0c;难以通过…

旅游信息推荐系统带文档springboot+vue

功能 用户注册和登录&#xff1a;用户可以注册一个账户并登录到系统中。旅游项目展示&#xff1a;系统展示各种旅游项目的信息&#xff0c;包括目的地、行程、费用等。旅游项目搜索和筛选&#xff1a;用户可以搜索和筛选旅游项目&#xff0c;根据目的地、日期、费用等条件。预…

Linux系统终端窗口ctrl+c,ctrl+z,ctrl+d的区别

时常在Linux系统上&#xff0c;执行某命令停不下来&#xff0c;就这几个ctrl组合键按来按去&#xff0c;今天稍微总结下具体差别&#xff0c;便于以后linux系统运维操作 1、ctrlc强制中断程序&#xff0c;相应进程会被杀死&#xff0c;中断进程任务无法恢复执行 2、ctrlz暂停正…

mongodb集群搭建

下载地址&#xff1a; https://www.mongodb.com/try/download/community下载mongodb-linux-x86_64-rhel70-5.0.18 搭建集群 tar -zxvf mongodb-linux-x86_64-rhel70-5.0.18.tgz mkdir -p data/dp cd mongodb-linux-x86_64-rhel70-5.0.18 mkdir -p data/db mkdir log mkdir c…

Ubuntu 23.04安装最新版本Halcon 23.05

Ubuntu 23.04安装最新版本Halcon 23.05 官网下载安装环境变量设置创建快捷方式给个最新ubuntu的镜像源地址 官网下载 去Halcon官网&#xff1a;https://www.mvtec.com/products/halcon/&#xff0c;注册或登录&#xff0c;点击Download&#xff1a; 或者进入大恒网站&#xf…

Ubuntu最新版本23.05配置Flameshot(途中解决疑难杂症)

Ubuntu最新版本23.05配置Flameshot截图软件 安装方法&#xff1a;添加Ubuntu的快捷键遇到的问题解决 安装方法&#xff1a; sudo apt install flameshot出现该页面表示成功&#xff1a; 可以直接在终端输入&#xff1a;flameshot gui flameshot gui进行截图。 添加Ubuntu的…

云计算与大数据——MPI集群配置

什么是MPI集群&#xff1f; MPI&#xff08;消息传递接口&#xff09;是一种用于编写并行程序的标准&#xff0c;它允许在多个计算节点上进行通信和协作。MPI集群配置是指在一个或多个计算节点上设置MPI环境以实现并行计算。 MPI集群配置的步骤&#xff1a; 硬件选型&#x…

三菱PLC上位机测试

利用三菱的MX Component与三菱PLC进行以太网通信&#xff0c;我们可以用官方的dll编写C#代码&#xff0c;特别简单&#xff0c;最后附上整个源码下载。 1. 安装MX Component&#xff08;必须&#xff09;和GX WORKS3&#xff08;主要是仿真用&#xff0c;实际可以不装&#xf…

空间光通信-调制解调滤波与同步

图文并茂&#xff0c;讲解电磁波传播原理_哔哩哔哩_bilibili 深入浅出空间光通信-3.调制解调滤波与同步_哔哩哔哩_bilibili 傅里叶变换这样学&#xff0c;何愁不会呢&#xff1f;直观理解傅里叶变换_哔哩哔哩_bilibili 第二十三课&#xff1a;声音编辑必看&#xff01;&…

【六袆 - windows】windows计划任务,命令行执行,开启计划任务,关闭计划任务,查询计划任务

windows计划任务 查看 Windows 自动执行的指令取消 Windows 中的计划任务启动执行计划任务 查看 Windows 自动执行的指令 您可以使用以下方法&#xff1a; 使用任务计划程序&#xff1a;任务计划程序是 Windows 内置的工具&#xff0c;可以用于创建、编辑和管理计划任务。您可…

pytest+allure运行出现乱码的解决方法

pytestallure运行出现乱码的解决方法 报错截图&#xff1a; 这是因为没有安装allure运行环境或者没有配置allure的环境变量导致&#xff0c;解决方案&#xff1a; 1.安装allure运行环境 官方下载地址&#xff1a;https://github.com/allure-framework/allure2/releases 百度…