当前位置: 首页 > web >正文

【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++;}}}
}

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

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

http://www.xdnf.cn/news/13288.html

相关文章:

  • Debian系统简介
  • R7-4 统计单词的个数
  • 电影感户外柔和旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • C++ 智能指针
  • postgresql搭建与初始化
  • EtherCAT转CC-Link IE协议转换通讯助力库卡机器人与三菱PLC无缝对接
  • 【Linux篇】细品环境变量与地址空间
  • Qt Widget类解析与代码注释
  • day27/60
  • 可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
  • 前端技术 HTML iframe 详细解读
  • 每日算法 -【Swift 算法】删除链表的倒数第 N 个结点
  • 聊聊 Pulsar:Producer 源码解析
  • STL 6分配器
  • 智能文档结构化技术的应用,重塑合同管理模式
  • CSS Modules使用
  • [特殊字符] 以太坊智能合约:原理、执行与核心机制
  • 新能源汽车智慧充电桩管理方案:智能安全识别的实际应用
  • mysql为什么一个表中不能同时存在两个字段自增
  • Q: dify的QA分段方式,question、answer和keywords哪些内容进入向量库呢?
  • 【已解决】python的kafka-python包连接kafka报认证失败
  • 【在线五子棋对战】四、MySQL API 使用
  • 多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​
  • Llama 4开源项目多维分析研究
  • VUE element table 列合并
  • 目标检测中F1-Score指标的详细解析:深度理解,避免误区
  • Nginx攻略
  • C# vs2022 找不到指定的 SDK“Microsof.NET.Sdk
  • Android Wi-Fi 连接失败日志分析
  • 第六章 外部中断