单片机矩阵键盘的原理以及代码编写

矩阵键盘应该是经常能够用到的一类器件了,4x4矩阵键盘只需要用到8个io口,即可完成16位按键的读取。其本质原理也就是行列扫描。本片文章将带你详细的学习矩阵键盘的原理以及代码编写。
矩阵键盘本质:
矩阵键盘本质是使用8个io口来进行16个按键的控制读取,可以减小io口的使用,用4条i/o线作为行线,4条i/o线作为列线组成的键盘。在行线和列线的每个交叉点上,设置一个按键。而这样的按键中按键的个数是4 x 4个。
这样的行列式键盘结构能够有效地提高单片机系统中i/o口的利用率。节约单片机的资源,其本质和独立按键类似,就是进行逐行扫描和逐列扫描,然后判断是第几行的第几列个按键,进而进行整体按键值得确定,我们使用的矩阵键盘是接到了单片机的p1口通过读取p1口电平变换即可完成矩阵键盘的数值读取,具体原理图如下:
第一行接到p17,第二行接到p16,第三行接到p15,第4行接到p14
第一列接到p13,第二列接到p12,第三列接到p11,第四列接到p10
矩阵键盘扫描的方式有两种:1.行列扫描,2.逐行/逐列扫描
其中行列扫描适用于8个io口接到了单片机8个连续的io口,则可以进行行列扫描
逐行/逐列扫描 适用于矩阵键盘接到了任意的io口,则使用逐行,逐列扫描
接下来我们分别介绍这两种方式:
行列扫描:
原理:
先从p1口的高四位(四个行)输出高电平,低四位(四个列)输出低电平,假设有按键按下,从p1口的高四位读取键盘状态。判断高四位的四行哪一行变成了低电平,就知道是第几行,再从p1口的低四位(四个列)输出高电平,高四位(四个行)输出低电平,从p1口的低四位读取键盘状态。判断低四位的四列哪一行变成了低电平,就知道是第几列,将两次读取结果组合起来就可以得到当前按键的特征编码。使用上述方法我们得到16个键的特征编码。
红色高电平,蓝色低电平
详解:
据矩阵键盘的原理图可知,如果矩阵键盘的8个io口连接到了连续的一个一个人p10-p17上,当没有按键按下时,将p1口的p1^0 和 p13 置高电平 p14 和 p17 置低电平 ,也就是将4个行的io口置高,4个列的io口置低。也就是p1=0x0f(0000 1111);
如果这时候有按键按下那么p1^0 和 p13 就有一个会变成低电平。因此p1的值就不等于0x0f,按下按键所在的行就会变成低电平,这是就可以判断有按键按下。
将对应p1口的值和0x0f(00001111)相 与& 则可以得到高四位第几行变成了0
按位“与”&(双目运算符):仅当两个操作数都为1时,结果为1,否则为0。
比方说:0&0=0;0&1=0;1&0=0;1&1=1
即:两个同时为1,结果为1,否则为0
比方说按下的第一行第一列 1x1
例:
0000 1110------------- 按下1x1之后p1的值
& 0000 1111------------- 0x0f
----------
0000 1110------------- 最后得到的结果,第一行为0
再给p1口赋值0x0f。将p1口的p1^0 和 p13 置低电平 p14 和 p17 置高电平 ,也就是将4个低的io口置高,4个列的io口置高然后读取低四位的电平, 此时的p1口 (1111 0000)
读取此时的p1口 和0xf0(11110000)相 与& 则可以得到低四位第几列变成了低电平
比方说我们按下的是1x1 按键,也就是第一行第一列,这时在按下之后可以看到p1.0和p1.3都是低电平,将row=p1&0x0f;(行的值) 和 col=p1&0xf0;//列值 进行相加,就可以得到按下的是那个按键
1x1:(一行一列)
row=p1&0x0f =  0000 1110
col=p1&0xf0=     1110 0000
row+col=           1110 1110   = 0xee
低电平0表示对应的行列按下
可以看到下方的p1.0和p1.4变成了低电平
2x2:(二行二列)
row=p1&0x0f =  0000 1101
col=p1&0xf0=     1101 0000
row+col=           1101 1101   = 0xdd
3x4:(三行四列)
row=p1&0x0f =  0000 1011
col=p1&0xf0=     0111 0000
row+col=           0111 1011   = 0x7b
这样就可以得到所有的16个按键的数值,具体代码如下:
unsigned char keyscan(){
unsigned char key,row,col;
p1=0x0f;
if(p1!=0x0f){
delay(10);//去抖
if(p1!=0x0f){
row=p1&0x0f;//确保端口值正确(行的值)
p1=0xf0;
col=p1&0xf0;//列值
}
while((p1&0xf0)!=0xf0);//判断键是否抬起
}
switch(row+col){
case 0xee:key=0;break;
case 0xde:key=1;break;
case 0xbe:key=2;break;
case 0x7e:key=3;break;
case 0xed:key=4;break;
case 0xdd:key=5;break;
case 0xbd:key=6;break;
case 0x7d:key=7;break;
case 0xeb:key=8;break;
case 0xdb:key=9;break;
case 0xbb:key=10;break;
case 0x7b:key=11;break;
case 0xe7:key=12;break;
case 0xd7:key=13;break;
case 0xb7:key=14;break;
case 0x77:key=15;break;
}
return key;
}
运行效果图:
逐行/列扫描:
逐行,逐列扫描的本质和行列扫描比较类似,本质是给某一行/某一列,低电平,其余七个全部为高电平,这时候读取电平变换,有电平变低表示按键按下,即可读取按键数据。
比如逐行扫描:
置第1行为低电平,其余n-1行和n列为高电平,
读取列线数据,列线有低电平表示此行有按键按下,比如按下的是1行三列(1x3),那么第三列的列线io口就为低电平。
置第2行为低电平,其余n-1行和n列为高电平,,读取列线数据,列线有低电平表示此行有按键按下。
以此类推,进行逐行扫描。
根据行线列线的电平不同可以识别是否有按键按下,哪一个按键按下,获取按键号。(n) 根据按键号跳转至对应的按键处理程序。
用我们的p1口来进行举例:
首先,给p1赋值 p1=0xfe(1111 1110);,这时p1.0为低电平,p1.1~p1.7为高电平,如果这时候有按键按下那么四个列线,p1.4,p1.5,p1.6,p1.7就有一个列会变成低电平。因此p1的值就不等于0xfe,这是就可以判断有按键按下。
然后延时一段时间去抖动,然后给p1赋值0xfd(1111 1101),也就是p1.1为低电平,其他为高电平,这时如果有在p1.1线上的p1.4,p1.5,p1.6,p1.7有按键按下,那么就会出现低电平,从而判断哪个按键按下;如果没有那么就给p1赋值0xfb(1111 1011),也就是p1.2为低电平,其他为高电平.,相同方法判断是否有按键按下;······如此类推,一共四次检测。
比如当第1行有按键按下时p1的相应值为:
  1x1(11101110=0xee)
  1x2(11011110=0xde)
  1x3(10111110=0xbe)
  1x4(01111110=0x7e)
第2行有按键按下时p1的相应值为:
     2x1(11101101=0xed)
     2x2(11011101=0xdd)
     2x3(10111101=0xbd)
     2x4(01111101=0x7d)
将p1^2输出低电平,其他的引脚都输出高电平,即p1=0xfb,那么当第3行有按键按下时p1的相应值为:
     3x1(11101011=0xeb)
     3x2(11011011=0xdb)
     3x3(10111011=0xbb)
     3x4(01111011=0x7b)
最后可得第四行的相对应值为:
     4x1(11100111=0xe7)
     4x2(11010111=0xd7)
     4x3(10110111=0xb7)
   4x4(01110111=0x77)
那么最后我们可以得到代码:
/*****************************************************************************
** 函数名称:keyscan
** 功能描述:按键获取函数
******************************************************************************/
void keyscan(void)
{
p1=0xfe;
   temp=p1;
   temp=temp&0xf0;
   if(temp!=0xf0)
   {
delay(10);
      if(temp!=0xf0)
      {
temp=p1;
switch(temp)
{
case 0xee:key=0;break;
case 0xde:key=1;break;
case 0xbe:key=2;break;
case 0x7e:key=3;break;
         }
         while(temp!=0xf0) 
{
temp=p1;
temp=temp&0xf0;
         }
}
}
p1=0xfd;
temp=p1;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10);
if(temp!=0xf0)
      {
temp=p1;
switch(temp)
{
case 0xed: key=4;break;
case 0xdd:key=5;break;
case 0xbd:key=6;break;
case 0x7d:key=7;break;
         }
         while(temp!=0xf0)
         {
           temp=p1;
           temp=temp&0xf0;
         }
      }
}
p1=0xfb;
temp=p1;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10);
      if(temp!=0xf0)
      {
temp=p1;
switch(temp)
{
case 0xeb:key=8;break;
case 0xdb:key=9;break;
case 0xbb:key=10;break;
case 0x7b:key=11;break;
         }
beep=0;delay(50);beep=1;
while(temp!=0xf0)
         {
temp=p1;
temp=temp&0xf0;
         }
      }
   }
p1=0xf7;
   temp=p1;
   temp=temp&0xf0;
   if(temp!=0xf0)
   {
      delay(10);
      if(temp!=0xf0)
      {
temp=p1;
switch(temp)
{
case 0xe7:key=12;break;
case 0xd7:key=13;break;
case 0xb7:key=14;break;
case 0x77:key=15;break;
         }
while(temp!=0xf0)
         {
temp=p1;
temp=temp&0xf0;
         }
      }
}
}
运行效果图:


国家不断推动智能制造融合发展,2020年初步估计达2.5万亿元
Redmi K30系列采用6.67英寸全面屏 及第二代挖孔屏技术
全球单晶硅片行业的发展经历了兴盛——低迷——逐渐复苏
摄像头不凸出!iPhone8再出“美到爆”概念图
ADL5317+AD8305典型应用电路
单片机矩阵键盘的原理以及代码编写
台积电计划2021年危险生产3nm Apple Silicon芯片
2023年度新能源汽车销量成绩单公布:谁的掌控力会更强?
等离子显示器知识问答
使用Arduino和LM317制作一个低电阻表
科学家研制出最长光纤激光
智能传感器和多功能传感器解析
金属膜电容和其他电容的简单分类
单片机介绍与应用领域
关于倒装COB显示屏与LED小间距的比较
推荐!适用于路由器的晶振
高压线路的作用_高压线路的优缺点
电池要“实修”几点要点不能不知道
声发射技术在立式储罐罐底检验与评价中的应用
CSP及无铅技术是怎么一回事