2021能看的网站不要app贴吧,企业如何创建品牌,做字素的网站,网站设计流程详细步骤矩阵键盘介绍
当键盘中按键数量较多时#xff0c;为了减少I/O口的占用#xff0c;通常将按键排列成矩阵形式。在矩阵式键盘中#xff0c;每条水平线和垂直线在交叉处不直接连通#xff0c;而是通过一个按键加以连接。采用逐行或逐列的“扫描”#xff0c;就可以读出任何位…矩阵键盘介绍
当键盘中按键数量较多时为了减少I/O口的占用通常将按键排列成矩阵形式。在矩阵式键盘中每条水平线和垂直线在交叉处不直接连通而是通过一个按键加以连接。采用逐行或逐列的“扫描”就可以读出任何位置按键的状态。
扫描原理 让我们来对比一下独立按键和矩阵按键 在独立按键中我们是按键一端接地当按键按下时端口处变为低电平。之前我只是记住了这一点但是现在我才了解到内部的原理。请看下面介绍。
51系列单片机I/O口各种不同的工作模式以及配置介绍
首先我们要先知道一件事情所有IO口通电后默认是高电平 总的来说我们使用的STC89C52中,P1,P2,P3,P4口都是弱上拉模式只有P0端口为开漏输出模式。具体什么是弱上拉模式呢 简单一点来讲就是像我们下面图中的结构开关打到上面此时端口为高电位输出为1但是此时驱动能力很弱当端口外面接低电位的时候允许将它的电位拉低我的理解是当外面接低电位的时候vcc和低电位之间形成通路有了电流这样电阻就能分压使得端口处电位降低不知道是否正确这就是我们一会儿讲到矩阵按键时的原理。当我们让端口为低电位开关打到下面时端口处为低电位输出为0此时驱动能力很强能吸收许多的电流。这其实就是在51单片机中能让发光二极管LED发光比较亮的原理当端口电位为0的时候驱动能力很强。所以我们通常检验端口电位是不是变化为0。 逐行扫描读取按键状态具体原理
结构s1s2s3s4的左边都连在了P17s5s6s7s8的左边都连在了P16依次共四行占用了4个端口s1s5s9s13的右边都连在了P13s2s6s10s14的右边都连在了P12依次共四列占用了4个端口。我们的想法是
p17 p16 p15 p14
0 1 1 1 //通过其余四个端口电位状态来检查第一行是否有按键按下
1 0 1 1 //通过其余四个端口电位状态来检查第二行是否有按键按下
1 1 0 1 //通过其余四个端口电位状态来检查第三行是否有按键按下
1 1 1 0 //通过其余四个端口电位状态来检查第四行是否有按键按下
具体原理这部分很重要这部分很重要这部分很重要
一.首先我们要知道当p17 p16 p15 p14中的端口给0时才能检测到是否这行的按键按下了当p17 p16 p15 p14中仅P17端口给0其他全给1时就算第二行的按键按下了但第一行的按键没有按下我们的P10P11P12P13也检测不到低电平来判断某一列有按键按下因为此时只有第一行的一端是低电平其他行按键左端都是高电平。
二.接着解释一下为什么按键一端为低电平的时候另一端也是高电平。我们刚刚在上面讲过了
所有IO口通电后默认是高电平当然这里的P10P11P12P13也不例外但是我们要看上面说过的弱上拉模式当端口外面接低电位的时候允许将它的电位拉低即使它处在高电位这里的P1系列8个端口都是弱上位模式所以比如给P17接低点位并且s2按下了此时P12的电位就变为了低电位检测为0实现了精准读取按键状态的效果。
三.最后来结合第一点解释一下为什么一次只能给p17 p16 p15 p14其中之一端口赋0让其处于低点位。假如我们同时让P17,P16为低电平此时当我们按下s5的时候和按下s1的时候或者说同时按下的时候P13端口的电位都变为了0我们无法分辨到底是哪个按键按下了所以一次只能给p17 p16 p15 p14其中之一端口赋0。 对I/O工作模式的补充
补充上面仅仅介绍了I/O口的弱上拉模式下面补充一些对于STC89C52系列单片机中的开漏输出模式的介绍。 个人总结欢迎大家指正我认为P0端口的开漏输出配置就相当于上面这幅图中将电阻去掉这样的话就不能让端口连通接地就使得端口的电位由1变为0它可以实现输出低电平0但不能直接通过接地来改变电位1为0这一点和弱上拉输出端口不一样。
开漏输出端口在输出高电平1时实际上是处于高阻态也就是并没有输出电平的功能因此无法通过接地来将其改变为低电平。如果需要在开漏输出端口上输出低电平通常需要外部上拉电阻和一个连接到负极比如地的开关或器件当外部上拉电阻连接到 VCC 电源时输出端口会被拉高到高电平1。当开关或器件闭合时输出端口会连接到地从而形成电路使得输出端口能够输出低电平0。
继续拓展 代码实现
具体要求按下按键s1-s16依次在液晶屏上显示数字1-16。
//main.c里面代码
#includeregx52.h
#includeLCD1602.h
#includematrix.h
void main(){unsigned char i0;LCD_Init();LCD_ShowString(1,1,hello world);while(1){imatrix_num();if(i){//这里注意如果不加这个if判断条件的话就会导致可能按下的时候闪烁一瞬间甚至不闪烁
//因为这里不写这个if的话当我们没有按下的时候会一直显示00所以我们要加一个if判断来让它不显示00LCD_ShowNum(2,2,i,2);}}}
//matrix.c里面代码
#includeregx52.h
#includedelay.h
unsigned char matrix_num(){unsigned char key_num0;P10xFF;P1_70;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num1;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num2;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num3;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num4;}}P10xFF;P1_60;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num5;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num6;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num7;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num8;}}P10xFF;P1_50;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num9;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num10;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num11;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num12;}}P10xFF;P1_40;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num13;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num14;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num15;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num16;}}return key_num;
}完整代码包括LCD系列内部实现代码参考文末并且注意使用这个函数要先进行初始化先调用LCD_Init()函数这个矩阵按键在理解了原理之后代码容易写出来。
下面做出两点解释
1.matrix_num()代码逻辑解释我们每次检测一行按键的时候都是像这样P10xFF; P1_70;仅仅让一个端口输出地低电平然后检测后四个端口是否会出现低电平。这里代码是逐行检测逐列检测和逐行检测代码逻辑几乎一样只不过前四个端口用途和后四个端口用途颠倒一下不多赘述。
2.代码耦合性最好不要让这边刚读取到了按键按下立即在这个代码中就显示读取到的数字最好写成这边用一个函数来读取按下的按键然后函数执行结束将按下按键的编号返回然后再显示这样代码更便于理解
用矩阵按键实现密码锁
功能按下1-9号按键输入对应数字按下10号按键输入0按下11号按键表示确认提交按下12号按键表示退格按下13号按键表示清除。密码正确提交显示YES 666错误提交显示“ERROE”密码锁一共四位数字。
代码这里只展示了main.c里面的因为是在上面那个显示1-16的代码基础上来的除了main.c不同其他都一样。
纯代码版本
#includeregx52.h
#includeLCD1602.h
#includematrix.h
void main(){unsigned char i0,count0;unsigned int password0,set_password3226;LCD_Init();LCD_ShowString(1,1,password);while(1){imatrix_num();if(0ii10count4){LCD_ShowString(1,10, );passwordpassword*10i%10;LCD_ShowNum(2,2,password,4);count;}if(i12count0){passwordpassword/10;LCD_ShowNum(2,2,password,4);count--;}if(i13){password0;LCD_ShowNum(2,2,password,4);count0;}if(i11){if(count4){if(password!set_password){LCD_ShowString(1,10,ERROR);password0;count0;}if(passwordset_password){LCD_ShowString(1,10,YES 666);password0;count0;}}}}
} 代码解释版本
#includeregx52.h
#includeLCD1602.h
#includematrix.h
void main(){unsigned char i0,count0;//count用来记录已经输入了几位unsigned int password0,set_password3226;//保存当前数字和预置密码LCD_Init();LCD_ShowString(1,1,password);while(1){imatrix_num();//获取按下按键对应的数字if(0ii10count4){//输入密码部分LCD_ShowString(1,10, );//为了假如一轮输入测试结束
//再次输入后覆盖上一次提示信息passwordpassword*10i%10;//这里i%10主要是针对10%100LCD_ShowNum(2,2,password,4);count;}if(i12count0){//如果按下退格键并且此时count就退格且count--passwordpassword/10;LCD_ShowNum(2,2,password,4);count--;}if(i13){//清除输入且count重置为0password0;LCD_ShowNum(2,2,password,4);count0;}if(i11){//如果按下确认键就进行检查if(count4){if(password!set_password){LCD_ShowString(1,10,ERROR);password0;//复位count0;}if(passwordset_password){LCD_ShowString(1,10,YES 666);password0;count0;}}}}
}
补充另一种扫描方法
首先第一种就是我们刚刚讲到的行列扫描法。
第二种是线翻转扫描方法十字交叉确定
先给所有行赋0给所有列赋1判断在哪一行
然后给所有行赋1所有列赋0判断在哪一列。
两次分别确定出按下按键所在的行与列就可以知道具体按下按键在哪里了。这里确定行列的原理也是当按键按下时弱上拉模式当端口外面接低电位的时候允许将它的电位拉低假如按键按下时就能在另一端检测到低电位。
下面是代码部分示例但是我认为这种方法写代码没有上一种简单且易于理解所以我还是推荐使用逐行或者逐列扫描。 完整代码 //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_RSP2^6;
sbit LCD_RWP2^5;
sbit LCD_ENP2^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_RS0;LCD_RW0;LCD_DataPortCommand;LCD_EN1;LCD_Delay();LCD_EN0;LCD_Delay();
}/*** brief LCD1602写数据* param Data 要写入的数据* retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS1;LCD_RW0;LCD_DataPortData;LCD_EN1;LCD_Delay();LCD_EN0;LCD_Delay();
}/*** brief LCD1602设置光标位置* param Line 行位置范围1~2* param Column 列位置范围1~16* retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line1){LCD_WriteCommand(0x80|(Column-1));}else if(Line2){LCD_WriteCommand(0x80|(Column-10x40));}
}/*** 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(i0;String[i]!\0;i){LCD_WriteData(String[i]);}
}/*** brief 返回值X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result1;for(i0;iY;i){Result*X;}return Result;
}/*** brief 在LCD1602指定位置开始显示所给数字* param Line 起始行位置范围1~2* param Column 起始列位置范围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(iLength;i0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%100);}
}/*** 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(Number0){LCD_WriteData();Number1Number;}else{LCD_WriteData(-);Number1-Number;}for(iLength;i0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%100);}
}/*** 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(iLength;i0;i--){SingleNumberNumber/LCD_Pow(16,i-1)%16;if(SingleNumber10){LCD_WriteData(SingleNumber0);}else{LCD_WriteData(SingleNumber-10A);}}
}/*** 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(iLength;i0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%20);}
}
//delay.h
#ifndef__delay_H__
#define__delay_H__
void delay(unsigned int t);#endif
//delay.c
void delay(unsigned int t){while(t--);
}
//matrix.h
#ifndef__matrix_H__
#define__matrix_H__
unsigned char matrix_num(); #endif
//matric.c
#includeregx52.h
#includedelay.h
unsigned char matrix_num(){unsigned char key_num0;P10xFF;P1_70;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num1;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num2;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num3;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num4;}}P10xFF;P1_60;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num5;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num6;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num7;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num8;}}P10xFF;P1_50;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num9;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num10;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num11;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num12;}}P10xFF;P1_40;if(P1_30){delay(200);if(P1_30){while(P1_30);key_num13;}}if(P1_20){delay(200);if(P1_20){while(P1_20);key_num14;}}if(P1_10){delay(200);if(P1_10){while(P1_10);key_num15;}}if(P1_00){delay(200);if(P1_00){while(P1_00);key_num16;}}return key_num;
}
参考文章【51单片机】矩阵键盘_51单片机4×4矩阵键盘-CSDN博客
欢迎大家指正