键盘分为编码键盘和非编码键盘。键盘上闭合键的识别由专用的硬件编码器实现, 并产生键编码号或键值的称为编码键盘, 如计算机键盘。而靠软件编程来识别的键盘称为非编码键盘, 在单片机组成的各种系统中, 用的较多的是非编码键盘。非编码键盘又分为独立键盘和行列式(又称矩阵式)键盘。

1 独立键盘检测

键盘实际上就是一组按键,在单片机外围电路中,通常用到的按键都是机械弹性开关,当开关闭合时,线路导通,开关断开时,线路断开,下图是几种单片机系统常见的按键。

弹性小按键被按下时闭合,松手后自动断开;自锁式按键按下时闭合且会自动锁住,只有再次按下时才弹起断开。通常我们把自锁试按键当做开关使用,比如实验板上的电源开关就使用自锁按键。单片机的外围输入控制用小弹性按键较好,单片机检测按键的原理是:单片机的I/O口既可作为输出也可作为输入使用,当检测按键时用的是它的输入功能,我们把按键的一端接地,另一端与单片机的某个I/O口相连,开始时先给该I/O口赋一高电平,然后让单片机不断地检测该I/O口是否变为低电平,当按键闭合时,即相当于该I/O口通过按键与地相连,变成低电平,程序一旦检测到I/O口变为低电平则说明按键被按下,然后执行相应的指令。
        按键的连接方法非常简单,如下图左所示,右侧I/O端与单片机的任一I/O口相连。按键在被按下时,其触点电压变化过程如下图右所示。

从上图右可看出,理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我们手动按下键然后立即释放,这个动作中稳定闭合的时间超过20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专用的去抖动芯片,但通常我们用软件延时的方法就能很容易解决抖动问题,而没有必要再添加多余的硬件电路。
        用示波器跟踪不同类型的开关,得到下图的波形,观察波形可以帮助我们对抖动现象有一个直观的了解。下图是一个小的按钮开关在闭合时的抖动现象,水平轴2ms/Div, 抖动间隙大约为10ms,在达到稳定状态前一共有6次变化,频率随时间升高。

下图是一个小型继电器在闭合时的抖动现象,水平轴2ms/Div,抖动间隙大约为8ms,在达到稳定状态前一共有13次变化。注意在开始和结束时,几个小的脉冲后伴随较高的频率。

编写单片机的键盘检测程序时,一般在检测按下时加入去抖延时,检测松手时就不用加了。按键检测流程图如下图所示。

实验板上独立按键与单片机链接的原理图如下图所示:

下面通过一个实例来讲解独立键盘的具体操作方法, 在实验板上实现如下描述。
        用数码管的前两位显示一个十进制数,变化范围为00~59,开始时显示00,每按下S2键一次,数值加1;每按下S3键一次,数值减 1;每按下S4键一次,数值归零;按下S5键一次, 利用定时器功能使数值开始自动每秒加1,再次按下S5键,数值停止自动加1,保持显示原数。新建文件Temp.c, 程序代码如下:
        共阳极数码管:

#include <reg52.h>         //52系列单片机头文件
#define uchar unsigned char
#define uint unsigned int
sbit key1 = P3^4;
sbit key2 = P3^5;
sbit key3 = P3^6;
sbit key4 = P3^7;
sbit dula = P2^5;         //申明U1锁存器的锁存端
sbit wela = P2^6;         //申明U2锁存器的锁存端
uchar code table[]={
0xC0, /*/"0"*/
0xF9, /*/"1"*/
0xA4, /*/"2"*/
0xB0, /*/"3"*/
0x99, /*/"4"*/
0x92, /*/"5"*/
0x82, /*/"6"*/
0xF8, /*/"7"*/
0x80, /*/"8"*/
0x90, /*/"9"*/
0x88, /*/"A"*/
0x83, /*/"B"*/
0xC6, /*/"C"*/
0xA1, /*/"D"*/
0x86, /*/"E"*/
0x8E, /*/"F"*/
0x89, /*/"H"*/
0xC7, /*/"L"*/
0xC8, /*/"n"*/
0xC1, /*/"u"*/
0x8C, /*/"P"*/
0xA3, /*/"o"*/
0xBF, /*/"-"*/
0xFF, /*/熄灭*/
0xFF /*/自定义*/
};
void delayms(uint xms);
uchar num,numt0;
void dispaly(uchar numdis)  //显示子函数
{uchar shi,ge;shi=numdis/10;ge=numdis%10;dula=1;P0=table[shi];  //送段选数据dula=0;P0=0xff ;     //送位选数据前关闭所有显示,防止打开位选锁存时wela=1;         //原来段选数据通过位选锁存器造成混乱P0=0x01   ;     //送位选数据wela=0;         //关闭U2锁存器delayms(5); //延时dula=1;P0=table[ge];  //送段选数据dula=0;P0=0xff  ;     //送位选数据前关闭所有显示,防止打开位选锁存时wela=1;         //原来段选数据通过位选锁存器造成混乱P0=0x02   ;     //送位选数据wela=0;         //关闭U2锁存器delayms(5); //延时
}
void init()  //初始化函数
{TMOD=0x01;  //设置定时器0为工作方式1(0000 0001)TH0=(65536-45872)/256;//装初值50ms一次中断TL0=(65536-45872)%256;//装初值50ms一次中断EA=1;       //开总中断ET0=1;   //开定时器0中断
}
void keyscan()
{if(key1==0){delayms(10);if(key1==0){num++;if(num==60)   //当到60时重新归0num=0;while(~key1);//等待按键释放}}if(key2==0){delayms(10);if(key2==0){if(num==0)   //当到0时重新归60num=60;num--;while(~key2);//等待按键释放}}if(key3==0){delayms(10);if(key3==0){num=0;         //清零while(~key3);//等待按键释放}}if(key4==0){delayms(10);if(key4==0){while(~key4);//等待按键释放TR0=~TR0;//启动或停止定时器0}}
}
void T0_time()interrupt 1
{TH0=(65536-45872)/256;//重装初值TH0=(65536-45872)%256;numt0++;             //num每加一次判断一次是否到了20次if(numt0==20){numt0=0;num++;if(num==60)num=0;}
}
void main()
{init();while(1){keyscan();dispaly(num);}}void delayms(uint xms)
{uint i,j;for(i=xms;i>0;i--)for(j=110;j>0;j--);
}

分析如下:
        1)上例中我们将定时器初始化部分、键盘扫描部分、数码管显示部分等分别写成独立的函数,在主函数中只需方便地直接调用它们就可以了,这样可使程序看上去简洁、明了,修改也很方便。
        2)大家在写程序时一定要注意代码的层次感,一级和一级之间用一个Tab 键隔开,尤其是初写程序的学员,不注意书写格式,程序从头到尾不加任何注释,级与级之间没有任何空格,当程序有问题,回头查询起来很不方便,大家从一开始就要养成良好的书写习惯,具体书写格式可参照本书例程。
        3)在键盘扫描程序中"delayms(10);" 即是去抖延时。在确认按键被按下后,程序中还有语句"while(!key,1);",它的意思是等待按键释放,若按键没有释放则key1始终为0,那么!key1即始终为1,程序就一直停止在这个while语句处,直到按键释放,key1变成了1,才退出这个while语句。通常我们在检测单片机的按键时,要等按键确认释放后才去执行相应的代码。若不加按键释放检测。由于单片机执行代码的速度非常快,而且是循环检测按键,所以当按下一个键时,单片机会在程序循环中多次检测到键被按下,从而造成错误的结果,大家可不加按键释放检测代码,编译程序下载后测试,当按下S2键时,会看到数码管上的数值变化很快,而且没有规律。

2 矩阵键盘检测

独立键盘与单片机连接时,每一个按键都需要单片机的一个I/O口,若某单片机系统需较多按键,如果用独立按键便会占用过多的I/O口资源。单片机系统中I/O口资源往往比较宝贵,当用到多个按键时,为了节省I/O口线,我们引入矩阵键盘。
        我们以4X4矩阵键盘为例讲解其工作原理和检测方法。将16个按键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样便一共有4行4列共8根线,我们将这8根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键。用这种方法我们也可实现3行3列9个键、5行5列25个键、6行6列36个键等。
        无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平。独立键盘有一端固定为低电平,单片机写程序检测时比较方便。而矩阵键盘两端都与单片机I/O口相连,因此在检测时需人为通过单片机I/O口送出低电平。检测时,先送一列为低电平,其余几列全为高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平,扫描列是否有低电平。这就是矩阵键盘检测的原理和方法。
        实验板上16个矩阵按键与单片机连接图如下图所示。

实验板上键盘区上面4行即为 16 个矩阵键盘,8条线分别与单片机的P3 口相连。
        从上图可知,矩阵键盘的4行分别与单片机的P3.0~P3.3相连,矩阵键盘的4列分别与单片机的P3.4~P3.7相连。
        在实验板上实现如下描述:实验板上电时,数码管不显示,顺序按下矩阵键盘后,在数码管上依次显示0~F,6个数码管同时静态显示即可,新建文件Temp.c,程序代码如下:

#include <reg52.h>         //52系列单片机头文件
#define uchar unsigned char
#define uint unsigned int
//sbit key1 = P3^4;
//sbit key2 = P3^5;
//sbit key3 = P3^6;
//sbit key4 = P3^7;
sbit dula = P2^5;         //申明U1锁存器的锁存端
sbit wela = P2^6;         //申明U2锁存器的锁存端
uchar code table[]={
0xC0, /*/"0"*/
0xF9, /*/"1"*/
0xA4, /*/"2"*/
0xB0, /*/"3"*/
0x99, /*/"4"*/
0x92, /*/"5"*/
0x82, /*/"6"*/
0xF8, /*/"7"*/
0x80, /*/"8"*/
0x90, /*/"9"*/
0x88, /*/"A"*/
0x83, /*/"B"*/
0xC6, /*/"C"*/
0xA1, /*/"D"*/
0x86, /*/"E"*/
0x8E, /*/"F"*/
0x89, /*/"H"*/
0xC7, /*/"L"*/
0xC8, /*/"n"*/
0xC1, /*/"u"*/
0x8C, /*/"P"*/
0xA3, /*/"o"*/
0xBF, /*/"-"*/
0xFF, /*/熄灭*/
0xFF /*/自定义*/
};
void delayms(uint xms);
uchar num,numt0;
void dispaly(uchar num)  //显示子函数
{P0=table[num];  //送段选数据dula=1;dula=0;}
void matrixkeyscan()
{uchar temp,key;P3=0xfe;temp=P3;temp=temp&0xf0;if(temp!=0xf0){delayms(10);temp=P3;temp=temp&0xf0;if(temp!=0xf0){temp=P3;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=P3;temp=temp&0xf0;}dispaly(key);//显示}}P3=0xfd;temp=P3;temp=temp&0xf0;if(temp!=0xf0){ delayms(10);temp=P3;temp=temp&0xf0;if(temp!=0xf0){temp=P3;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=P3;temp=temp&0xf0;}dispaly(key);//显示}}P3=0xfb;temp=P3;temp=temp&0xf0;if(temp!=0xf0){ delayms(10);temp=P3;temp=temp&0xf0;if(temp!=0xf0){temp=P3;switch(temp){case 0xeb:key=8;break;case 0xdb:key=9;break;case 0xbb:key=10;break;case 0x7b:key=11;break;}while(temp!=0xf0)//等待按键释放{temp=P3;temp=temp&0xf0;}dispaly(key);//显示}}      P3=0xf7;temp=P3;temp=temp&0xf0;if(temp!=0xf0){ delayms(10);temp=P3;temp=temp&0xf0;if(temp!=0xf0){temp=P3;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=P3;temp=temp&0xf0;}dispaly(key);//显示}}
}       void main()  //zhu函数
{P0=0;dula=1;dula=0;P0=0xc0;wela=1;wela=0;while(1){matrixkeyscan();}
}
void delayms(uint xms)
{uint i,j;for(i=xms;i>0;i--)for(j=110;j>0;j--);
}

分析如下:
        1)进入主函数后,首先关闭所有数码管的段选,也就是不让数码管显示任何数字,接着位选中所有的数码管,以后再次操作数码管时只需要送段选数据即可,因为题目要求所有数码管都显示,接着进入while()大循环不停的扫描键盘是否有被按下。
        2)在检测矩阵键盘时我们用到这样几条语句:

P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{delayms(10);temp=P3;temp=temp&0xf0;if(temp!=0xf0){
...

上面这几句扫描的是第一行按键,搞明白这几句后,其他的都一样,每句解释如下:
                "P3=0xfe;"将第1行线置低电平,其余行线全部为高电平。
                "temp=P3;"读取P3口当前状态值赋给临时变量temp, 用千后面计算
                "temp=temp&0xf0;"将temp与0xf0进行”与“运算,然后再将结果赋给temp,主要目的是判断temp的高4位是否有0,如果temp的高4位有0,那么与0xf0’'与“运算后结果必然不等于0xf0;如果temp的高4位没有0,那么它与0xf0"与“运算后的结果仍然等于0xf0。temp的高4位数据实际上就是矩阵键盘的4个列线,从而我们可通过判断temp与0xf0"与“运算后的结果是否为0xf0来判断出第一行按键是否有键被按下。
                "if(temp!=0xf0)"将temp和上面P3口数据与0xf0"与“运算后的结果进行比较,如果temp不等于0xf0,说明有键被按下。
                "delayms(10);"延时去抖操作。
                "temp=P3;"重新读一次P3口数据
                "temp=temp&0xf0;"重新进行一次“与“运算
                "if(temp!=0xf0)"如果temp仍然不等千0xf0,这次确认第一行确实有键被按下了
        3)判断被按下的是该行第几列的键,我们用到了switch-case语句,关于该语句请看下一个知识点。在判断列线时我们再将P3口数据读一次,“temp=P3;” 如果读回P3口的值为0xee,则说明行线1与列线1都为低电平,那么它们的交叉处是第1个按键,如果读回P3口的值为0xde,则说明行线1与列线2都为低电平,那么它们的交叉处是第2个按键,用同样的方法可检测第一行的所有键,每检测到有按键被按下后我们可以将这个键的键值赋给一个变量,用来后期处理。用同样方法检测其他几行便可检测到矩阵键盘的所有按键。
        4)在判断完按键序号后,我们还需要等待按键被释放,检测释放语句如下:

while(temp!=0xf0)   //等待按键释放
{temp=P3;temp=temp&0xf0;
}

不断地读取P3口数据,然后和0xf0"与“运算,只要结果不等于0xf0,则说明按键没有被释放,直到释放按键,程序才退出该while语句。

2.1 switch-case语句

if语句一般用来处理两个分支。处理多个分支时需使用if-else-if结构,但如果分支较多,则嵌套的if语句层就越多,程序不但庞大而且理解也比较困难。因此,C语言又提供了一个专门用于处理多分支结构的条件选择语句,称为switch语句,又称开关语句。使用switch语句可直接处理多个分支(当然包括两个分支)。其一般形式为:

switch(表达式)
{case 常量表达式1: (注意这里,常量表达式l后面是冒号而不是分号)语句1;break;case 常量表达式2:语句2;break;••••••case常量表达式n:语句n;break;default:语句n+1;break;
}

switch语句的执行流程是:首先计算switch后面圆括号中表达式的值,然后用此值依次与各个case后的常量表达式比较,若switch后面圆括号中表达式的值与某个case后面的常量表达式的值相等,就执行此case后面的语句,当执行遇到break语句就退出switch语句;若圆括号中表达式的值与所有case后面的常量表达式都不等,则执行default后面的语句n+1,然后退出switch语句,程序转向switch语句后面的下一个语句。如下程序,可以根据输入的考试成绩的等级,输出百分制分数段:

switch(grade)
{case’A’: /注意,这里是冒号:并不是分号;/printf(“85-100\n”);break; /每一个case语句后都要跟一个break用来退出switch语句/case’B’: /每一个case后的常量表达式必须是不同的值,以保证分支的唯一性/printf(“70-84\n”);break;case’C’:printf(“60-69\n”);break;case’D’:printf("<60\n");break;default:printf(“error!\n”);
}

如果在case后面包含多条执行语句时,case后面不需要像if语句那样加大括号,进入某个case后,会自动顺序执行本case后面的所有语句。
        default总是放在最后,这时default后不需要break语句,并且default部分也不是必须的,如果没有这一部分,当switch后面圆括号中表达式的值与所有case后面的常量表达式的值都不相等时,则不执行任何一个分支,而是直接退出switch语句。此时,switch语句相当于一个空语句。如上面的矩阵键盘扫描程序,当没有任何键按下时,单片机不执行case中的任何语句。
        在switch-case语句中,多个case可以共用一条执行语句,如:

...
case’A’:
case’B’:
case’C’:
printf(">60\n");
break;
...

在A,B,C三种情况下,均执行相同的语句,即输出">60"。
        最开始那个例子中,如果把每个case后的break删除掉,则当 greak='A’时,程序从"printf(“85-100\n”);"开始执行,输出结果为:

85-100
70-84
60-69
<60
error

这是因为case后面的常量表达式实际上只起语句号作用,而不起条件判断作用,即“只是开始执行处的入口标号”。因此,一旦与switch后面圆括号中表达式的值匹配就从此标号处开始执行,而且执行完一个case后面的语句后,若没遇到break语句,就自动进入下一个继续执行,而不再判断是否与之匹配,直到遇到break语句才停止执行,退出switch语句。因此,若想执行一个case分支之后立即跳出switch语句,就必须在此分支的最后添加一个break语句。

STC51-键盘检测相关推荐

  1. STC51入门笔记(郭天祥C语言)---第四节:键盘检测原理及应用实现

    声明:本篇文章只是个人知识盲区.知识弱点.重点部分的归纳总结,望各位大佬不喜勿喷.梳理顺序是按照书籍的实际顺序梳理,转载请注明出处. 作者:sumjess 键盘分为编码键盘和非编码键盘.键盘上闭合键的 ...

  2. 51单片机的键盘检测原理

    一.独立键盘检测 1.按键的检测原理 单片机的I/O口既可以作为输出也可以作为输入使用,检测按键时用的是输入功能.把按键的一端接地,另一端与单片机的某个I/O口相连,开始时先给该I/O口赋一个高电平, ...

  3. 51单片机(五)独立键盘检测与矩阵键盘检测

    独立键盘检测与矩阵键盘检测 一.独立键盘检测 1.工作原理 2.举例 2.1 位定义 2.2 数码管显示 3.3 按键 2.4 中断服务函数 2.5 完整程序 二.矩阵键盘检测 1.工作原理 2.程序 ...

  4. 80C51单片机:5.独立键盘、矩阵键盘检测

    80C51单片机系列 80C51单片机 点亮第一个发光二极管,及流水灯案例 数码管显示及封装与消隐 中断.定时器 独立键盘.矩阵键盘检测 文章目录 80C51单片机系列 前言 独立键盘 独立键盘封装 ...

  5. linux查看键盘命令,linux下的键盘检测

    话说,仅仅是一个键盘检测就好麻烦.我还是懂的太少了... #include #include #include #include #include #include #include #include ...

  6. linux键盘检测软件,linux下的键盘检测

    话说,仅仅是一个键盘检测就好麻烦.我还是懂的太少了... #include #include #include #include #include #include #include #include ...

  7. 三、键盘检测原理及应用实现

    键盘检测原理及应用实现 视频链接:[3-4] 独立按键控制LED移位_哔哩哔哩_bilibili 对应课程P7-P10 键盘实际上就是一组按键,在单片机的外围电路中,通常用到的按键都是机械弹性开关,当 ...

  8. proteus矩阵按键计算机,矩阵键盘检测Proteus仿真电路图这里将16个按键按照4*4排列...

    该按钮可以说是51单片机项目开发的重要组成部分,是51单片机IO端口输入的重要方式.我们可以通过按下按钮来控制微控制器执行相应的程序,以获得所需的效果. 51单片机的键输入主要有两种.一种是独立密钥. ...

  9. 实验(五)键盘检测实验

    一.实验目的.内容.仪器 实验目的: 了解并区分独立按键,矩阵按键,以及其它模块的按键. 编写键盘的相关算法,了解相关概念 实验内容:进行独立按键以及矩阵按键的测试实验 实验仪器:7SEG-MPX8- ...

  10. javaGUI编写的键盘检测,利用GUI实现按键效果展示

    博客来源: 键盘用久了难免会用起来不舒服,偶尔有一下失灵,不晓得是自己的问题还是键盘的问题,写一个按键检测的小demo,来检测按键是否失灵 程序是使用java语言中的GUI 编写,话不多,直接上代码 ...

最新文章

  1. python官网下载安装教程-各种版本的Python下载安装教程
  2. 25个让Java程序员更高效的Eclipse插件
  3. Linux watch 监控系统状态
  4. 13.简述MYSQL的权限级别_MySQL权限级别
  5. BZOJ3170: [Tjoi2013]松鼠聚会 - 暴力
  6. oracle之创建和管理表
  7. 【英语学习】【医学】无机化学 - 化合物命名(3) - 含氧酸/无氧酸
  8. 【Es】Es 选主流程
  9. 《软件工程》团队第一阶段Sprint检查表
  10. tomcat日志设置与详解
  11. WIN7各种系统大全
  12. 小米 12 Ultra 搭载 3D ToF 摄像头和 Surge C2 ISP
  13. 计算机论文专著 论文集,学习计算机方面论文参考文献 学习计算机专著类参考文献有哪些...
  14. Hadoop 3.x 笔记(配置、命令、脚本、重要图示、代码实现)
  15. diy 单片机 自动浇花_自动浇花系统的设计及制作 基于Arduino
  16. 2023电工杯数学建模竞赛A题思路解析+代码+论文
  17. Jenkins针对不同的项目视图对不同的用户进行权限分配
  18. 修改配置文件不用重启tomcat
  19. 苹果xr黑屏转圈圈解决方法_iPhonexr黑屏转圈怎样解决?
  20. Mysql的课外补充与进阶

热门文章

  1. java随机打印一个数组元素_java 怎么随机打印自定义数组里面的字符串
  2. 服务器系统bsd,BSD操作系统大盘点:其它BSD变体
  3. pytorch修改tensor数据类型
  4. oracle视图无法使用rowid,请问:无法从没有键值保存表的连接视图中选择 ROWID 这个是什么原因啊?...
  5. 201671010460朱艺璇 实验三作业互评与改进报告
  6. VS和IIS的一些问题
  7. 算法笔记_231:网格中移动字母(Java)
  8. [转]G++与GCC的区别
  9. dispatch_after中时间的计算
  10. 如何用matlab画正态分布曲线