矩阵键盘

独立键盘与单片机进行连接时,每一个按键都需要单片机的一个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;
}
//参数是 函数接收外面传进来的
//返回值 是函数从里面扔出去的

模块化代码

各模块具体代码如下

  1. 主函数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 间隔的时间太短 以至于人眼无法识别
    */
    
  2. MaxtrixKey.h

    #ifndef__MAXTRIXKRY_H__
    #define__MAXTRIXKRY_H__unsigned char MaxtrixKey();#endif
    
  3. 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;}
  4. 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
    
  5. 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');}
    }
  6. Delay.h

    #ifndef __DELAY_H__   //
    #define __DELAY_H__void Delay(unsigned int xms);#endif
    
  7. 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起不到进位的效果,此外也没加退格的功能,等后续有时间我再更新添加退格功能的代码。

五、用矩阵键盘实现密码锁相关推荐

  1. 利用矩阵键盘制作密码锁

    本周学习了关于矩阵键盘的知识,并利用矩阵键盘制作了密码锁. 矩阵键盘利用矩阵式的连接减少了IO口的使用,并用扫描的方式保证每一个按键的响应. 下面是代码 main #include <REGX5 ...

  2. 模块学习(五)——矩阵键盘

    矩阵键盘的学习只是为了做一个简单的遥控器,主要目标还是后续的遥控器控制小车实现简单而精准的直行和转弯,加上前面的模块,锻炼自己PID的调试和理解能力.但毕竟矩阵键盘也算个模块嘛,就也记录一下,分享给有 ...

  3. (五)51单片机基础——矩阵键盘

    矩阵键盘介绍: 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式 采用逐行或逐列的"扫描",就可以读出任何位置按键的状态 扫描: 数码管扫描(输出扫描) 原 ...

  4. 51单片机红外电子密码锁【红外对管矩阵键盘数码管LCD1602显示模块】

    系统功能 51单片机红外电子密码锁系统主要由红外线编码电路.红外线解码开锁电路.掉电保护电路.声光提示报警电路.键盘及显示电路等组成,编写相应的程序代码并进行结果测试和仿真演示. 利用红外遥控技术和单 ...

  5. 【设计方案分享】基于单片机温度监测监控报警系统设计-基于单片机钞票自动智能识别系统设计-基于单片机乒乓球游戏机控制系统设计-基于单片机温度监测监控报警系统设计-基于单片机矩阵键盘的电子密码锁设计

    820基于单片机温度监测监控报警系统设计-设计资料 温度监测器功能描述: 1.主控芯片用的是51单片机(STC89C51). 2.使用温度传感器DS18b20采集温度. 3.用1602液晶显示显示温度 ...

  6. 基于单片机乒乓球游戏机控制系统设计-基于单片机矩阵键盘的电子密码锁设计-基于单片机温度监测监控报警系统设计-基于单片机钞票自动智能识别系统设计-设计资料【转发分享】

    819基于单片机乒乓球游戏机控制系统设计-设计资料下载 乒乓球游戏机设计任务为: (1)使用乒乓游戏机的甲乙双方各在不同的位置发球或击球. (2)乒乓球的位置和移动方向由灯亮及依次点燃的方向决定,球移 ...

  7. 51-矩阵键盘和矩阵键盘密码锁

    一.矩阵键盘 上面两个图中的 扫描 可以理解为 循环 . 在上图中,所谓最终实现所有按键 "同时" 检测的效果指的就是:当我们在矩阵键盘中任意按下或按下并松开一个按键,都能被检测出 ...

  8. 使用51单片机的矩阵键盘和LCD1602做一个密码锁

    文章目录 前言 一.工具准备 工具一:51单片机 工具二:LCD1602液晶显示屏 工具三:Keil软件 二.开始操作 1.LCD1602的程序调试 2.矩阵键盘的程序调试 3.密码锁程序设计 总结 ...

  9. 基于51单片机LCD1602的简易矩阵键盘密码锁

    写在前面 初次开始写博客,表达方面也许会有很多不足,希望自己能通过这种方式巩固自己的学习和锻炼表达能力,同时也是对于自己学习的记录. 一.功能介绍 四位密码锁,密码可更改,输入仅四次以内有效 矩阵键盘 ...

最新文章

  1. [转载]C# 二进制与十进制,十进制与十六进制相互转换
  2. Rust 2020 调查报告出炉,95%的开发者吐槽Rust难学
  3. Django博客系统(详情评论数据展示)
  4. 包括 一个 20像素的黑条条
  5. ubuntu下mysql整个数据库备份与还原
  6. ps 命令的详细功能解析
  7. 我的编程之路——VB篇
  8. band math函数_Envi中波段运算(bandmath)常用的函数
  9. STM32接电机驱动,杜邦线供电,然后反烧问题
  10. 今日头条 Android ‘秒‘ 级编译速度优化
  11. 测试用例设计方法——等价类划分法、边界值分析法、场景法、判定表、因果图、错误推测法和正交试验法
  12. linux环境下解压压缩包失败
  13. 剪绳子(python)
  14. 2018/02/12
  15. 修改注册表值scancode map来屏蔽键盘上的键
  16. nodejs aes 加解密
  17. mc服务器领地位置,服务器领地amp;地皮指令大全 183独家汇总_18183我的世界专区...
  18. 《Collaborative Memory Network for Recommendation Systems》推荐系统之协同记忆网络CMN
  19. 企业邮箱和普通邮箱有什么区别
  20. 动力节点 SpringMVC P44-53

热门文章

  1. BBEdit的正则表达式语法
  2. 2021谷歌算法排名因素大全
  3. SAP客户信贷配置与管理解析【转】
  4. windows设置Tomcat使用指定的JDK
  5. 从前后台系统转到RTOS系统编程
  6. 海康威视SDK登录失败,错误码为8
  7. Python爬取网易云音乐歌单名以及歌单标签
  8. 赵小楼《天道》《遥远的救世主》解读(1)怎么评价《遥远的救世主》《天幕红尘》这两本书和《天道》这部电视剧?
  9. java发送html模板的高逼格邮件
  10. python访问陌生人qq空间_使用Python+Selenium模拟登录QQ空间