嵌入式开发学习(STC51-7-矩阵按键)

news2024/11/14 19:46:34

内容

按下S1-S16键,对应数码管最左边显示0-F

矩阵按键简介

独立按键与单片机连接时,每一个按键都需要单片机的一个I/O 口,若某单片机系统需较多按键,如果用独立按键便会占用过多的I/O口资源;而单片机
系统中I/O口资源往往比较宝贵,当用到多个按键时为了减少I/O口引脚,引入了矩阵按键;

以4*4矩阵键盘为例,键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样便一共有4行4列共8根线,我们将这8根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键;

用这种方法也可实现3行3列9个键、5行5列25个键、6行6列36个键甚至更多;

无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平;

独立键盘有一端固定为低电平,此种方式编程比较简单;而矩阵键盘两端都与单片机I/O口相连,因此在检测时需编程通过单片机I/O口送出低电平;

检测方法有多种,最常用的是行列扫描和线翻转法:

  • 行列扫描法检测时,(可以看作是每次把一列当作独立按键来检测)依次送一列为低电平,其余几列全为高电平(行全为高电平),如果检测到该列有行电平变低,即该列有按键按下,就可以确定列,然后立即检测该行哪列为低电平,则可确定行,这样我们就可确认当前被按下的键是哪一行哪一列的;当然我们也可以依次将行线置低电平(其余行列为高电平),扫描列是否有低电平;
  • 线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值;得到的行列值就是按下的按键;

矩阵键盘也少不了按键消抖的环节;

原理图

线路图
在这里插入图片描述
由线路图可知,P17-14控制行,P13-10控制列

思路

使用行列扫描法,每次把一列当作独立按键来检测,依次让每列为低电位,如果某列有行变为低电位,则该行和列即是按下的按键;

注意消抖;

编码

main.c

/*
 * @Description: 矩阵按键-按下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};

/**
 * @description: 延时函数(循环一次大约10us)
 * @param {u16} ten_us
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**
 * @description: 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
 * @return {u8} key的键值
 */
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;
}

/**
 * @description: 使用线翻转扫描方法,检测矩阵按键是否按下,按下则返回对应键值
 * @return {u8} key的键值
 */
u8 key_matrix_flip_scan(void)
{
	static u8 key_value = 0;

	KEY_MATRIX_PORT = 0x0f;		 // 给所有行赋值0,列全为1
	if (KEY_MATRIX_PORT != 0x0f) // 判断按键是否按下
	{
		delay_10us(1000); // 消抖
		if (KEY_MATRIX_PORT != 0x0f)
		{
			// 测试列
			KEY_MATRIX_PORT = 0x0f;
			switch (KEY_MATRIX_PORT) // 保存行为0,按键按下后的列值
			{
			case 0x07:
				key_value = 1;
				break;
			case 0x0b:
				key_value = 2;
				break;
			case 0x0d:
				key_value = 3;
				break;
			case 0x0e:
				key_value = 4;
				break;
			}
			// 测试行
			KEY_MATRIX_PORT = 0xf0;
			switch (KEY_MATRIX_PORT) // 保存列为0,按键按下后的键值
			{
			case 0x70:
				key_value = key_value;
				break;
			case 0xb0:
				key_value = key_value + 4;
				break;
			case 0xd0:
				key_value = key_value + 8;
				break;
			case 0xe0:
				key_value = key_value + 12;
				break;
			}
			while (KEY_MATRIX_PORT != 0xf0)
				; // 等待按键松开
		}
	}
	else
		key_value = 0;

	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段码
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:按下S1-S16键,对应数码管最左边显示0-F
在这里插入图片描述

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

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

相关文章

搜索是什么

1、什么是搜索? 搜索:计算机根据用户输入的关键词进行匹配,从已有的数据库中摘录出相关的记录反馈给用户。 常见的全网搜索引擎,有百度、谷歌这样搜索网站。 除此,搜索技术在垂直领域也有广泛的使用,比如淘…

利用awk筛选给定时间范围内的日志

文章目录 筛选给定时间范围内的日志时间时间戳什么是时间戳? 系统时间 筛选日志时间示例简单示例mktime()函数是什么 进阶示例 筛选给定时间范围内的日志 时间 时间的表示方法: 时间戳系统时间(年月日时间) 时间戳 什么是时间…

Spring Boot读取yml或者properties配置信息

文章目录 Spring Boot读取yml或者properties配置信息方法一:Value获取基本信息,适用于少量信息方法二:通过注解ConfigurationProperties(prefix "spring.datasource")方法三:通过api Environment Spring Boot读取yml或…

Leetcode-每日一题【剑指 Offer 09. 用两个栈实现队列】

题目 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 ) 示例 1: 输入: [&…

Java判断文件的系统格式编码格式

使用Java判断一个文件的系统格式(亲测可用),比如我们常见的Windows格式的文件,Unixg格式的文件,Mac格式的文件;常常有这样的场景:我们在Windows系统编写的脚步上传到Linux系统执行,执…

ClickHouse(十二):Clickhouse MergeTree系列表引擎 - MergeTree(2)

进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:含各种IT体系技术,IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

QT 使用单例模式

目录 1. 单例模式介绍 2.单例模式实现 1. 单例模式介绍 有些时候我们在做 qt 项目的时候,要用到很多类. 例如我们用到的类有 A,B,C,D. 其中,A 是 B,C,D 中都需要用到的类,A 类非常的抢手. 但是,A 类非常的占内存,定义一个 A 对象需要 500M 内存,假如在 B,C,D 中都定义一个 A 类…

解决github打不开的方法

解决github打不开的方法 本文参考文章:解决可ping通但无法访问github网站的问题 一、确定域名github.com的ip地址 进入网址 IP/服务器github.com的信息 - 站长工具 (chinaz.com),查看 ip 地址。 20.205.243.166 github.com二、确定域名github.global.…

【websocket - Tornado】简易聊天应用

1、背景 项目测试的过程中需要自己搭建一个webscoket站点,确保此类服务接入后台系统后访问不受影响。python的服务框架常用的有Flask、Django、Tornado,每个框架的侧重点不同,导致使用的场景就会有所差异。 Flask轻量级,采用常规的同步编程方式,需要安装其他模块辅助,主…

JavaEE——网络初识 (简单介绍两种协议以及网络通信的基础概念)

文章目录 一、简单了解网络发展二、网络通信基础认识三、利用UDP举例解释网络信息传输 一、简单了解网络发展 总的来讲,网络的发展史就是,先是一小部分的计算机之间连接通信,随着技术发展,逐渐扩大范围,形成了我们当前…

【Yolov5+Deepsort】训练自己的数据集(1)| 目标检测追踪 | 轨迹绘制

📢前言:本篇是关于如何使用YoloV5Deepsort训练自己的数据集,从而实现目标检测与目标追踪,并绘制出物体的运动轨迹。本章讲解的为第一个内容:简单介绍YoloV5Deepsort中所用到的目标检测,追踪及sort&Depp…

diffusion model2 扩散模型的文本信息融合、交叉注意力机制、lora

前言 在上一篇文章中,我们剖析了diffusion model的原理,而在这一篇文章中,我们探讨与扩散模型有关的其他话题,包括扩散模型的unet是如何在推理噪声的过程中,融入文本信息的考量?其原理为交叉注意力机制&am…

Kubernetes 整体架构介绍

架构图 Kubernetes 主要由以下几个核心组件组成: etcd 保存了整个集群的状态;kube-apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;kube-controller-manager 负责维护集群的状态&#xf…

【LeetCode】287. 寻找重复数

287 . 寻找重复数(中等) 方法 快慢指针 思路 要解决这道题首先要理解如何将输入的数组看作为链表。对于数组 nums 中的数字范围在 [1, n],考虑两种情况: 如果数组中没有重复的数字,以 [1, 3, 4, 2] 为例,将…

从8个新 NFT AMM,聊聊能如何为 NFT 提供流动性

DeFi 的出现,开启了数字金融民主化的革命。其中,通过 AMM 自由创建流动性池极大地增加了 ERC-20 Token 的流动性,并为一些长尾 Token 解锁了价值的发现,因而今天在链上可以看到各种丰富的交易、借贷和杠杆等活动。 而另一方面&am…

uni-app——下拉框多选

一、组件components/my-selectCheckbox.vue <template><view class"uni-stat__select"><span v-if"label" class"uni-label-text">{{label &#xff1a;}}</span><view class"uni-stat-box" :class"…

SpringBoot实现数据库读写分离

SpringBoot实现数据库读写分离 参考博客https://blog.csdn.net/qq_31708899/article/details/121577253 实现原理&#xff1a;翻看AbstractRoutingDataSource源码我们可以看到其中的targetDataSource可以维护一组目标数据源(采用map数据结构)&#xff0c;并且做了路由key与目标…

《华为认证》SR-MPLS-TE

实验需求&#xff1a;运营商网络配置SR-MPLS-TE&#xff0c;实现CE1和CE2之间的互访流量通过PE1-P2-P4-PE3。 步骤1&#xff1a;配置运营商网络的IGP协议&#xff08;本实验采用ISIS协议&#xff09; PE1&#xff1a; isis 1is-level level-2cost-style widenetwork-entity 49…

一个.NET开发的Web版Redis管理工具

今天给大家推荐一款web 版的Redis可视化工具WebRedisManager&#xff0c;即可以作为单机的web 版的Redis可视化工具来使用&#xff0c;也可以挂在服务器上多人管理使用的web 版的Redis可视化工具。 WebRedisManager基于SAEA.Socket通信框架中的SAEA.RedisSocket、SAEA.WebApi两…

Python实现决策树算法:完整源码逐行解析

决策树是一种常用的机器学习算法&#xff0c;它可以用来解决分类和回归问题。决策树的优点是易于理解和解释&#xff0c;可以处理数值和类别数据&#xff0c;可以处理缺失值和异常值&#xff0c;可以进行特征选择和剪枝等操作。决策树的缺点是容易过拟合&#xff0c;对噪声和不…