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

目录

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;		// 默认状态等于0while (1){if (key1 == 0){// 发现1次低电平,有可能是按键按下,也有可能是抖动,软件消抖delay10ms();if (key1 == 0){// 10ms后还是低电平,说明真的是按键按下了,不是抖动// 这里说明发现了一个按下事件//flag = 1;if (flag == 0){AddDisplay();flag = 1;}}}else{// 电平 == 1delay10ms();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;elseled1 = 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,其余全为1if(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,其余全为1if(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,其余全为1if(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,其余全为1if(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,列全为1if(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,列全为0if(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.mzph.cn/news/643.shtml

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

相关文章

Web APIs

文章目录 1.Web APIs 和 JS 基础关联性1.1 JS 的组成 2. API 和 Web API2.1 API2.2 Web API 1.Web APIs 和 JS 基础关联性 1.1 JS 的组成 2. API 和 Web API 2.1 API **API&#xff08;Application Programming Interface,应用程序编程接口&#xff09;**是一些预先定义的函…

观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

上一节课中&#xff0c;我们学习了观察者模式的原理、实现、应用场景&#xff0c;重点介绍了不同应用场景下&#xff0c;几种不同的实现方式&#xff0c;包括&#xff1a;同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞是最经典的实现方式&#xff0c;主要是为了…

SSH框架简介篇

文章目录 概述目录结构 strutsSpringHibernate总结 概述 SSH框架&#xff08;Struts Spring Hibernate&#xff09;是一种广泛应用的Java企业级开发框架组合&#xff0c;它将Struts、Spring和Hibernate三个优秀的框架有机地结合在一起&#xff0c;提供了一套完整的解决方案&…

Linux系统编程:文件系统和inode

目录 一. 磁盘的结构和读写数据的方式 1.1 磁盘级文件和内存级文件 1.2 磁盘的物理结构 1.3 访问磁盘数据的方式 二. 磁盘文件系统 2.1 磁盘的分区管理方法 2.2 文件名和inode的关系 三. 结合文件系统对文件创建和删除的相关问题的理解 3.1 文件创建时操作系统进行的工…

51单片机--DS1302时钟

文章目录 DS1302引脚定义和应用电路内部结构框图寄存器的定义时序定义BCD码DS1302时钟代码 DS1302 DS1302是美国DALLAS公司推出的一款实时时钟电路芯片。它具有高性能和低功耗的特点&#xff0c;可以通过SPI三线接口与CPU进行同步通信。DS1302能够提供秒、分、时、日、星期、月…

【SQL应知应会】表分区(一)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分区表 • MySQL版 一、分区表1.非分区表2.分区表2…

利用集合框架实现-超市会员管理系统

借助集合框架来实现超市会员管理系统&#xff0c;实现以下功能&#xff1a; 1.开卡 2.积分累计 3.查询剩余积分 4.积分兑换 5.修改密码 6.退出 -------------------------------------------------------------------------------------------------- 展示&#x…

【信号去噪和分类】基于小波的隐马尔可夫模型统计信号处理(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

C语言实现扫雷【经典】

前言   本篇文章要实现的是扫雷游戏&#xff0c;其代码实现与上一篇的三子棋游戏类同&#xff0c;都是在棋盘的基础上&#xff0c;与电脑进行对抗&#xff0c;不同的是&#xff0c;扫雷游戏一开始电脑就已经随机布置好了所有“雷”。 请戳 --->三子棋 扫雷游戏 1. 扫雷游…

MySQL每日一练——MySQL多表查询进阶挑战

目录 1、首先创建表 t_dept: t_emp: 2、插入数据 t_dept表&#xff1a; t_tmp表: 3、修改表 4、按条件查找 1、首先创建表 t_dept: CREATE TABLE t_dept (id INT(11) NOT NULL AUTO_INCREMENT,deptName VARCHAR(30) DEFAULT NULL,address VARCHAR(40) DEFAULT NULL,P…

Python结巴中文分词笔记

&#x1f4da; jieba库基本介绍 &#x1f310; jieba库概述 Jieba是一个流行的中文分词库&#xff0c;它能够将中文文本切分成词语&#xff0c;并对每个词语进行词性标注。中文分词是自然语言处理的重要步骤之一&#xff0c;它对于文本挖掘、信息检索、情感分析等任务具有重要…

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论 我们使用Linux作为服务器操作系统时&#xff0c;为了达到高并发处理能力&#xff0c;充分利用机器性能&#xff0c;经常会进行一些内核参数的调整优化&#xff0c;但不合理的调整常常也会引起意想不到的其他问题&#x…

Elasticsearch原理剖析

一、 Elasticsearch结构 Elasticsearch集群方案由EsMaster、EsClient和EsNode1、EsNode2、EsNode3、EsNode4、EsNode5、EsNode6、EsNode7、EsNode8、EsNode9进程组成&#xff0c;如下图所示&#xff0c;模块说明如表下所示。 说明如表&#xff1a; 名称说明ClientClient使用H…

Android系统启动流程分析

当按下Android系统的开机电源按键时候&#xff0c;硬件会触发引导芯片&#xff0c;执行预定义的代码&#xff0c;然后加载引导程序(BootLoader)到RAM&#xff0c;Bootloader是Android系统起来前第一个程序&#xff0c;主要用来拉起Android系统程序&#xff0c;Android系统被拉起…

C# Linq 详解四

目录 概述 二十、SelectMany 二十一、Aggregate 二十二、DistinctBy 二十三、Reverse 二十四、SequenceEqual 二十五、Zip 二十六、SkipWhile 二十七、TakeWhile C# Linq 详解一 1.Where 2.Select 3.GroupBy 4.First / FirstOrDefault 5.Last / LastOrDefault C# Li…

排序子序列,倒置字符串讲解(图文并茂)

目录 1.排序子序列 2.倒置字符串 1.排序子序列 排序子序列_牛客笔试题_牛客网 (nowcoder.com) 首先题干中提到非递增序列和非递减序列&#xff0c;那么我们就要先弄明白什么是上述2种序列&#xff1a; 非递增序列&#xff1a;a[i] > a[i1] 如&#xff1a;3 2 1 或者 3 3 …

使用docker简单创建一个python容器

/root/docker_python目录结构&#xff1a; . |-- demo | -- main.py -- docker-compose.ymlmain.py内容&#xff1a; # codingutf-8 # -*- coding: utf-8 -*-if __name__ __main__:print("hello world")docker-compose.yml内容&#xff1a; version: "3&q…

注册中心技术Eureka、Nacos

说明&#xff1a;在微服务框架中&#xff0c;各个服务之间都是独立的。理论上来说&#xff0c;各个服务之间是可以直接通信的&#xff0c;但实际上因为服务之间通信需要管理和规划&#xff0c;如请求怎么负载均衡、请求怎么降级处理等等&#xff0c;所以就需要使用一个技术&…

Linux 常用命令

认识 Linux 目录结构 Linux 系统中&#xff0c;磁盘上的文件和目录被组成一棵目录树&#xff0c;每个节点都是目录或文件 Linux 是一个树形目录结构。Linux 上没有盘符概念&#xff0c;不分 C 盘等&#xff0c;根目录 \ 的地位相当与 Java 的 Object ——几个特殊的目录&…

uniapp快速开发小程序全流程

uniapp快速开发小程序全流程 完整项目代码&#xff1a;https://gitee.com/Zifasdfa/ziyi-app 欢迎fork与star 1 项目效果及环境准备 1.1 项目效果 本文主要使用uniapp实现一个简单的app应用 1.2 环境准备&项目初始化 ①node环境&#xff1a;去node.js官网下载稳定版的nod…