一、独立式按键与矩阵式按键
常用的键盘电路有两种形式:独立式按键和矩阵式按键。
1. 独立式按键
独立式按键比较简单,它们各自与独立的输入线相连接,如上图所示。4条输入线接到单片机的 I / O 口上,当按下按键K1时,+5V依次通过电阻R1和按键K1最终进入 GND 形成一条通路,这条线路的全部电压都加到电阻R1上,引脚 KeyInl 就是一个低电平。当松开按键后,线路断开,不会有电流通过, KeyInl 和+5V应该是等电位,是一个高电平。因此,可以通过引脚 KeyInl 这个 I / O 口的高低电平来判断是否有按键按下。
实际上在单片机 I / O 口内部,也有一个上拉电阻。按键是接到P2口上,P2口上电默认是准双向 I / O 口,下面来简单了解一下准双向 I / O 口的电路,电路如上图所示。方框内的电路都是指单片机内部部分,方框外的就是外接的上拉电阻和按键。这个地方大家要注意一下,就是当要读取外部按键信号的时候,单片机必须先给该引脚写"1",也就是高电平,这样才能正确读取到外部按键信号,下面来分析一下缘由。
当内部输出是高电平时,经过一个反向器变成低电平, NPN 三极管不会导通,单片机 I / O 口从内部来看,由于上拉电阻 R 的存在,所以是一个高电平。当外部没有按键按下将电平拉低, VCC 也是+5V,它们之间虽然有两个电阻,但是没有压差,就不会有电流,线上所有的位置是高电平,这时就可以正常读取按键的状态了。
当内部输出是低电平时,经过一个反相器变成高电平, NPN 三极管导通,单片机的内部 I / O 口就是一个低电平,这时,外部虽然也有上拉电阻的存在,但是两个电阻是并联关系,不管按键是否按下,单片机的 I / O 口上输入单片机内部的状态都是低电平,因此无法正常读取按键的状态。
2. 矩阵式按键
在某一个系统设计中,当需要使用很多按键时,做成独立按键会大量占用I / O口,因此引入了矩阵按键的设计。如下图所示的矩阵按键电路原理图,使用8个 I / O 口实现16个按键。
如果理解了独立按键,就不难理解矩阵按键,下面来分析一下。图中一共有4组按键,我们只看其中一组,如下图所示。大家认真看一下,如果 KeyOutl 输出一个低电平,则KeyOut1相当于 GND ,此时,K1、K2、K3、K4相当于4个独立按键。当然这时KeyOut2、KeyOut3、KeyOut4必须都输出高电平,才能保证与它们相连的3路按键不会对这一路产生干扰,大家可以对照两张原理图分析一下。
3. 矩阵键盘的扫描
矩阵键盘的逐行逐列扫描方法可以先进行行扫描,也可以先进行列扫描。这取决于具体的实现方式和需求。在行扫描中,逐行检测按键状态,而在列扫描中,逐列检测按键状态。无论是先进行行扫描还是列扫描,最终都可以获取到按键的状态信息。 在逐行逐列扫描方法中,键盘的按键布局被组织成多行和多列的矩阵。每一行的按键都连接到一个行线上,而每一列的按键则连接到一个列线上。
扫描过程中可以从第一行开始,逐行扫描每一行。在扫描过程中,将当前行的行线置为低电平(相当于是接GND),然后检测该行上每一列的列线上是否有按键按下。如果有按键按下,相应的列线会被置低电平,就可以确定按下的按键是哪一个。
类似地,在逐列扫描方法中,扫描过程从第一列开始,逐列扫描每一列。将当前列的列线置为低电平(相当于接GND),然后检测每一行的行线上是否有按键按下。如果有按键按下,相应的行线会被置低电平,就可以确定按下的按键是哪一个。
#include <regx52.h>
#include "DELAY.h"
unsigned char MatrixKey()
{
unsigned char Keynumber=0;
P0=0xff; //初始化 使用P0寄存器控制 都接入高电平
P0_4=0; //第一列置0 逐次扫描该列的每一行
if(P0_0==0){Delay(20);while(P0_0==0);Delay(20);Keynumber=1;}//按键消抖
if(P0_1==0){Delay(20);while(P0_1==0);Delay(20);Keynumber=5;}
if(P0_2==0){Delay(20);while(P0_2==0);Delay(20);Keynumber=9;}
if(P0_3==0){Delay(20);while(P0_3==0);Delay(20);Keynumber=13;}
P0=0xff; //初始化 都接入高电平
P0_5=0; //第二列置0 逐次扫描该列的每一行
if(P0_0==0){Delay(20);while(P0_0==0);Delay(20);Keynumber=2;}
if(P0_1==0){Delay(20);while(P0_1==0);Delay(20);Keynumber=6;}
if(P0_2==0){Delay(20);while(P0_2==0);Delay(20);Keynumber=10;}
if(P0_3==0){Delay(20);while(P0_3==0);Delay(20);Keynumber=14;}
P0=0xff; //初始化 都接入高电平
P0_6=0; //第三列置0 逐次扫描该列的每一行
if(P0_0==0){Delay(20);while(P0_0==0);Delay(20);Keynumber=3;}
if(P0_1==0){Delay(20);while(P0_1==0);Delay(20);Keynumber=7;}
if(P0_2==0){Delay(20);while(P0_2==0);Delay(20);Keynumber=11;}
if(P0_3==0){Delay(20);while(P0_3==0);Delay(20);Keynumber=15;}
P0=0xff; //初始化 都接入高电平
P0_7=0; //第四列置0 逐次扫描该列的每一行
if(P0_0==0){Delay(20);while(P0_0==0);Delay(20);Keynumber=4;}
if(P0_1==0){Delay(20);while(P0_1==0);Delay(20);Keynumber=8;}
if(P0_2==0){Delay(20);while(P0_2==0);Delay(20);Keynumber=12;}
if(P0_3==0){Delay(20);while(P0_3==0);Delay(20);Keynumber=16;}
return Keynumber;
}
上述程序展现的只是简单的“矩阵键盘扫描”的函数模块,主要是用来帮助大家理解矩阵键盘扫描的原理及程序实现。
二、带返回值的函数
在单片机中,函数的定义格式与一般的C语言函数定义格式相同。以下是函数定义的一般格式:
返回类型 函数名(参数列表)
{
数据定义;
函数体;
return 返回值;
}
返回类型指定了函数返回的数据类型,可以是整数、浮点数、字符等。函数名是函数的标识符,用于调用该函数。参数列表是函数接受的输入参数,可以有零个或多个参数,每个参数由参数类型和参数名组成。函数体是函数的具体实现,包含了函数要执行的操作。return语句返回处理后的结果数据。
例如,下面是一个简单的函数定义示例:
int add(int a, int b)
{
int sum;
sum = a + b;
return sum;
}
这个函数名为add,返回类型为整数类型int,接受两个整数类型的参数a和b。函数体中计算了a和b的和,并将结果存储在sum变量中,最后通过return语句返回sum的值。