先来说一下这三种模式的意思:

1. 短按模式:单击按键时,返回一次有效按键值;长按时也只返回一次有效按键值。这样可以有效地排除因不小心长按带来的返回多次有效按键,进而执行多次按键处理程序。

2. 长按模式: 单击按键时,返回一次有效按键;长按时,返回多次有效按键值。这样可以很快的调节某个较大的参数,比如时间的时分秒参数。

3. 按键释放模式:这个模式与短按模式是相对的。短按模式只要按键按下去,立即返回有效键值,进而试行按键处理程序;二按键释放模式,却是要等到按下键,释放之后,才会返回有效键值,进而执行按键处理程序。


接下来说一下扫描程序:
1)采用的是轮询的方式(非中断)
2)消抖动的方式:多次扫描,来确定按键值。下面的程序的是设定了5次。主要是根据扫描周期来确定,次数的多少。
注:
扫描周期:从进入按键扫描程序开始,直到到下一次进入按键扫描程序时 结束,之间所用的时间。

下面是整个按键扫描程序的源码,可以读一读,语句都很简单,而且每一句都有注释,一步一步看下去,应该能明白。
如果不明白,可以留言谈论。
以下是 KeyScan.c 文件的内容,

//======================================================
//KeyScan.c
//======================================================
//注意:该宏定义,定义在keyscan.h文件中
//#define KEYDEBOUNCE 0x05             //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发unsigned int g_uiCurrKey;            //当前按键值
unsigned int g_uiLastKey;            //上次按键值
unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动unsigned int g_uiPreKeyValue;        // 上一次的有效按键值
unsigned int g_uiKeyDown;            //键被按下,返回的键值。       作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。     作用:只有按下的按键被释放后,才返回按下的键值
unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值//P0口的低八位作为按键
//没有按键时,返回的是0xff,
void Int_Key_Scan(void)
{static unsigned short LastReadKey;        //上次从IO口读取的键值 ,注意是静态变量unsigned short CurrReadKey;               //当前从IO口读取的键值CurrReadKey = P0 & 0x00ff;                //获取当前的键值if(CurrReadKey == LastReadKey)            //如果当前读取的键值与上次从IO口读取的键值相同{if(g_uiKeyScanCount >= KEYDEBOUNCE)   //首先判断是否大于等于debounce的设定值(即是,是否大于等于设定的采样次数){//按住不放,多次响应g_uiCurrKey = CurrReadKey;                //如果是,将当前的读取值判定为有效按键值(如果是,在采样周期中,都是这个值,则判定为有效按键值)g_uiKeyContinue = g_uiCurrKey ;           //长按,多次响应 按键值//按住不放只响应一次if(g_uiPreKeyValue == g_uiCurrKey){g_uiKeyDown = 0xff;                    //没有键值}else{g_uiKeyDown = g_uiCurrKey;             //如果不同,按键有效,(就是第一次有效值时)}//按键释放时,按键值才有效     if(g_uiCurrKey == 0xff)                  //当有效按键值从非0到0的状态时(即是,从有按键到无按键,表示已经释放了),表示之前按键已经释放了{g_uiKeyRelease = g_uiPreKeyValue;}g_uiLastKey = g_uiCurrKey;               //记录上次有效按键值}else                                       //如果否,则debounce加一(如果否,则继续采样键值){g_uiKeyScanCount++;}}else                                          //如果当前读取的键值与上次从IO口读取的键值不同,说明按键已经变化{g_uiKeyDown = 0xff;                       //放开按键后第一次进入扫描程序,清零g_uiKeyDown.作用:消除一个BUG(你猜BUG是什么?)g_uiKeyScanCount = 0;                     //清零之前的按键的debounce计数LastReadKey = CurrReadKey;                //将当前读取的键值记录为上次读取的按键值}
}

以下是KeyScan.h文件内容

//======================================================
//KeyScan.h
//======================================================//宏定义
#define KEYDEBOUNCE 0x05             //消抖动,按键扫描次数。如果连续5次都是扫描的都是相同键值,则认为是有效键值,否则是误触发//声明变量
extern unsigned int g_uiCurrKey;            //当前按键值
extern unsigned int g_uiLastKey;            //上次按键值
extern unsigned int g_uiKeyScanCount;       //按键扫描计数,作用:消抖动extern unsigned int g_uiPreKeyValue;                //上一次的有效按键值
extern unsigned int g_uiKeyDown;            //键被按下,返回的键值。       作用:单次按键,单次返回有效键值;按住不放,也只返回被按下的一个键值
extern unsigned int g_uiKeyRelease;         //键被释放后,返回的键值。     作用:只有按下的按键被释放后,才返回按下的键值
extern unsigned int g_uiKeyContinue;        //键连续按键,重复返回的键值。 作用:只要按住不放,就会重复地返回相同键值//函数声明
void Int_Key_Scan(void);

使用注意:
1.作为按键使用的相应IO口,必须设置为输入模式(如果是51单片机的话,无需关心)
2.按键的硬件连接必须是一端接GND,一端接IO口。

下面介绍一下程序的使用方法:
这里以51单片机的 按键点亮和熄灭LED灯作为例子。
硬件:
1)按键使用单片机的P0端口
2)LED灯使用P1.0的IO口,低电平点亮

返回的按键值:
没有键按下, 返回键值是0xFF
如果P0.0按下,返回键值是0xFE
如果P0.1按下,返回键值是0xFD
如果P0.2按下,返回键值是0xFB
如果P0.3按下,返回键值是0xF7

如果P0.7按下,返回键值是0x7F

下面是例子的参考源码:

//======================================================
//main.c
//======================================================
#include "reg51.h"
#include "KeyScan.h"sbit LED = P1.0;       //定义LEDIO口
char time_10ms_ok;     // 10ms 定时标志void init_timer0(void)
{//定时器的初始化                          TMOD = 0x01;               //选择定时器的工作模式:定时器0,方式1TH0 = (65535 - 10000)/256; //定时器的初值TL0 = (65535 - 10000)%256;EA = 1;                    //开打总中断使能ET0 = 1;                   //打开定时器0 的使能TR0 = 1;                   //打开定时器0 ,开始工作
}void main(void)
{P0 = 0xff;LED = 0;           //点亮LEDinit_timer0();     //初始化定时器 定时10mswhile(1){if(time_10ms_ok)        //这里表示10ms扫描一次{time_10ms_ok = 0;   //清除10ms定时标志Int_Key_Scan();      //按键扫描程序}//第一种:KeyDown的使用//单按时和长按时,都只返回一次有效键值(无需等到按键释放,就可以返回有效键值)switch(g_uiKeyDown){case 0xFE://P0.0按键程序LED = !LED; break;case 0xFD://P0.1按键程序 //...break;case 0xFB://P0.2按键程序 //...break;case 0xF7:break;case 0xEF:break;case 0xDF:break;case 0xBF:break;case 0x7F:break;      case 0xFF://没有按键程序//...break;}//第二种:KeyRelease的使用//只有当按键释放之后,才返回一次有效键值,即是按键释放后,才执行相应的函数switch(g_uiKeyRelease){case 0xFE://P0.0按键程序LED = !LED; break;case 0xFD://P0.1按键程序 //...break;case 0xFB://P0.2按键程序 //...break;case 0xF7:break;case 0xEF:break;case 0xDF:break;case 0xBF:break;case 0xEF:break;      case 0xFF://没有按键程序//...break;  }//第三种:KeyContinue的使用//1)单次按键(非长按),返回一次有效值。//2)长按,返回多次相同有效值switch(g_uiKeyContinue){case 0xFE://P0.0按键程序LED = !LED; break;case 0xFD://P0.1按键程序 //...break;case 0xFB://P0.2按键程序 //...break;case 0xF7:break;case 0xEF:break;case 0xDF:break;case 0xBF:break;case 0xEF:break;      case 0xFF://没有按键程序//...break;  }}}void timer0(void) interrupt 1   //用的是定时器0, 这个“interrupt 1”中的“1”代表1号中断即是定时器0中断。如果是“0”就是外部中断0;“2“=外部中断1;”3“定时器1中断;”4“=串行口中断
{TH0 = (65535 - 10000)/256;TL0 = (65535 - 10000)%256; //定时器0的方式1,得在中断程序中重复初值。time_10ms_ok = 1;  //定时10MS 的标志
}

Pillar Peng
2015.5.25 - 18:23

log:
感谢网友“Shiow1984”的提醒,有漏掉和不足的地方,我已经修改。
之前写的文章意在按键程序的思路,就没有将定时扫描程序添加进去,怕影响按键程序的理解,若要稳定地运用于程序中,就要使用定时扫描了,这样按键扫描的时间就可以确定,调节好扫描次数后,几乎就没有什么误触了。

我也在上面的程序中添加了定时扫面程序。


扩展篇:
该方法的矩阵键盘程序

【按键】短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)相关推荐

  1. 【按键】短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动)--- 矩阵键盘

    请先阅读上篇: 短按,长按,按键释放,三种模式的按键扫描程序(软件消抖动) 上面的程序适用于单个按键,那是不是也可以适用于矩阵键盘呢? 答案是肯定的. 接下来在这里做一个简单的扩展,具体框架不用改变, ...

  2. 云计算三种服务器模式,云服务的三种模式

    原标题:云服务的三种模式 我们来讲一下云服务的三种模式. IaaS IaaS,即基础教育设施即服务.该服务是提供给消费者使用的所有计算基础架构包 括处理CPU.内存.存储.网络和其它企业基本的计算资源 ...

  3. 怎么给视频配音?短视频作者在用的三种方法,简单易操作

    怎么给视频配音?短视频作者在用的三种方法,简单易操作 如果你也是做短视频的作者,那么相信你也一定知道,做短视频并不仅仅只是简单的拍摄剪辑发布而已,其中要做的工作还有很多,比如配音就是其中的一项.虽然并 ...

  4. 短视频如何进行种草?三种方式别忘记,帮你获得用户喜爱

    短视频如何进行种草?三种方式别忘记,帮你获得用户喜爱 用短视频来进行种草,其实算得上是一个不错的创意,不过,如果想要真正的利用短视频种草,获得更多用户,那么不妨尝试一下以下三种方式,或许可以帮助你获得 ...

  5. android分屏模式_安卓 7.0 分屏功能详解:三种模式,想分就分

    IT之家讯 8月18日消息,安卓7.0的一大特色就是自带全新分屏功能,分为多画面显示模式.画中画模式和自由分屏模式三种,本文将详细介绍这三种模式. 多画面显示模式 启动该模式的方法有两种,一是在后台应 ...

  6. httpd的三种模式比较

    查看你的httpd使用了哪种模式: /usr/local/apache2/bin/httpd -V |grep 'Server MPM' 使用哪种模式,需要在编译的时候指定 --with-mpm=pr ...

  7. apache2三种模式及参数调优

    问题来源 如果访问量比较小,其实什么配置都没问题.在一些特殊场景下,例如促销活动,访问量会比较集中.峰值差不多能达到每秒1000到2000次的访问,而且还在继续增长中. 我们一直判断是数据库卡死的问题 ...

  8. 分布式事务讲解 - TX-LCN分布式事务框架(含LCN、TCC、TXC三种模式)

    分布式事务讲解 - TX-LCN分布式事务框架(含LCN.TCC.TXC三种模式) 分布式事务系列博客: TX-LCN框架原理 LCN 原理及主要特点 代码实现 实现场景 创建数据库及表(三个数据库, ...

  9. vi和vim常用的三种模式

    文章目录 1. vi 和 vim 的基本介绍 2. vi 和 vim 常用的三种模式 2.1 正常模式 2.2 插入模式 2.3 命令行模式 3. vi 和 vim 基本使用 4. 各种模式的相互切换 ...

最新文章

  1. 【异步编程】Part3:取消异步操作
  2. 计算机硬件大型作业报告,计算机硬件技术大作业报告.doc
  3. b站弹幕姬python_基于asyncio异步协程框架实现收集B站直播弹幕详细介绍
  4. URAL 2047 Maths 打表 递推
  5. 【Openstack】实录手动部署Openstack Rocky 双节点(3)- Glance
  6. Sql中Output参数用法和分页存储过程
  7. 关心国事-21世纪经济报道:周鸿祎人治雅虎中国
  8. 生活中有趣的6个数学小故事
  9. ADS1115使用之电路与程序
  10. SVC vs SVR
  11. matlab中使用ode45来解热水冷却问题一阶微分方程
  12. 为什么c语言输出到文件慢,【图片】今天写几个性能测试,为什么C语言跑得这么慢呢??【c语言吧】_百度贴吧...
  13. 计算机组成原理与体系结构 —— 南桥与北桥
  14. Redis集群之主从模式
  15. 纯前端JS导出Word包含图片
  16. 使用163邮箱发送邮件报错(554, b'DT:SPM 163 smtp3,G9xpCgCHi5RJOFVemMZ4Dw--.348S3 1582643274,please see http://ma
  17. Linux下网络传输测速程序小记
  18. 计算机网络管理设计,计算机网络管理系统设计与应用分析
  19. 数据治理解决方案数据治理标准化现状
  20. 【人人开源】集成通过Token认证统一登录功能

热门文章

  1. 公司网站营销方案(简版)
  2. Xubuntu22.04之替换blueman-manager连接蓝牙设备(一百七十五)
  3. 计算机主机信号不过去的解决办法,解决办法:如何解决将计算机连接到显示器时无信号的问题...
  4. 【知识点】不同LED颜色导通电压
  5. 平面设计师常用尺寸规格
  6. AtCoder Beginner Contest 191 A~D题解
  7. Docker 3.2.5:基于 Dockerfile 制作 Nginx 镜像
  8. 京东天猫数据查询与分析:2023年厨电细分市场数据分析
  9. 五行代码实现千万类别分类网络,飞桨大规模分类库揭秘
  10. jsoup 对 HTML 文档进行解析和操作