单片机第一季:零基础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.hqwc.cn/news/23009.html

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

相关文章

【监控系统】Prometheus监控组件Mysql-Exporter配置实战

Mysql-Exporter主要监控Mysql数据库的稳定性、吞吐量、连接情况、缓冲池使用情况、查询性能等各项指标&#xff0c;是我们压测时常常需要监控的一些指标。 目前&#xff0c;Exporter 支持高于5.6版本的 MySQL 和高于10.1版本的 MariaDB。在 MySQL/MariaDB 低于5.6版本时&#…

Jmeter接口关联(四)【使用正则表达式提取值】关联上下接口实际当中的运用

文章目录 前言一、Jmeter中正则表达式的运用&#xff08;使用案例讲解&#xff09;注意 前言 这篇主要是用一个实际的例子讲解正则表达式在2个有关联的接口中运用。 天气预报接口&#xff1a; 请求Key&#xff1a;79a35e2a5997710f3bdc86de81f21dbb 根据城市查询天气 接口地…

WSL2 忘记用户密码

步骤一&#xff1a;将默认用户切换为root 在Windows里启动命令提示符&#xff0c;输入&#xff1a; ubuntu2004 config --default-user root这就已经将我的ubuntu20.04的默认用户切换为了root&#xff0c; 不同的WSL版本可能命令的第一个符号不一样&#xff0c;区别如下图&am…

网络安全设备Bypass功能介绍及分析

网络安全平台厂商往往需要用到一项比较特殊的技术&#xff0c;那就是Bypass&#xff0c;那么到底什么是Bypass呢&#xff0c;Bypass设备又是如何来实现的&#xff1f;下面我就对Bypass技术做一下简单的介绍和说明。 一、 什么是Bypass。 大家知道&#xff0c;网络安全设备一般…

K8s为什么需要calico? calico 原理深入理解.

文章目录 为什么需要calico&#xff1f;-网络插件”千千万”&#xff0c;为何k8s要用calicocalico的架构calico Pod 跨node通信tunl0 的作用&#xff1f;为什么所有pod的默认网关都是169.254.1.1 &#xff1f;什么是ARP 代理&#xff1f;jksj BGP模式的calico工作原理calico BG…

《深度学习推荐系统》笔记

目录 一、推荐系统是什么1.作用和意义2.推荐系统的架构2.1 逻辑架构2.2 技术架构 二、传统的推荐系统方法1. 协同过滤算法1.1 userCF&&ItemCF1.3 矩阵分解算法 2. 逻辑回归算法3. 因子分解机3.1 POLY2模型3.2 FM模型3.3 FFM模型3.4 小结 4. 组合模型4.1 GBDTLR组合模型…

MySQL 事务与存储引擎

目录 一、MySQL事务的概念 二、事务的ACID特点 2.1 原子性 2.2 一致性 2.3 隔离性 2.4 持久性 2.5 事务之间的相互影响 三、Mysql及事物隔离级别 3.1 查询全局事务隔离级别 3.2 查询会话事务隔离级别 3.3 设置全局事务隔离级别 3.4 设置会话事务隔离级别 …

剑指oferr68-II.二叉树的最近公共祖先

为什么这道题的难度是easy&#xff0c;我感觉挺难的啊&#xff0c;我想了挺久没有一点思路就直接看题解了。题解有两种解法&#xff0c;先看第一种存储父节点 class Solution {Map<Integer,TreeNode> parent new HashMap<Integer,TreeNode>();Set<Integer>…

14.live555mediaserver-play请求与响应

live555工程代码路径 live555工程在我的gitee下&#xff08;doc下有思维导图、drawio图&#xff09;&#xff1a; live555 https://gitee.com/lure_ai/live555/tree/master 章节目录链接 0.前言——章节目录链接与为何要写这个&#xff1f; https://blog.csdn.net/yhb1206/art…

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;**是一些预先定义的函…

关于cip.cc查IP出口地址的工具到底准确不准确?

关于 cip.cc 或其他在线IP查询工具的准确性&#xff0c; 首先来看2张图片&#xff0c;分别如下 实际情况就是作者在杭州&#xff0c;使用的网络出口地址应该是百度的&#xff0c;而不是cip.cc所显示的地址。 所以结论是cip.cc并不靠谱&#xff0c;我又查阅了相关资料如下 1.…

2023-07-14:讲一讲Kafka与RocketMQ中存储设计的异同?

2023-07-14&#xff1a;讲一讲Kafka与RocketMQ中存储设计的异同&#xff1f; 答案2023-07-14&#xff1a; 在Kafka中&#xff0c;文件的布局采用了Topic/Partition的方式&#xff0c;每个分区对应一个物理文件夹&#xff0c;且在分区文件级别上实现了顺序写入。然而&#xff0…