STC51-键盘检测
键盘分为编码键盘和非编码键盘。键盘上闭合键的识别由专用的硬件编码器实现, 并产生键编码号或键值的称为编码键盘, 如计算机键盘。而靠软件编程来识别的键盘称为非编码键盘, 在单片机组成的各种系统中, 用的较多的是非编码键盘。非编码键盘又分为独立键盘和行列式(又称矩阵式)键盘。
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-键盘检测相关推荐
- STC51入门笔记(郭天祥C语言)---第四节:键盘检测原理及应用实现
声明:本篇文章只是个人知识盲区.知识弱点.重点部分的归纳总结,望各位大佬不喜勿喷.梳理顺序是按照书籍的实际顺序梳理,转载请注明出处. 作者:sumjess 键盘分为编码键盘和非编码键盘.键盘上闭合键的 ...
- 51单片机的键盘检测原理
一.独立键盘检测 1.按键的检测原理 单片机的I/O口既可以作为输出也可以作为输入使用,检测按键时用的是输入功能.把按键的一端接地,另一端与单片机的某个I/O口相连,开始时先给该I/O口赋一个高电平, ...
- 51单片机(五)独立键盘检测与矩阵键盘检测
独立键盘检测与矩阵键盘检测 一.独立键盘检测 1.工作原理 2.举例 2.1 位定义 2.2 数码管显示 3.3 按键 2.4 中断服务函数 2.5 完整程序 二.矩阵键盘检测 1.工作原理 2.程序 ...
- 80C51单片机:5.独立键盘、矩阵键盘检测
80C51单片机系列 80C51单片机 点亮第一个发光二极管,及流水灯案例 数码管显示及封装与消隐 中断.定时器 独立键盘.矩阵键盘检测 文章目录 80C51单片机系列 前言 独立键盘 独立键盘封装 ...
- linux查看键盘命令,linux下的键盘检测
话说,仅仅是一个键盘检测就好麻烦.我还是懂的太少了... #include #include #include #include #include #include #include #include ...
- linux键盘检测软件,linux下的键盘检测
话说,仅仅是一个键盘检测就好麻烦.我还是懂的太少了... #include #include #include #include #include #include #include #include ...
- 三、键盘检测原理及应用实现
键盘检测原理及应用实现 视频链接:[3-4] 独立按键控制LED移位_哔哩哔哩_bilibili 对应课程P7-P10 键盘实际上就是一组按键,在单片机的外围电路中,通常用到的按键都是机械弹性开关,当 ...
- proteus矩阵按键计算机,矩阵键盘检测Proteus仿真电路图这里将16个按键按照4*4排列...
该按钮可以说是51单片机项目开发的重要组成部分,是51单片机IO端口输入的重要方式.我们可以通过按下按钮来控制微控制器执行相应的程序,以获得所需的效果. 51单片机的键输入主要有两种.一种是独立密钥. ...
- 实验(五)键盘检测实验
一.实验目的.内容.仪器 实验目的: 了解并区分独立按键,矩阵按键,以及其它模块的按键. 编写键盘的相关算法,了解相关概念 实验内容:进行独立按键以及矩阵按键的测试实验 实验仪器:7SEG-MPX8- ...
- javaGUI编写的键盘检测,利用GUI实现按键效果展示
博客来源: 键盘用久了难免会用起来不舒服,偶尔有一下失灵,不晓得是自己的问题还是键盘的问题,写一个按键检测的小demo,来检测按键是否失灵 程序是使用java语言中的GUI 编写,话不多,直接上代码 ...
最新文章
- python官网下载安装教程-各种版本的Python下载安装教程
- 25个让Java程序员更高效的Eclipse插件
- Linux watch 监控系统状态
- 13.简述MYSQL的权限级别_MySQL权限级别
- BZOJ3170: [Tjoi2013]松鼠聚会 - 暴力
- oracle之创建和管理表
- 【英语学习】【医学】无机化学 - 化合物命名(3) - 含氧酸/无氧酸
- 【Es】Es 选主流程
- 《软件工程》团队第一阶段Sprint检查表
- tomcat日志设置与详解
- WIN7各种系统大全
- 小米 12 Ultra 搭载 3D ToF 摄像头和 Surge C2 ISP
- 计算机论文专著 论文集,学习计算机方面论文参考文献 学习计算机专著类参考文献有哪些...
- Hadoop 3.x 笔记(配置、命令、脚本、重要图示、代码实现)
- diy 单片机 自动浇花_自动浇花系统的设计及制作 基于Arduino
- 2023电工杯数学建模竞赛A题思路解析+代码+论文
- Jenkins针对不同的项目视图对不同的用户进行权限分配
- 修改配置文件不用重启tomcat
- 苹果xr黑屏转圈圈解决方法_iPhonexr黑屏转圈怎样解决?
- Mysql的课外补充与进阶
热门文章
- java随机打印一个数组元素_java 怎么随机打印自定义数组里面的字符串
- 服务器系统bsd,BSD操作系统大盘点:其它BSD变体
- pytorch修改tensor数据类型
- oracle视图无法使用rowid,请问:无法从没有键值保存表的连接视图中选择 ROWID 这个是什么原因啊?...
- 201671010460朱艺璇 实验三作业互评与改进报告
- VS和IIS的一些问题
- 算法笔记_231:网格中移动字母(Java)
- [转]G++与GCC的区别
- dispatch_after中时间的计算
- 如何用matlab画正态分布曲线