1.按键介绍

这也许是第一个接触的单片机输入设备了,而且这玩意能玩很久,基本上有啥没啥都能加一个上去,执行某些你想达到的功能。

这里的独立按键也叫“轻触式按键”,这是我们平日里面见到的大部分按键,(这里的轻触式机械层面上的接触,而不是感光或者电容屏),所以按键的特性其实是一种机械性质

这里的初始就是有没有按下去。但是,查阅资料发现,绿色导线是永久接通的?那我还真没试过,所以用按键的时候建议用万用表测这个按键哪两个引脚是嫩能够起到开关作用,某个自锁开关也是如此。

对于按键,还有一个问题要解决,那就是消除抖动。

抖动是啥意思?理想状态下按键按下去电平时这样的:

可实际上是:

这是由于通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。 只有一小段是平整的,其余抖动的部分都会给单片机捕获判断带来影响。

所以消除抖动,哦,应该是规避抖动范围(只不过我们都习惯叫消除抖动而已)其实就是对单片机捕获低电平判断(或者也有反向捕获高电平判断)。

2.消抖的方式介绍

2.1按键消抖原理

首先来认识一下按键延时消抖,按键由于是机械结构,按下的时候难免产生抖动,一般抖动会在按下的时候与松开的时候产生,抖动时间大概是10ms,通常我们手动按键然后释放,这个动作中稳定闭合的时间超过了20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专门的去抖动芯片,但通常我们采用延时的方法就可以解决抖动问题。

于是针对按键抖动就有了下面几个章节的解决方法。

3.普通按键消抖教学

在使用51单片机读取按键转态时,将按键连接的单片机接口赋值1(这种方式适合51单片机,但不一定适合其他单片机),如果按键按下,则端口被拉低。因此,通过读取单片机接口的电平状态就可以判断按键是否按下,如果输入时高电平,则按键没有按下;如果输入是低电平,则按键按下。

由上面的讲解我们已知,按键的按下会出现信号的抖动,这会造成单片机的误判断。可能造成按下一下按键却判断成按下了多次按键。为了得到正确的结果,要对按键进行去抖。去抖分为硬件去抖和软件去抖两种。

硬件去抖就是在按键的两端加上一个电容,软件去抖则不需要增加硬件成本。只需要软件处理。软件去抖的具体方法是:当判断有按键按下时,程序延时一段时间,跳过这个抖动区域,之后再检测按键状态。如果再次检测时输入时高电平,说明是抖动或干扰造成的。如果输入是低电平,说明确实有按键按下。

下面的代码是对按键按下的典型判断语句,先判断KEY的值是否为0,如果为0则延时10ms,然后再次读取KEY的值,依然为0则判断为按键按下,进行按键按下的处理代码,最后等待按键松开后退出。

按键软件延时函数消抖一般分为4步:

1、判断按键是否按下

2、消抖

3、再次判断按键是否按下

4、等待按键松开

if(KEY==0)     //按键KEY按下
{delay1ms(10);  //延时10ms去抖if(KEY==0)      //再次判断按键KEY按下{//加入处理代码}  while(KEY==0);  //等待按键松开
}

延时函数按键消抖

检测出键闭合后执行一个延时程序,5ms~10ms(取决于机械特性)的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。

优点:简单方便

缺点:程序在空跑浪费CPU资源、不够精准

4.通过普通按键消抖控制LED(有BUG版本)

接下来我们就通过软件延时函数消抖的方式来让独立按键去操控led灯的亮灭。

示例程序如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
//定义按键端口
sbit key = P3^4;
//定义LED灯端口
sbit led = P1^0;
//延时函数声明
void delay(uint xms);
//程序入口
void main()
{led=0;while(1){if(key==0)//第一次判断{delay(1000);   //延时10ms 消除抖动while(key==0); //第二次判断delay(1000);led=~led;  //led取反控制亮灭}}
}
void delay(unsigned int i)
{while(i--);
}

5.引入外部中断形式的按键

接下来我们引入了外部中断来检测按键,用定时器来进行延时,51单片机的外部中断0引脚接一只按键,该按键通过上拉电阻接到电源,即没有按键发生时单片机检测到的是高电平,当按键按下时单片机检测到的是低电平。这样的好处就是可以不占用cpu资源,提高cpu的利用率。

外部中断消抖原理

1、判断按键是否按下,

2、若检测到有按键按下,则开启定时器,开启定时中断,定时时间为10ms 左右,使得按键按下10ms后进入定时中断,进入中断的时候按键抖动时间已过

3、在定时器中断中再次判断按键是否按下

4、关闭定时器,等待按键松开

如图所示:

图中 t1到t3 这一段时间就是按键抖动,是需要消除的。设置按键为下降沿触发(由按键的电路决定),因此会在 t1、t2 和 t3 这三个时刻会触发按键中断,每次进入中断处理函数都会重新开器定时器中断,所以会在 t1、t2 和 t3 这三个时刻开器定时器中断。

但是 t1 ~ t2 和 t2 ~ t3 这两个时间段是小于我们设置的定时器中断周期(也就是消抖时间,比如 10ms),所以虽然 t1 开启了定时器,但是定时器定时时间还没到 t2 时刻就重置了定时器,最终只有 t3 时刻开启的定时器能完整的完成整个定时周期并触发中断,我们就可以在定时器中断处理函数里面做按键处理了。

优点:节约CPU资源

缺点:消耗一个定时器

示例程序如下:

#include <reg51.h>
sbit key=P3^2;  //定义key为P3.2
sbit led=P1^0; //定义LED为P1.0
void main(void)
{TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。TH0=(65536-10000)/256;  //给定时器赋初值,定时10msTL0=(65536-10000)%256;ET0=1;//打开定时器0中断允许TR0=0;//关闭定时器IT0=1;//跳变沿出发方式(下降沿)EX0=1;//打开INT0的中断允许。 EA=1;//打开总中断       while(1){}
}
/*定时器中断*/
void Timer0() interrupt 1
{TH0=(65536-10000)/256;    //给定时器赋初值,定时10msTL0=(65536-10000)%256;  TR0=0;//关闭定时器if(key==0)   //再次判断按键是否按下{  led=!led;  //控制led状态取反}
}
/*外部中断0*/
void Int0() interrupt 0     //外部中断0的中断函数
{TR0=1;//打开定时器
}

注:

1.消抖的定时时间由按键的机械特性决定,多调试。

2.中断处理函数处理完要清除相应中断标志。

3.进行按键处理后要清零和失能定时器,否则无按键按下时也在定时。

4.由于实验室的板子外部中断端口接了矩阵按键和独立按键,所以以上代码要实现需要同时按下S1和KEY9。

6.状态机写法

延时法简单实用,编程也容易,使用非常普遍。但是这个办法有些缺点,1是加上延时后,在延时期间单片机什么也没干,就在那里兜圈子耗时间,如果这时有其他事情需要处理也只好放一放,降低了运行效率。2是对一些需要较复杂按键功能的情况例如区别长按键和短按键难于实现。

所以我们也可以使用外部中断法,不按键就不查询,直到按键触发外部中断。但是单片机通常外部中断口很少而按键较多,给应用造成不便。

下面就来介绍一个新的按键查询方法:状态机法。

原理如下:

1,给按键设定3种状态:状态0:无按键,状态1:已经按下,状态2:已经释放。按键可以一直处于状态0,也可以由状态0转为状态1,也可以由状态1转为 状态2,然后恢复到状态0。如此顺序循环。

2,通过定时(例如定时器中断)每隔一段时间(例如10毫秒)检查一下按键 状态,根据上次检查的状态和当前的状态比较,来确定应该做什么。在上述时间间隔内,单片机就可以执行其他任务。一旦确认按键成立,就可以立即找出键码并进 行随后的键码处理程序而无需等待按键释放,加上适当的处置也可以对比较复杂的按键进行处理。

下面以一个具体的程序,详细解释怎么实现:读键函数key_scanf()。无参数,返回键码。

实例程序:

#include <reg52.h>
#define uint unsigned int                         //对数据类型进行声明定义
#define uchar unsigned char
//读取按键
uchar key_scanf()
{static int Key_on_off = 0 ;//按键自锁变量uchar  num , temp ;num = P2 ;            //将P2的值存在变量num中num &= 0xf0 ;    //将低四位清0if(num != 0xf0){if(Key_on_off == 0){Key_on_off = 1 ;switch(num){//返回按键的编码case 0xe0 : temp = 1 ; break ; case 0xd0 : temp = 2 ; break ; case 0xb0 : temp = 3 ; break ; case 0x70 : temp = 4 ; break ; }}   }elseKey_on_off = 0 ;return temp  ;

7.用状态机写法的按键改写第五节的可按键切换数码管时钟

参考程序如下:

#include <reg52.h>#define uint unsigned int                          //对数据类型进行声明定义
#define uchar unsigned charsbit LSA=P2^4;
sbit LSB=P2^5;
sbit LSC=P2^6;
sbit LSD=P2^7;uchar DT_s = 0;                               //秒计时
uchar DT_min = 0;                            //分计时/************************************************
函数名             :delay1ms
函数功能            :t=1,大约延时1ms
************************************************/
void delay(unsigned int i)
{while(i--);
}
void smg(unsigned char x,y)
{unsigned char a[17]={~0x3f,~0x06,~0x5b,~0x4f,~0x66,~0x6d,~0x7d,~0x07,~0x7f,~0x6f,~0x77,~0x7c,~0x39,~0x5e,~0x79,~0x71};//段选switch(x){case 1:LSA=0;LSB=1;LSC=1;LSD=1;break;//位选case 2:LSA=1;LSB=0;LSC=1;LSD=1;break;case 3:LSA=1;LSB=1;LSC=0;LSD=1;break;case 4:LSA=1;LSB=1;LSC=1;LSD=0;break;}P0=a[y];delay(100);
}
void smg1(unsigned char x,y)//末尾加上小数点
{unsigned char a[17]={~0x3f&0x7f,~0x06&0x7f,~0x5b&0x7f,~0x4f&0x7f,~0x66&0x7f,~0x6d&0x7f,~0x7d&0x7f,~0x07&0x7f,~0x7f&0x7f,~0x6f&0x7f,~0x77&0x7f,~0x7c&0x7f,~0x39&0x7f,~0x5e&0x7f,~0x79&0x7f,~0x71&0x7f};//段选且加上小数点switch(x){case 1:LSA=0;LSB=1;LSC=1;LSD=1;break;//位选case 2:LSA=1;LSB=0;LSC=1;LSD=1;break;case 3:LSA=1;LSB=1;LSC=0;LSD=1;break;case 4:LSA=1;LSB=1;LSC=1;LSD=0;break;}P0=a[y];delay(100);
}
/************************************************
函数名             :Timer0Init
函数功能            :定时/计数器0中断初始化
*************************************************/
void Timer0Init()
{TMOD = 0x01;      //选择T0定时/计数器,工作在方式1,16位计数器        TH0 = 0xFC;            //计数初始值,计数从64536开始,计1000个数,完成一次计数,时间为1msTL0 = 0x18;        ET0 = 1;           //定时/计数器0中断允许位EA = 1;              //总中断
}/************************************************
函数名             :S3
函数功能            :按下按钮S3时,秒表归零
*************************************************/
void S3()
{DT_s = 0;         //秒归零DT_min = 0;           //分归零TR0 = 0;          //运行控制位清0,关闭定时器
}
//读取按键
uchar key_scanf()
{static int Key_on_off = 0 ;//按键自锁变量uchar  num , temp ;num = P3 ;            ///将P2的值存在变量num中num &= 0xf0 ;   //将低四位清0if(num != 0xf0){if(Key_on_off == 0){Key_on_off = 1 ;switch(num){//返回按键的编码case 0xe0 : temp = 1 ; break ; case 0xd0 : temp = 2 ; break ; case 0xb0 : temp = 3 ; break ; case 0x70 : temp = 4 ; break ; }}   }elseKey_on_off = 0 ;return temp  ;
}
void DisplayKey()
{int key;key=key_scanf();if(key)    //确认按键被按下{switch(key){case 1:                //S1(P3^2)被按下TR0 = 1;    //定时器0运行控制位为1,启动定时器0break;case 2:                //S2(P3^1)被按下TR0 = 0;    //定时器0运行控制位为0,关闭定时器0break;case 3:                //S3(P3^3)被按下S3();break;}}
}
/*******************************************************************
函数名             :DigDisplay
函数功能            :数码管动态扫描函数,循环扫描8个数码管显示
********************************************************************/
void DigDisplay(uchar s,uchar min)
{smg(4,s%10);smg(3,s/10);smg1(2,min%10);smg(1,min/10);
}/**********************************************************
函数名             :主函数
函数功能            :无
**********************************************************/
void main(void)
{P0 = 0x00;          //读端口前写1P3 = 0xFF;         //读端口前写1Timer0Init();   //定时器中断初始化函数while(1){
DisplayKey();DigDisplay(DT_s,DT_min);      //数码管显示函数}
}/**********************************************************
函数名             :Timer0
函数功能            :定时器计数
**********************************************************/
void Timer0() interrupt 1
{static uint count_s;   static uint count_min;      TH0 = 0xFC;                //计数值初始化,从64536开始计数,计满时为65536,溢出时即为 1msTL0 = 0x18;count_s++;                     //秒计数count_min++;                 //分计数if(count_s==1000)            //计数到1s时,秒计数器开始工作{count_s = 0;      //秒计数清零DT_s++;            //显示秒计数值自增if(DT_s>59)        //秒数最大为59{DT_s = 0;}}if(count_min==60000)         //计数到60000ms时,秒计数器开始工作{count_min = 0;           //分计数清零DT_min++;                  //显示分计数值自增if(DT_min>59)              //分数最大为59{DT_min = 0;}}

第五节 按键的三种用法相关推荐

  1. 微课系列(6):Python关键字else的三种用法

    技术要点:Python关键字else在选择结构.循环结构和异常处理结构中的三种用法. ----------相关阅读---------- 教学课件 1900页Python系列PPT分享一:基础知识(10 ...

  2. Python扩展库numpy中where()函数的三种用法

    第一种用法:只给where()函数传递一个数组作为参数,返回其中非0元素的下标. 第二种用法:给where()函数传递一个包含True/False值的数组,返回该数组中True值的下标,结合numpy ...

  3. 很多人不知道的Python 炫技操作:海象运算符的三种用法

    Python 炫技操作--海象运算符的三种用法 1. 第一个用法:if/else 2. 第二个用法:while 3. 第三个用法:推导式 Python 版本发展非常快,如今最新的版本已经是 Pyhto ...

  4. using 的三种用法

    using 有哪三种用法? 1)引入命名空间. 2)给命名空间或者类型起别名. 3)划定作用域.自动释放资源,使用该方法的类型必须实现了 System.IDisposable接口,当对象脱离作用域之后 ...

  5. static的三种用法,定义静态变量,静态函数,静态代码块!

    static的三种用法,定义静态变量,静态函数,静态代码块! 1.定义静态变量 class PersonStatic { //静态变量的特点 //1.静态变量无需生成对象就可被调用,可以使用类名和对象 ...

  6. Vue——基本的代码结构和插值表达式、v-cloak||Vue指令之v-text和v-html||v-bind的三种用法||Vue指令之v-on

    Vue的基本代码 <!DOCTYPE html> <html lang="en"><head><meta charset="UT ...

  7. SQL、LINQ、Lambda 三种用法(转)

    SQL.LINQ.Lambda 三种用法 颜色注释: SQL LinqToSql Lambda QA 1. 查询Student表中的所有记录的Sname.Ssex和Class列. select sna ...

  8. 标准SQL的update语句三种用法

    标准SQL的update语句三种用法 一.环境: MySQL-5.0.41-win32 Windows XP professional   二.建立测试环境:   DROP TABLE IF EXIS ...

  9. C++容器的insert()函数有以下三种用法: 最终*it=val;

    C++容器的insert()函数有以下三种用法: 最终*it=val; //用法1:在指定位置it前"插入"值为val的元素,返回指向这个元素的迭代器, iterator inse ...

最新文章

  1. 【学习总结】之 3Blue1Brown系列
  2. 如何摆脱穷打工的命?
  3. C++ 设计People类-1
  4. al-khaser 调试器,虚拟机检测工具
  5. 徒手撸了个markdown笔记平台
  6. SAP License:GR/IR
  7. ibatis sql_Map中出现异常:Cause: java.lang.RuntimeException: JavaBeansDataExchange could not instantiate..
  8. Synchronized方法锁、对象锁、类锁区别
  9. python: 正则表达式2--元字符与re模块
  10. 老毛桃u盘装系统linux,老毛桃U盘装系统综合教程
  11. 深度解析:印度软件产业为何如此发达
  12. 最美人间四月天,我用五个关键词带你回顾
  13. RK3399 GMAC驱动失败,打印如下log,DMA engine initialization failed 原因
  14. linux下删除oracle数据库实例
  15. 高效视频编码 (HEVC) -H.265(结构解析)
  16. 第21章、系统设定工具(网络与打印机)与硬件侦测
  17. 迷途中的融担:在下沉与专注中寻找未来
  18. 阿里云外包之行的反思
  19. ylinux系统找到软件_Linux【常用软件安装篇】
  20. 最新go语言学习教程

热门文章

  1. ffmpeg 多个视频先去掉声音合并后,再添加文字与音频
  2. 手电筒充放电管理芯片SM5401兼容HT4892 超低静态电流 低成本充放电管理芯片 移动电源芯片
  3. 现有2个空水壶,容积分别为5升和6升,问题是如何只用这2个水壶从池塘里取得3升的水?
  4. 考教育统计与测量可以带哪种计算机,2011年4月自考教育统计与测量试题及答案(1)...
  5. html5仿抖音切换效果,仿抖音视频滑动效果
  6. 联想电脑管家无耻之徒,无法关闭杀软
  7. Nginx服务使用stream模块TCP代理rtmp视频流
  8. 我劝你别再做流量的奴隶了...
  9. 【笔记篇】最良心的计算几何学习笔记(二)
  10. CF刷题-Codeforces Round #481-F. Mentors