【51单片机】5. 矩阵键盘与矩阵键盘密码锁Demo

1. 矩阵键盘原理

在这里插入图片描述

通过矩阵连接的模式,原本需要16个引脚连接的按钮只需要8个引脚就能连接好,减少了I/O口的占用。

矩阵按钮是通过扫描来读取状态的。

2. 扫描的概念

输出扫描示例:数码管扫描

原理:显示第1位→显示第2位→显示第2位→…,快速重复此过程实现所有数码管同时显示的效果

输入扫描示例:矩阵键盘扫描

原理:读取第1行(列)→读取第2行(列)→读取第3行(列)→…,快速循环此过程,最终实现所有按键同时检测的效果

这种循环扫描的优势在于节省I/O口

3. Templates的定义

Template实际上就是双击出来指定的内容,比如平时写.h文件的时候,总是需要反复写如下开头结尾:

#ifndef __XXX_H__
#define __XXX_H__#endif

会比较麻烦,通过Template可以将这部分代码定义存储,再使用的时候双击即可,定义Template步骤如下:

第一步:点击下方的【Templates】→【右键】→【Configure Templates】

在这里插入图片描述

第二步:新建,起一个好识别的名字,写入常用的部分作为Text

在这里插入图片描述

第三步:在希望光标落入的地方打一个|,最终Text部分输入如下:

#ifndef __|_H__
#define#endif

在文件中双击,就会出现如图所示的结果:

在这里插入图片描述

4. 矩阵键盘扫描方式1

建立了MatrixKeyboard.cMatrixKeyboard.h,里面写扫描的代码

需要先理解矩阵键盘的按键按下是怎么被扫描到的,我这里直接复制了deepseek的回答:

在矩阵键盘中,检测按钮被按下的正确原理是:

  1. 行线(输出端)需要主动置低
  2. 列线(输入端)被按键下拉为低
  3. 检测时需满足:行线输出低电平 + 列线检测到低电平

基于此,我们需要给行线置低(根据上面的电路图图示,行线分别是P17,P16,P15,P14),同时检测列线(P13,P12,P11,P10)。

我在MatrixKeyboard.c中自己写了一个不带消抖的按行扫描:

#include <REGX52.H>
#include "Delay.h"// 按行扫描
unsigned char MatrixKeyboardScan()
{unsigned char keyNum = 0;P1 = 0xFF;P1_7 = 0;if (P1_3 == 0) keyNum = 1;if (P1_2 == 0) keyNum = 2;if (P1_1 == 0) keyNum = 3;if (P1_0 == 0) keyNum = 4;P1 = 0xFF;P1_6 = 0;if (P1_3 == 0) keyNum = 5;if (P1_2 == 0) keyNum = 6;if (P1_1 == 0) keyNum = 7;if (P1_0 == 0) keyNum = 8;P1 = 0xFF;P1_5 = 0;if (P1_3 == 0) keyNum = 9;if (P1_2 == 0) keyNum = 10;if (P1_1 == 0) keyNum = 11;if (P1_0 == 0) keyNum = 12;P1 = 0xFF;P1_4 = 0;if (P1_3 == 0) keyNum = 13;if (P1_2 == 0) keyNum = 14;if (P1_1 == 0) keyNum = 15;if (P1_0 == 0) keyNum = 16;return keyNum;
}

MartixKeyboard.h如下:

#ifndef __MATRIXKEYBOARD_H__
#define __MATRIXKEYBOARD_H__unsigned char MatrixKeyboardScan();#endif

为了验证不带消抖按行扫描存在的问题,我写了如下一段主函数:

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKeyboard.h"void main()
{unsigned int myCount = 0;unsigned char keyboardNum = 0;LCD_Init();// LCD_ShowNum(2,1,keyboardNum,2);while (1){keyboardNum = MatrixKeyboardScan();if (keyboardNum){myCount++;LCD_ShowNum(1,1,keyboardNum,2);LCD_ShowNum(2,1,myCount,3);}}
}

此时,LCD第一行显示按下的按键,第二行显示受抖动电压影响,LCD_ShowNum语句执行的次数:

在这里插入图片描述

可以看到,不稳的电压起码抖动了11次,从而11次执行了LCD_ShowNum语句,为此,消抖语句的存在还是很必要的,故修改如下:

#include <REGX52.H>
#include "Delay.h"// 按行扫描
unsigned char MatrixKeyboardScan()
{unsigned char keyNum = 0;P1 = 0xFF;P1_7 = 0;if (P1_3 == 0) {Delay(20); while(P1_3 == 0) ; Delay(20); keyNum = 1;}if (P1_2 == 0) {Delay(20); while(P1_2 == 0) ; Delay(20); keyNum = 2;}if (P1_1 == 0) {Delay(20); while(P1_1 == 0) ; Delay(20); keyNum = 3;}if (P1_0 == 0) {Delay(20); while(P1_0 == 0) ; Delay(20); keyNum = 4;}P1 = 0xFF;P1_6 = 0;if (P1_3 == 0) {Delay(20); while(P1_3 == 0) ; Delay(20); keyNum = 5;}if (P1_2 == 0) {Delay(20); while(P1_2 == 0) ; Delay(20); keyNum = 6;}if (P1_1 == 0) {Delay(20); while(P1_1 == 0) ; Delay(20); keyNum = 7;}if (P1_0 == 0) {Delay(20); while(P1_0 == 0) ; Delay(20); keyNum = 8;}P1 = 0xFF;P1_5 = 0;if (P1_3 == 0) {Delay(20); while(P1_3 == 0) ; Delay(20); keyNum = 9;}if (P1_2 == 0) {Delay(20); while(P1_2 == 0) ; Delay(20); keyNum = 10;}if (P1_1 == 0) {Delay(20); while(P1_1 == 0) ; Delay(20); keyNum = 11;}if (P1_0 == 0) {Delay(20); while(P1_0 == 0) ; Delay(20); keyNum = 12;}P1 = 0xFF;P1_4 = 0;if (P1_3 == 0) {Delay(20); while(P1_3 == 0) ; Delay(20); keyNum = 13;}if (P1_2 == 0) {Delay(20); while(P1_2 == 0) ; Delay(20); keyNum = 14;}if (P1_1 == 0) {Delay(20); while(P1_1 == 0) ; Delay(20); keyNum = 15;}if (P1_0 == 0) {Delay(20); while(P1_0 == 0) ; Delay(20); keyNum = 16;}return keyNum;
}

修改后的执行结果如下,可以看到按下一次按钮,相应的代码只会执行一次:

在这里插入图片描述

5. 矩阵扫描方式2

先扫出按键的列,再扫出按键的行

#include <REGX52.H>
#include "Delay.h"// 先找列再找行
unsigned char MatrixKeyboardScan()
{// 先扫出按下按键所处列unsigned char keyNum = 0;P1 = 0x0F;switch(P1){case (0x07): keyNum = 1; break;case (0x0B): keyNum = 2; break;case (0x0D): keyNum = 3; break;case (0x0E): keyNum = 4; break;}// 再扫出按下按键所处行P1 = 0xF0;switch(P1){case (0x70): keyNum += 0; break;case (0xB0): keyNum += 4; break;case (0xD0): keyNum += 8; break;case (0xE0): keyNum += 12; break;}return keyNum;
}

原理很简单,当第一步将P1置为0x0F时,通过判断低位处于什么样的状态,就可以知道按下的是哪一列的按钮:

在这里插入图片描述

同理,第二步将P1置为0xF0时,判断高位处于什么样的状态,就可以判断出按下按钮的所处行。

因为在第一步假设按下按钮位于第1行,分别置了1、2、3、4;则第二行仅需根据行数,加上具体相隔的按钮值即可。

但是我不太清楚消抖应该加在哪,我自己试了一下会很奇怪,所以这里的代码只是简单写了一下,仅供参考,可能会出现抖动问题。

6. 主函数代码

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKeyboard.h"void main()
{unsigned char keyboardNum = 0;LCD_Init();LCD_ShowString(1,1,"Press:");LCD_ShowNum(2,1,keyboardNum,2);while (1){keyboardNum = MatrixKeyboardScan();if (keyboardNum){LCD_ShowNum(2,1,keyboardNum,2);}}
}

结果如下:

在这里插入图片描述

7. 矩阵键盘密码锁

需求是由用户输入密码,单片机检测是否正确,正确则输出“OK”,反之输出“ERR”,除此之外,还需要有“删除(回退一格)”和“取消(输入密码全部清空)”功能。

我的代码实现如下,S1-S9代表数字1-9,S10是回退一格,S11是取消,S12是确认:

#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKeyboard.h"void main()
{unsigned int password = 0;unsigned int truePassword = 1234;unsigned int passwordNum = 0;LCD_Init();LCD_ShowString(1,1,"Password:");LCD_ShowNum(2,1,password,4);password = password / 10;while(1){unsigned int currNum = MatrixKeyboardScan();if (currNum){// 非法输入不允许,直接忽略if (currNum == 13 || currNum == 14 || currNum == 15 || currNum == 16) continue;// 功能键:10删除,11取消,12确认检查密码// 删除操作if (currNum == 10 && passwordNum <= 4){password /= 10; // 回退一位LCD_ShowNum(2,1,password,4); // 刷新密码passwordNum--; // 位数-1continue;}// 取消操作else if (currNum == 11){passwordNum = 0;password = 0;LCD_ShowNum(2,1,password,4);continue;}// 确认操作else if (currNum == 12){if (password == truePassword) // 密码正确显示OK,程序结束{LCD_ShowString(1,15,"OK");}					else // 密码错误显示ERR,password重置为0{LCD_ShowString(1,14,"ERR");passwordNum = 0;password = 0;LCD_ShowNum(2,1,password,4);}continue;}// 功能键以外的数字键处理if (passwordNum == 0){LCD_ShowString(1,14,"   ");if (currNum == 13) currNum = 0; // 用13作为数字0,允许用户输入0password = currNum;LCD_ShowNum(2,1,password,4);passwordNum++;}else if (passwordNum < 4){if (currNum == 13) currNum = 0; // 用13作为数字0,允许用户输入0password = password * 10 + currNum;LCD_ShowNum(2,1,password,4);passwordNum++;}}}
}

效果如下:

密码锁Demo

输入密码:
在这里插入图片描述
输入密码后按一下S10(回退一格):
在这里插入图片描述
按取消(密码置0):
在这里插入图片描述

输入错误密码按确定(密码清0,显示ERR):
在这里插入图片描述
输入正确密码按确定:
在这里插入图片描述

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

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

相关文章

Android Studio jetpack compose折叠日历日期选择器【折叠日历】

今天写一个日期选择器&#xff0c;大家根据自己需求改代码&#xff0c;记得点赞支持&#xff0c;谢谢&#xff5e; 这是进入的默认状态 折叠状态选中本周其他日期状态 切换上下周状态 展开日历状态 切换上下月状态 选中状态 代码如下&#xff1a; import android.content.C…

驭码CodeRider 2.0全栈开发实战指南:从零构建现代化电商平台

驭码CodeRider 2.0全栈开发实战指南:从零构建现代化电商平台 一、CodeRider 2.0:重新定义全栈智能开发 1.1 革命性升级亮点 #mermaid-svg-AKjytNB4hD95UZtF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-AKjyt…

大模型智能体AutoGen面试题及参考答案

目录 AutoGen 的核心是什么? Agent 在 AutoGen 中承担什么角色? AutoGen 是如何定义 AssistantAgent、UserProxyAgent 等代理类型的? 什么是 GroupChat(组对话)模式? AutoGen 的 system message 在框架中扮演什么作用? 如何通过 Agent 实现自然语言处理? AutoGen…

深度学习笔记26-天气预测(Tensorflow)

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、前期准备 1.数据导入 import numpy as np import pandas as pd import warnings import seaborn as sns import matplotlib.pyplot as plt warnings.filt…

day54 python对抗生成网络

目录 一、GAN对抗生成网络思想 二、实践过程 1. 数据准备 2. 构建生成器和判别器 3. 训练过程 4. 生成结果与可视化 三、学习总结 一、GAN对抗生成网络思想 GAN的核心思想非常有趣且富有对抗性。它由两部分组成&#xff1a;生成器&#xff08;Generator&#xff09;和判…

龙虎榜——20250613

上证指数放量下跌收阴线&#xff0c;个股下跌超4000只&#xff0c;受外围消息影响情绪总体较差。 深证指数放量下跌&#xff0c;收阴线&#xff0c;6月总体外围风险较高&#xff0c;转下跌走势的概率较大&#xff0c;注意风险。 2025年6月13日龙虎榜行业方向分析 1. 石油石化&…

Linux常用命令加强版替代品

Linux常用命令加强版替代品 还在日复一日地使用 ls、grep、cd 这些“上古”命令吗&#xff1f;是时候给你的终端来一次大升级了&#xff01;本文将为你介绍一系列强大、高效且设计现代的Linux命令行工具&#xff0c;它们将彻底改变你的工作流&#xff0c;让你爱上在终端里操作…

Hadoop 003 — JAVA操作MapReduce入门案例

MapReduce入门案例-分词统计 文章目录 MapReduce入门案例-分词统计1.xml依赖2.编写MapReduce处理逻辑3.上传统计文件到HDFS3.配置MapReduce作业并测试4.执行结果 1.xml依赖 <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-commo…

Python打卡第53天

浙大疏锦行 作业&#xff1a; 对于心脏病数据集&#xff0c;对于病人这个不平衡的样本用GAN来学习并生成病人样本&#xff0c;观察不用GAN和用GAN的F1分数差异。 import pandas as pd import numpy as np import torch import torch.nn as nn import torch.optim as optim from…

力扣-279.完全平方数

题目描述 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4、9 和 16 都是完全平方数&#xff0c;而 3 和 1…

前端构建工具Webapck、Vite——>前沿字节开源Rspack详解——2023D2大会

Rspack 以下是针对主流构建工具&#xff08;Webpack、Vite、Rollup、esbuild&#xff09;的核心不足分析&#xff0c;以及 Rspack 如何基于这些痛点进行针对性改进 的深度解析&#xff1a; 一、主流构建工具的不足 1. Webpack&#xff1a;性能与生态的失衡 核心问题 冷启动慢…

输入法,开头输入这U I V 三个字母会不显示 任何中文

1. 汉语拼音规则的限制 汉语拼音中不存在以“V”“U”“I”为声母的情况&#xff1a; 汉语拼音的声母是辅音&#xff0c;而“V”“U”“I”在汉语拼音中都是元音&#xff08;或韵母的一部分&#xff09;。汉语拼音的声母系统中没有“V”“U”“I”作为声母的音节。例如&#xf…

Linux文件权限详解:从入门到精通

前言 权限是什么&#xff1f; 本质&#xff1a;无非就是能做和不能做什么。 为什么要有权限呢&#xff1f; 目的&#xff1a;为了控制用户行为&#xff0c;防止发生错误。 1.权限的理解 在学习下面知识之前要先知道的一点是&#xff1a;linux下一切皆文件&#xff0c;对li…

在多云环境透析连接ngx_stream_proxy_protocol_vendor_module

1、模块定位与价值 多云接入&#xff1a;在同一 Nginx 实例前端接入来自多云平台的私有链路时&#xff0c;能区分 AWS、GCP、Azure 特有的连接 ID。安全审计&#xff1a;自动记录云平台侧的 Endpoint/VPC ID&#xff0c;有助于联调和安全事件追踪。路由分流&#xff1a;基于不…

力扣:基本计算器

基本计算器: 224. 基本计算器 - 力扣&#xff08;LeetCode&#xff09; 本体思路为&#xff0c;将中缀表达式转为后缀表达式&#xff0c;通过后缀表达式进行运算。 中缀表达式: 我们日常生活中熟知的表达式如12-30 就是一个中缀表达式。 后缀表达式: 150. 逆波兰表达式求值 - …

《AI日报 · 0613|ChatGPT支持导出、Manus免费开放、GCP全球宕机》

AI 资讯 1️⃣ OpenAI ChatGPT Canvas新增多格式导出功能 OpenAI终于为ChatGPT Canvas推出了用户期待已久的导出功能。现在,用户可以将创作内容导出为多种格式:文档类支持PDF、docx和markdown格式,代码文件则可直接保存为对应扩展名的源文件(如.py、.js、.sql等)。这一功…

C++中的零拷贝技术

一、C中零拷贝技术的核心概念 零拷贝&#xff08;Zero-copy&#xff09;是一种重要的优化技术&#xff0c;旨在减少数据在内存中的不必要复制&#xff0c;从而提高程序性能、降低内存使用并减少CPU消耗。在C中&#xff0c;零拷贝技术通过多种方式实现&#xff0c;包括引用语义…

RT_Thread内核源码分析(五)——内存管理@小堆内存管理算法

目录 1、内存堆控制 1.1 内存堆控制器 1.2 内存块节点 1.3 内存堆管理 2、内存堆初始化 2.1 初始化接口 2.2 初始化示例 2.3 源码分析 3、内存堆操作 3.1 内存块申请 3.1.1 相关接口 3.1.2 原理分析 3.1.3 示例分析 3.1.4 代码分析 3.2 内存块伸缩 3.2.1 相关…

MyBatis-Plus 混合使用 XML 和注解

mybatisplus代码生成器&#xff1a; 版本匹配是个比较麻烦的问题&#xff0c;这是我的配置&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>…

基于ssm的教学质量评估系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…