键盘检测原理及应用
独立键盘检测
- 通常用到的按键都是机械弹性开关,当开关闭合时,线路导通,开关断开时,线路断开
- 弹性小按键:按下时闭合,松手后自动断开
- 自锁式按键:按下时闭合且会自动锁住,只有再次按下时才会弹起断开
- 按键与单片机的连接方式如下图所示 :
- 从上图可以看出,理想波形与实际波形之间是有区别的,实际波形再按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。
- 按键消抖可分为硬件消抖和软件消抖。
- 硬件消抖:利用电容的充放电特性来对抖动过程中产生的电压毛刺进行平滑处理,从而实现消抖但实际应用中,这种方式的效果往往不是很好,而且还增加了成本和电路复杂度,所以实际中使用的并不多。
- 软件实现消抖:通过延时程序过滤。最简单的消抖原理,就是当检测到按键状态变化后,先等待一个10ms左右的延时时间,让抖动消失后再进行一次按键状态检测,如果与刚才检测到的状态相同,就可以确认按键已经稳定的动作了
代码:
/*
用数码管显示一十进制数,变化范围为00~59,初始显示00;
每按下K1数值加1,按下K2数值减1,按下K3数值归零,按下
K4利用定时功能开始自动每秒加1,再按下K4停止自动加1
*/
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit key1 = P3^0;
sbit key2 = P3^1;
sbit key3 = P3^2;
sbit key4 = P3^3;
//三八译码器的端口定义
sbit HC138A = P2^0;
sbit HC138B = P2^1;
sbit HC138C = P2^2;
uchar code table[] = {
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71
};
uchar numt0, num;
//延时函数
void delayms(uint xms){
uint i, j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
//键盘输入
void keyscan(){
if(key1==0){
delayms(10);
if(key1==0){
num++;
if(num==60)
num=0;
while(!key1); //等待按键释放
}
}
if(key2==0){
delayms(10);
if(key2==0){
if(num==0)
num=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; //启动或停止定时器
}
}
}
void dispaly(uchar num){
uchar shi, ge;
shi = num/10;
ge = num%10;
dula = 1;
P0 = table[shi];
dula = 0;
wela = 1;
HC138A = 0;
HC138B = 0;
HC138C = 0;
wela = 0;
delayms(5);
dula = 1;
P0 = table[ge];
dula = 0;
wela = 1;
HC138A = 1;
HC138B = 0;
HC138C = 0;
wela = 0;
delayms(5);
}
void main(){
//初始化
TMOD=0x01; //设置定时器0为工作方式1(M1M0为01)
TH0=(65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0=(65536-45872)%256;
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
while(1){
keyscan();
dispaly(num);
}
}
void T0_time()interrupt 1{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
numt0++; //num每加1次判断一次是否到20次
if(numt0==8){ //若到了20次,说明1秒时间到
numt0 = 0; //将num清空
num++; //num每一秒自动加1
if(num==60)
num=0;
}
}
矩阵键盘检测
- 原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到+5V电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平与此行线相连的列线电平决定。
- 2:行列扫描法原理:
- 第一步, 使行线为编程的输入线,列线是输出线,拉低所有的列线,判断行线的变化,如果有按键按下,按键按下的对应行线被拉低,否则所有的行线都为高电平。
- 第二步,在第一步判断有键按下后, 延时10ms消除机械抖动,再次读取行值,如果此行线还处于低电平状态则进入下 一步,否则返回第一步重新判断。
- 第三步,开始扫描按键位置,采用逐 行扫描,每间隔1ms的时间,分别拉低第一列,第二列,第三列,第四 列,无论拉低哪一列其他三列都为高电平,读取行值找到按键的位置,分别把行值和列值储存在寄存器里。
- 第四步,从寄存器中找到行值和列 值并把其合并,得到按键值,对此按键值进行编码,按照从第一行第一个一直到第四行第四个逐行进行编码,编码值从“0000” 至“1111” , 再进行译码,最后显示按键号码。数码管动态扫描原理。数码管动态扫描原理:数码管的 7 个段及小数点都是由 LED 块组成的,显示方式分为静 态显示和动态显示两种。数码管在静态显示方式时,其共阳管的位选 信号均为低电平,四个数码管的共用段选线 a、b、c、d、e、f、g、dp 分别与 CPLD 的 8 根 I/O 口线相连,显示数字时只要给相应的段选线送 低电平。数码管在动态显示方式时,在某一时刻只能有一个数码管被点亮显示数字,其余的处于非选通状态,位选码端口的信号改变时, 段选码端口的信号也要做相应的改变 ,每位显示字符停留显示的时间 一般为 1-5ms,利用人眼睛的视觉惯性,在数码管上就能看到相当稳定的数字显示。
代码:
/*
实验板上电是数码管不显示, 顺序按下矩阵键盘后,在数码管上依次显示0~F
*/
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
//三八译码器的端口定义
sbit HC138A = P2^0;
sbit HC138B = P2^1;
sbit HC138C = P2^2;
uchar code table[] = {
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71
};
//延时函数
void delayms(uint xms){
uint i, j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void display(uchar num){
P0 = table[num];
HC138A = 0;
HC138B = 0;
HC138C = 0;
}
void keyscan(){
uchar key, temp;
//扫描第一行
P1 = 0xfe;
temp = P1;
temp = temp&0xf0;
if(temp!=0xf0){
delayms(10); //延时去抖操作
temp = P1;
temp = temp&0xf0;
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;
}
display(key);
}
}
//扫描第二行
P1 = 0xfd;
temp = P1;
temp = temp&0xf0;
if(temp!=0xf0){
delayms(10); //延时去抖操作
temp = P1;
temp = temp&0xf0;
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;
}
display(key);
}
}
//扫描第三行
P1 = 0xfb;
temp = P1;
temp = temp&0xf0;
if(temp!=0xf0){
delayms(10); //延时去抖操作
temp = P1;
temp = temp&0xf0;
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;
}
//等待按键被释放
while(temp!=0xf0){
temp = P1;
temp = temp&0xf0;
}
display(key);
}
}
//扫描第四行
P1 = 0xf7;
temp = P1;
temp = temp&0xf0;
if(temp!=0xf0){
delayms(10); //延时去抖操作
temp = P1;
temp = temp&0xf0;
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;
}
display(key);
}
}
}
void main(){
P0 = 0;
while(1)
keyscan(); //不停的扫描键盘是否被按下
}