五、用矩阵键盘实现密码锁
矩阵键盘
独立键盘与单片机进行连接时,每一个按键都需要单片机的一个I/O口,若某单片机系统较多按键,如果用独立按键便会占用较多的I/O口资源。为了尽可能节省I/O口线,引入矩阵键盘。
矩阵按键原理
- 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式
- 采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态
以巫妖王单片机上的4×4矩阵键盘为例,讲解矩阵键盘的原理和检测方法,其原理图如上图所示。将16个按键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样一共有4行4列共八根线,我们将这八根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键。用这种办法我们也可以实现3行3列9个键,5行5列25个键等。
无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平。独立键盘有一端固定为低电平,单片机写程序检测时比较方便,但是矩阵键盘两端都与单片机I/O口相连,因此在检测时需要人为通过单片机的I/O口送出低电平。
下面着重介绍一下扫描的概念:
数码管扫描(输出扫描)
原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描)
原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
以按行扫描为例:
当P1.4-P1.7口赋值为1101时,此时先看P17所在的行,无论S1、S2、S3和S4谁按下,两端都是高电平,同理P15和P14同样如此,而P16所在的行因为P1.6口赋值为0,只要在此基础检测P1.0-P1.3口电平情况就可以判断究竟是S5、S6、S7还是S8被按下。具体的说,当P1.4-P1.7口赋值为1101时,如果P1.0-P1.3口赋值为:1110(P1 .3为0),则S5被按下,其他同理,并以此类推即可。
在写代码前,再介绍C51子函数带返回值的写法
//格式
类型 函数名(形参)
{函数体;return 数据;
}
例如:int getSum(int num1,int num2)
{int sum = num1 + num2;return sum;
}
//参数是 函数接收外面传进来的
//返回值 是函数从里面扔出去的
模块化代码
各模块具体代码如下
主函数
main()
#include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "MaxtrixKey.h"unsigned char KeyNum; //定义变量 接一下返回值 void main() {LCD_Init();LCD_ShowString(1,1,"Hello World!");while(1){KeyNum=MaxtrixKey();if(KeyNum){LCD_ShowNum(2,1,KeyNum,2);}} } /* 为什么需要加上if(KeyNum)进行判断,这是因为如果不加的话有一下一种情况: 当你没有按下键时,开始执行KeyNum=MaxtrixKey()语句,即KeyNum通过调用MaxtrixKey函数获取键值,此时KeyNum获取的值为0(不按下键,MaxtrixKey函数内的值就是初始化的值0) 然后开始执行LCD_ShowNum函数,这样1602上就会显示00 当你按下某个键,不松手,会卡在执行KeyNum=MaxtrixKey()语,显示0 显示的键值和显示0 间隔的时间太短 以至于人眼无法识别 */
MaxtrixKey.h
#ifndef__MAXTRIXKRY_H__ #define__MAXTRIXKRY_H__unsigned char MaxtrixKey();#endif
MaxtrixKey.c
#include <REGX52.H> #include "Delay.h"/*** @brief 矩阵键盘读取按键键码* @param 无* @retval KeyNumer 按下按键的键码值如果按下不放,程序会停留在此函数,松手一瞬间,返回按键码,没有按键时,返回0*/unsigned char MaxtrixKey() {unsigned char KeyNumber=0;P1=0xff;P1_3=0;if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}P1=0xff;P1_2=0;if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}P1=0xff;P1_1=0;if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}P1=0xff;P1_0=0;if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}return KeyNumber;}
LCD1602.h
定义如下#ifndef __LCD1602_H__ #define __LCD1602_H__//用户调用函数: void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); void LCD_ShowString(unsigned char Line,unsigned char Column,char *String); void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length); void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif
LCD1602.c
定义如下#include <REGX52.H>//引脚配置: sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^7; #define LCD_DataPort P0//函数定义: /*** @brief LCD1602延时函数,12MHz调用可延时1ms* @param 无* @retval 无*/ void LCD_Delay() {unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i); }/*** @brief LCD1602写命令* @param Command 要写入的命令* @retval 无*/ void LCD_WriteCommand(unsigned char Command) {LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay(); }/*** @brief LCD1602写数据* @param Data 要写入的数据* @retval 无*/ void LCD_WriteData(unsigned char Data) {LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay(); }/*** @brief LCD1602设置光标位置* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @retval 无*/ void LCD_SetCursor(unsigned char Line,unsigned char Column) {if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));} }/*** @brief LCD1602初始化函数* @param 无* @retval 无*/ void LCD_Init() {LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏 }/*** @brief 在LCD1602指定位置上显示一个字符* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @param Char 要显示的字符* @retval 无*/ void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char) {LCD_SetCursor(Line,Column);LCD_WriteData(Char); }/*** @brief 在LCD1602指定位置开始显示所给字符串* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param String 要显示的字符串* @retval 无*/ void LCD_ShowString(unsigned char Line,unsigned char Column,char *String) {unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);} }/*** @brief 返回值=X的Y次方*/ int LCD_Pow(int X,int Y) {unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result; }/*** @brief 在,范围:1~2* @param Column 起始列位LCD1602指定位置开始显示所给数字* @param Line 起始行位置置,范围:1~16* @param Number 要显示的数字,范围:0~65535* @param Length 要显示数字的长度,范围:1~5* @retval 无*/ void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) {unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');} }/*** @brief 在LCD1602指定位置开始以有符号十进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:-32768~32767* @param Length 要显示数字的长度,范围:1~5* @retval 无*/ void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length) {unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');} }/*** @brief 在LCD1602指定位置开始以十六进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~0xFFFF* @param Length 要显示数字的长度,范围:1~4* @retval 无*/ void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) {unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}} }/*** @brief 在LCD1602指定位置开始以二进制显示所给数字* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param Number 要显示的数字,范围:0~1111 1111 1111 1111* @param Length 要显示数字的长度,范围:1~16* @retval 无*/ void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) {unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');} }
Delay.h
#ifndef __DELAY_H__ // #define __DELAY_H__void Delay(unsigned int xms);#endif
Delay.c
void Delay(unsigned int xms) //@12.000MHz {while(xms--){unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);} }
密码锁
在上面的基础上模块化的代码上,只需要改动主函数mian()
即可实现,因此,下面就只给出主函数密码
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MaxtrixKey.h"unsigned char KeyNum; //定义变量 接一下返回值
unsigned int PassWord,Count; //定义四位密码和计数器void main()
{LCD_Init();LCD_ShowString(1,1,"PassWord:");while(1){KeyNum=MaxtrixKey();if(KeyNum){if(KeyNum<=10) //密码区,如果S1~S10按键按下,输入密码{if(Count<4){PassWord*=10;//相当于PassWord=PassWord*10 目的:密码左移一位PassWord+=KeyNum%10 ;//取余,1-9取余还是1-9 而10取余则为0 ,目的:获取一位密码Count++; //计次加1}LCD_ShowNum(2,1,PassWord,4); }if(KeyNum==11)//如果S11按下,即为确认密码{if(PassWord==9981) //如果密码等于正确密码显示Pass,否则显示EROR{LCD_ShowString(1,14,"ok ");//注意OK后有空格 用于对齐ERRPassWord=0; //密码清0Count=0; //计次清0LCD_ShowNum(2,1,PassWord,4);//更新显示}else{LCD_ShowString(1,14,"Err");PassWord=0; //密码清0Count=0; //计次清0LCD_ShowNum(2,1,PassWord,4);//更新显示} }if(KeyNum==12)//如果S12按下,取消{PassWord=0; //密码清0Count=0; //计次清0LCD_ShowNum(2,1,PassWord,4);//更新显示}}}
}
需要注意的是,当第一个输入的是0的时候会存在bug,这是因为0*10h还是等于0起不到进位的效果,此外也没加退格的功能,等后续有时间我再更新添加退格功能的代码。
五、用矩阵键盘实现密码锁相关推荐
- 利用矩阵键盘制作密码锁
本周学习了关于矩阵键盘的知识,并利用矩阵键盘制作了密码锁. 矩阵键盘利用矩阵式的连接减少了IO口的使用,并用扫描的方式保证每一个按键的响应. 下面是代码 main #include <REGX5 ...
- 模块学习(五)——矩阵键盘
矩阵键盘的学习只是为了做一个简单的遥控器,主要目标还是后续的遥控器控制小车实现简单而精准的直行和转弯,加上前面的模块,锻炼自己PID的调试和理解能力.但毕竟矩阵键盘也算个模块嘛,就也记录一下,分享给有 ...
- (五)51单片机基础——矩阵键盘
矩阵键盘介绍: 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式 采用逐行或逐列的"扫描",就可以读出任何位置按键的状态 扫描: 数码管扫描(输出扫描) 原 ...
- 51单片机红外电子密码锁【红外对管矩阵键盘数码管LCD1602显示模块】
系统功能 51单片机红外电子密码锁系统主要由红外线编码电路.红外线解码开锁电路.掉电保护电路.声光提示报警电路.键盘及显示电路等组成,编写相应的程序代码并进行结果测试和仿真演示. 利用红外遥控技术和单 ...
- 【设计方案分享】基于单片机温度监测监控报警系统设计-基于单片机钞票自动智能识别系统设计-基于单片机乒乓球游戏机控制系统设计-基于单片机温度监测监控报警系统设计-基于单片机矩阵键盘的电子密码锁设计
820基于单片机温度监测监控报警系统设计-设计资料 温度监测器功能描述: 1.主控芯片用的是51单片机(STC89C51). 2.使用温度传感器DS18b20采集温度. 3.用1602液晶显示显示温度 ...
- 基于单片机乒乓球游戏机控制系统设计-基于单片机矩阵键盘的电子密码锁设计-基于单片机温度监测监控报警系统设计-基于单片机钞票自动智能识别系统设计-设计资料【转发分享】
819基于单片机乒乓球游戏机控制系统设计-设计资料下载 乒乓球游戏机设计任务为: (1)使用乒乓游戏机的甲乙双方各在不同的位置发球或击球. (2)乒乓球的位置和移动方向由灯亮及依次点燃的方向决定,球移 ...
- 51-矩阵键盘和矩阵键盘密码锁
一.矩阵键盘 上面两个图中的 扫描 可以理解为 循环 . 在上图中,所谓最终实现所有按键 "同时" 检测的效果指的就是:当我们在矩阵键盘中任意按下或按下并松开一个按键,都能被检测出 ...
- 使用51单片机的矩阵键盘和LCD1602做一个密码锁
文章目录 前言 一.工具准备 工具一:51单片机 工具二:LCD1602液晶显示屏 工具三:Keil软件 二.开始操作 1.LCD1602的程序调试 2.矩阵键盘的程序调试 3.密码锁程序设计 总结 ...
- 基于51单片机LCD1602的简易矩阵键盘密码锁
写在前面 初次开始写博客,表达方面也许会有很多不足,希望自己能通过这种方式巩固自己的学习和锻炼表达能力,同时也是对于自己学习的记录. 一.功能介绍 四位密码锁,密码可更改,输入仅四次以内有效 矩阵键盘 ...
最新文章
- [转载]C# 二进制与十进制,十进制与十六进制相互转换
- Rust 2020 调查报告出炉,95%的开发者吐槽Rust难学
- Django博客系统(详情评论数据展示)
- 包括 一个 20像素的黑条条
- ubuntu下mysql整个数据库备份与还原
- ps 命令的详细功能解析
- 我的编程之路——VB篇
- band math函数_Envi中波段运算(bandmath)常用的函数
- STM32接电机驱动,杜邦线供电,然后反烧问题
- 今日头条 Android ‘秒‘ 级编译速度优化
- 测试用例设计方法——等价类划分法、边界值分析法、场景法、判定表、因果图、错误推测法和正交试验法
- linux环境下解压压缩包失败
- 剪绳子(python)
- 2018/02/12
- 修改注册表值scancode map来屏蔽键盘上的键
- nodejs aes 加解密
- mc服务器领地位置,服务器领地amp;地皮指令大全 183独家汇总_18183我的世界专区...
- 《Collaborative Memory Network for Recommendation Systems》推荐系统之协同记忆网络CMN
- 企业邮箱和普通邮箱有什么区别
- 动力节点 SpringMVC P44-53