51单片机入门教程(4)——按键控制

岁酱吖の 提交于 2019-12-04 17:54:21


单片机与外界的信息交互主要有两大类,输入信息和输出信息。
之前的博客介绍了通过单片机控制LED灯和数码管向外界输出信息,该教程介绍单片机常用的输入设备——独立按键和矩阵键盘。

一、独立按键

1.1 独立按键的原理

独立按键一共有四个针脚,两个短针脚之间默认不导通,两个长针脚之间默认导通。实物图如图:
在这里插入图片描述

1.2 独立按键的仿真电路

在Proteus中对按键进行了简化,只有两个接线针脚。按键的一端接到单片机的IO口上,另一端与GND连接。当按键按下时,单片机的IO口与GND连接,端口电平被拉低。因此通过读取端口电平即可获知按键状态。
仿真电路如图:
在这里插入图片描述

1.3 按键消抖

关于按键抖动
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。即单片机在按键被按下的一瞬间检测到的信号是很多次的忽高忽低的电平信号,如图:
在这里插入图片描述
这种信号是不稳定的。因此,我们需要使用按键消抖的算法使单片机获取到正常稳定的信号。
为了避免在最不稳定的时候采集信号,常用的操作是延时,即当检测到低电平输入时,延时若干时间(常用20ms),再次采集信号,如果仍是低电平,则为按键被按下。
示例程序如下:

/**************************
* 说明: 按键被按下时led灯灭
**************************/
#include <reg52.h>
#define uint unsigned int 
#define uchar unsigned char
//定义按键端口
sbit key = P1^0;
//定义LED灯端口
sbit led = P2^0;
//延时函数声明
void delay(uint xms);
//程序入口
void main(){
  while(1){
    //第一次判断
    if(key == 0){
	  //延时20ms 消除抖动
	  delay(20);
	  //第二次判断
	  if(key == 0){
	    led = 0;	  //灯灭
	  }
	  else {
	    led = 1;	  //灯亮
	  }
	  //等待按键被松开
	  while(!key);  //当按键未松开时,key为0,执行该死循环。
	}
  }
}
void delay(uint xms)
{
  uint i,j;
  for(i = 0; i<xms; ++i)
    for(j = 0; j<110; ++j);
}

在这里插入图片描述
在这里插入图片描述

二、矩阵键盘

2.1 矩阵键盘原理

矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组。在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。其实物图如图:
在这里插入图片描述

2.2 矩阵键盘扫描原理

在Proteus上搭建矩阵键盘仿真电路如图:
在这里插入图片描述
首先搞清楚电路是怎样接的:
4×4的矩阵键盘,对于每一行,左端连在一起,四行按键因此引出四组线,接在P2_0 - P2_3上。如图:
在这里插入图片描述
同理,对于每一列,右端连在一起,四列按键因此引出四组线,接在P2_4 - P2_7上。如图:
在这里插入图片描述
组合起来就得到了第一张图片。


因此可以通过读取按键输入哪一行,哪一列从而确定按键的位置。
首先得到被按下的按键属于哪一行:
把P2_0 - P2_3 端口置为高电平,P2_4 - P2_7置为低电平,如果其中一行的某一个按键被按下,那么对应的P2_0 - P2_3 端口就会有一个端口被拉低,通过判断即可确定被按下按键属于哪一行。
如图,第一行有按钮被按下(端口红色为高电平,蓝色为低电平)
在这里插入图片描述
代码段如下:

P2 = 0x0f;       //P2_0 - P2_3 端口置为高电平,P2_4 - P2_7置为低电平
if (P2 != 0x0f)  //如果不为0x0f,说明有按键被按下
{
delay(20);
//读出端口从读出值来判断是哪一行
switch (P2)
{
  case 0x0e: x = 0; break;   //0000 1110
  case 0x0d: x = 1; break;   //0000 1101
  case 0x0b: x = 2; break;   //0000 1011
  case 0x07: x = 3; break;   //0000 0111
  default: break;
}

读取被按下的按键属于哪一列:
类似地:把P2_0 - P2_3 端口置为低电平,P2_4 - P2_7置为高电平,如果其中一列的某一个按键被按下,那么对应的P2_4 - P2_7 端口就会有一个端口被拉低,通过判断即可确定被按下按键属于哪一列。
如图,第三列有按钮被按下
在这里插入图片描述
代码段如下:

P2 = 0xf0;     //P2_0 - P2_3 端口置为低电平,P2_4 - P2_7置为高电平
if (P2 != 0xf0){  //如果不为0xf0,说明有按键被按下
  switch (P2)
  {
    case 0xe0: y = 0; break;
    case 0xd0: y = 1; break;
    case 0xb0: y = 2; break;
    case 0x70: y = 3; break;
    default: break;
  } 
}

2.3 矩阵键盘扫描程序

将读取到的矩阵键盘值(0 - F)显示在数码管上:
仿真电路如图:
在这里插入图片描述
代码如下:

/**************************
* 读取矩阵键盘值并显示至数码管上
**************************/
#include <reg52.h>
#define uint unsigned int 
#define uchar unsigned char

//共阳数码管编码表
uchar code table[] =
{0xc0,0xf9,0xa4,0xb0,
0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,
0xc6,0xa1,0x86,0x8e};

//函数声明
void delay(uint xms);   //延时函数
void display(uchar x);  //数码管显示函数,输入0-15,在数码管上显示0-F
uchar getKey();			//读取矩阵键盘函数,返回0-15

//程序入口
void main(){
  uchar keyValue;
  while(1){
	 keyValue = getKey();  //读取键盘值
	 display(keyValue);	   //数码管显示
  }
}

void delay(uint xms){
  uint i,j;
  for(i = 0; i<xms; ++i)
    for(j = 0; j<110; ++j);
}

void display(uchar x){
  P0 = table[x];
}

uchar getKey(){
  uchar x = 0, y = 0;
  uchar result = 0;
  // 第1回合第1步
  P2 = 0x0f; // 从IO口输出,写IO口
  if (P2 != 0x0f)// 从IO口输入,读IO口
  {
  // 读出的不是0x0f说明有按键被按下
  delay(20);
  // 第1回合第2步:读出端口从读出值来判断是哪一行
  switch (P2)
  {
    case 0x0e: x = 0; break;
    case 0x0d: x = 1; break;
    case 0x0b: x = 2; break;
    case 0x07: x = 3; break;
    default: break;
  }
  delay(10);
  // 第2回合第1步
  P2 = 0xf0;
  if (P2 != 0xf0){
    switch (P2)
    {
      case 0xe0:y = 0; break;
      case 0xd0: y = 1; break;
      case 0xb0: y = 2; break;
      case 0x70: y = 3; break;
      default: break;
    }
    // 经过2个回合后行x和列y都知道了,然后根据x和y去计算键值即可  
    }
  }
  result = x * 4 + y;
  return result;
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!