【按键】[独立按键] - 3:三击 以及 N击
三、 [三击]程序
1.三击介绍
- 三击判定:在完成第一次【单击】之后,在一定的时间间隔内(本程序使用的是300ms),接着完成第二次【单击】,时间间隔重新计时,然后又在这个时间间隔内,完成第三次【单击】,及判定为【三击】。
注:【单击】是包括按下和释放按键的过程,判定方式沿用上文所说的,如果忘了,可以参考上文。 - 三击响应时序图
注:
T1:是单击判定的时间,范围:30ms < T1 < 3000ms。
T2:是判定双击的时间间隔,是个定值 300ms。这个时间间隔的计算,是从第一次【单击】释放后开始计时,直到第二次【单击】释放按键后结束。在完成第一次【单击】后(释放按键后开始计时),在这个时间间隔内,如果再一次完成【单击】
T3:是判定三击的时间间隔,双击之后,在这个时间内,再一次有【单击】,击判定为【三击】。其实质与T2一样。
2.按键程序结果
按键程序结构可以分为三个部分:
- 第一部分:判断有无单击按键;
- 第二部分:判断双击,即是在预设的时间间隔内,有无第二次【单击】;
第三部分:判断三击,即是在【双击】后,在预设的时间间隔内,有无再一次的【单击】。
3.三击程序的源代码以及注释
程序的注释写的很详细,应该是可以读懂的,如果有疑问可以留言讨论。
以下是key.c 的源代码:
// =========================== key.c ======================
#include "reg51.h"#define KEY_INPUT P1.0 // 按键IO#define KEY_STATE_0 0 // 按键状态
#define KEY_STATE_1 1
#define KEY_STATE_2 2
#define KEY_STATE_3 3#define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定单击的时间长度,软件消抖
#define KEY_INTERVAL 30 // KEY_INTERVAL*10MS = 300MS 判定双击的时间间隔
#define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定长按的时间长度#define N_KEY 0 // no click
#define S_KEY 1 // single click
#define D_KEY 2 // double click
#define T_KEY 3 // Triple click
#define L_KEY 10 // long press// ----------------------------------- key_driver --------------------------
unsigned char key_driver(void)
{ static unsigned char key_state = 0;static unsigned int key_time = 0;unsigned char key_press, key_return; key_return = N_KEY; // 清除 返回按键值key_press = key_input; // 读取当前键值switch (key_state) { case KEY_STATE_0: // 按键状态0:判断有无按键按下if (!key_press) // 有按键按下{key_time = 0; // 清零时间间隔计数key_state = KEY_STATE_1; // 然后进入 按键状态1} break;case KEY_STATE_1: // 按键状态1:软件消抖(确定按键是否有效,而不是误触)。按键有效的定义:按键持续按下超过设定的消抖时间。if (!key_press) {key_time++; // 一次10msif(key_time>=SINGLE_KEY_TIME) // 消抖时间为:SINGLE_KEY_TIME*10ms = 30ms;{key_state = KEY_STATE_2; // 如果按键时间超过 消抖时间,即判定为按下的按键有效。按键有效包括两种:单击或者长按,进入 按键状态2, 继续判定到底是那种有效按键}} else key_state = KEY_STATE_0; // 如果按键时间没有超过,判定为误触,按键无效,返回 按键状态0,继续等待按键break; case KEY_STATE_2: // 按键状态2:判定按键有效的种类:是单击,还是长按if(key_press) // 如果按键在 设定的长按时间 内释放,则判定为单击{ key_return = S_KEY; // 返回 有效按键值:单击key_state = KEY_STATE_0; // 返回 按键状态0,继续等待按键} else{key_time++; if(key_time >= LONG_KEY_TIME) // 如果按键时间超过 设定的长按时间(LONG_KEY_TIME*10ms=300*10ms=3000ms), 则判定为 长按{key_return = L_KEY; // 返回 有效键值值:长按key_state = KEY_STATE_3; // 去状态3,等待按键释放}}break;case KEY_STATE_3: // 等待按键释放if (key_press) {key_state = KEY_STATE_0; // 按键释放后,进入 按键状态0 ,进行下一次按键的判定} break; default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候key_state = KEY_STATE_0;break;}return key_return; // 返回 按键值
} // ----------------------------------- key_read --------------------------------
unsigned char key_read(void)
{ static unsigned char key_state1=0, key_time1=0;unsigned char key_return,key_temp;key_return = N_KEY; // 清零 返回按键值key_temp = key_driver(); // 读取键值switch(key_state1) { case KEY_STATE_0: // 按键状态0:等待有效按键(通过 key_driver 返回的有效按键值)if (key_temp == S_KEY) // 如果是[单击],不马上返回单击按键值,先进入 按键状态1,判断是否有[双击]的可能{ key_time1 = 0; // 清零计时key_state1 = KEY_STATE_1; } else // 如果不是[单击],直接返回按键值。这里的按键值可能是:[长按],[无效按键]{key_return = key_temp; // 返回 按键值}break;case KEY_STATE_1: // 按键状态1:判定是否有[双击]if (key_temp == S_KEY) // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[双击],但是不马上返回 有效按键值为[双击],先进入 按键状态2,判断是否有[三击]{key_time1 = 0; // 清零 时间间隔key_state1 = KEY_STATE_2; // 改变 按键状态值} else // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]{key_time1++; // 计数 时间间隔if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔{ key_return = S_KEY; // 返回 有效按键:[单击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} } break; case KEY_STATE_2: // 按键状态2:判定是否有[三击]if (key_temp == S_KEY) // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]{key_return = T_KEY; // 返回 有效按键:[三击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} else // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]{key_time1++; // 计数 时间间隔if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔{ key_return = D_KEY; // 返回 有效按键:[双击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} } break; default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候key_state1 = KEY_STATE_0;break;}return key_return; // 返回 按键值
}
使用注意:
1)硬件:按键的一端接地(GND),另一端接IO口。IO为输入,一定要有上拉电阻。
2)定时器:这里为了精确的定时,所以使用了定时器,定时器的时间是10ms。
3)扫描周期:调用此函数时,一定确保”扫描周期“要小于10ms。不然按键内所涉及的时间就会不准,会偏大。所涉及的时间包括消抖时长,按键长按时长等。
扫描周期定义:从 第一次进入按键扫描程序 开始,到第二次进入按键扫描程序时 结束,之间所用的时间。
测量扫描周期的方法:可以在按键扫描程序的第一句,添加IO口取反函数,然后用示波器查看改IO口,其IO口周期的一般就是扫描周期了。
4)特别注意以上程序的3个关于时间的宏,相当的重要。如果想更改按键的单击的灵敏度,双击(三击)的速度,或者长按的时间,只需要修改这些宏的值即可。
比如:针对于老人的使用的按键,就需要将双击的速度调节的慢一点,就可以将KEY_INTERVAL的值增大。
- SINGLE_KEY_TIME:单击的灵敏度,值越小,越灵敏。
- KEY_INTERVAL :双击的点击速度,修改这个值,值越小,速度越快。同时这个值也决定了单击的响应时间,因为单击之后,还需要判断在这个时间间隔内没有第二次单击,如果没有才是真正的单击,所以单击响应的时间为:SINGLE_KEY_TIME+KEY_INTERVAL;
- LONG_KEY_TIME :长按的时间,修改这个即可,值越大,时间越长。
4.按键程序的使用实例
这里以C51位硬件平台进行实例讲解
1)实例程序的功能:
- 单击:取反LED1
- 双击:取反LED2
- 三击:取反LED3
- 长按:点亮LED1,LED2,LED3
2)硬件:
- 按键IO:P1.0
- LED1 :P2.0
- LED2 :P2.1
- LED3 :P2.2
以下是 main.c 源代码:
// =========================== key.c ======================#include "reg51.h"
#include "key.c"#define LED_ALL_ON() P2 |= 0x07
#define LED_ALL_OFF() P2 &= ~0x07sbit LED1 = P2.0; // 定义LEDIO口
sbit LED2 = P2.1; // 定义LEDIO口
sbit LED3 = P2.2; // 定义LEDIO口unsigned char g_u8_KeyValue; // 按键值
unsigned char g_flag_10ms_key; // 10ms 计时标志void key_read(); // 声明读取按键函数void T0_Init_10ms(void) // timer0,初始化函数 ,定时时间为 10ms
{ TMOD |= 0x01;TH0 = (65535 - 10000)/256;TL0 = (65535 - 10000)%256;ET0 = 1;TR0 = 1; EA = 1;
}// 主函数
void main(void)
{P1.0 = 1; // P1.0 拉高T0_Init_10ms(); // 定时器0,初始化,定时10mswhile(1){if(g_flag_10ms_key) // 等待10ms,定时完成{g_flag_10ms_key = 0; // 清零10ms定时标志g_u8_KeyValue = key_read(); // 读取按键值switch(g_u8_KeyValue){case S_KEY: LED1 = !LED1; break; // 单击 取反LED1case D_KEY: LED2 = !LED2; break; // 双击 取反LED2case T_KEY: LED3 = !LED3; break; // 三击 取反LED3case L_KEY: LED_ALL_ON(); break; // 长按 点亮所有的LED}}}
}// timer0 中断服务程序
void IRQ_T0(void) interrupt 1
{g_flag_10ms_key = 1; // 置位 10ms 定时标志
}
Pillar Peng
2016.3.29 - 17:30
有之前单击,双击,三击的介绍,四击以及N击只是一小段程序的区别。有兴趣的可以扩展一下4击以及N击,试着自己动手编写程序,其实很简单的,只需要更改key_read 函数即可,大家可以对照双击,三击和四击的程序,就可以看得出来,改动的地方很少。
将扩展的四击程序附在这里:
// =========================== key.c ======================
#include "reg51.h"#define KEY_INPUT P1.0 // 按键IO#define KEY_STATE_0 0 // 按键状态
#define KEY_STATE_1 1
#define KEY_STATE_2 2
#define KEY_STATE_3 3#define SINGLE_KEY_TIME 3 // SINGLE_KEY_TIME*10MS = 30MS 判定单击的时间长度,软件消抖
#define KEY_INTERVAL 30 // KEY_INTERVAL*10MS = 300MS 判定双击的时间间隔
#define LONG_KEY_TIME 300 // LONG_KEY_TIME*10MS = 3S 判定长按的时间长度#define N_KEY 0 // no click
#define S_KEY 1 // single click 单击
#define D_KEY 2 // double click 双击
#define T_KEY 3 // Triple click 三击
#define Q_KEY 4 // Quadruple click 四击
#define L_KEY 10 // long press 长按// ----------------------------------- key_driver --------------------------
unsigned char key_driver(void)
{ static unsigned char key_state = 0;static unsigned int key_time = 0;unsigned char key_press, key_return; key_return = N_KEY; // 清除 返回按键值key_press = key_input; // 读取当前键值switch (key_state) { case KEY_STATE_0: // 按键状态0:判断有无按键按下if (!key_press) // 有按键按下{key_time = 0; // 清零时间间隔计数key_state = KEY_STATE_1; // 然后进入 按键状态1} break;case KEY_STATE_1: // 按键状态1:软件消抖(确定按键是否有效,而不是误触)。按键有效的定义:按键持续按下超过设定的消抖时间。if (!key_press) {key_time++; // 一次10msif(key_time>=SINGLE_KEY_TIME) // 消抖时间为:SINGLE_KEY_TIME*10ms = 30ms;{key_state = KEY_STATE_2; // 如果按键时间超过 消抖时间,即判定为按下的按键有效。按键有效包括两种:单击或者长按,进入 按键状态2, 继续判定到底是那种有效按键}} else key_state = KEY_STATE_0; // 如果按键时间没有超过,判定为误触,按键无效,返回 按键状态0,继续等待按键break; case KEY_STATE_2: // 按键状态2:判定按键有效的种类:是单击,还是长按if(key_press) // 如果按键在 设定的长按时间 内释放,则判定为单击{ key_return = S_key; // 返回 有效按键值:单击key_state = KEY_STATE_0; // 返回 按键状态0,继续等待按键} else{key_time++; if(key_time >= LONG_KEY_TIME) // 如果按键时间超过 设定的长按时间(LONG_KEY_TIME*10ms=300*10ms=3000ms), 则判定为 长按{key_return = L_KEY; // 返回 有效键值值:长按key_state = KEY_STATE_3; // 去状态3,等待按键释放}}break;case KEY_STATE_3: // 等待按键释放if (key_press) {key_state = KEY_STATE_0; // 按键释放后,进入 按键状态0 ,进行下一次按键的判定} break; default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候key_state = KEY_STATE_0;break;}return key_return; // 返回 按键值
} // ----------------------------------- key_read --------------------------------
void key_read(void)
{ static unsigned char key_state1=0, key_time1=0;unsigned char key_return,key_temp;key_return = N_KEY; // 清零 返回按键值key_temp = key_driver(); // 读取键值switch(key_state1) { case KEY_STATE_0: // 按键状态0:等待有效按键(通过 key_driver 返回的有效按键值)if (key_temp == S_key ) // 如果是[单击],不马上返回单击按键值,先进入 按键状态1,判断是否有[双击]的可能{ key_time1 = 0; // 清零计时key_state1 = KEY_STATE_1; } else // 如果不是[单击],直接返回按键值。这里的按键值可能是:[长按],[无效按键]{key_return = key_temp; // 返回 按键值}break;case KEY_STATE_1: // 按键状态1:判定是否有[双击]if (key_temp == S_key) // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[双击],但是不马上返回 有效按键值为[双击],先进入 按键状态2,判断是否有[三击]{key_time1 = 0; // 清零 时间间隔key_state1 = KEY_STATE_2; // 改变 按键状态值} else // 有[单击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击]出现,则判定为 [单击]{key_time1++; // 计数 时间间隔if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔{ key_return = S_key; // 返回 有效按键:[单击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} } break; case KEY_STATE_2: // 按键状态2:判定是否有[三击]if (key_temp == S_key) // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[三击],由于这里只扩展到[三击],所以马上返回 有效按键值为[三击]{key_time1 = 0; // 清零 时间间隔key_state1 = KEY_STATE_3; // 改变 按键状态值 } else // 有[双击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [双击]{key_time1++; // 计数 时间间隔if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔{ key_return = D_KEY; // 返回 有效按键:[双击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键}}break; case KEY_STATE_3: // 按键状态3:等待按键释放if (key_temp == S_key) // 有[三击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms) 内,再次有[单击],则为[四击],马上返回有效按键值为[四击],{ key_return = Q_key; // 返回 有效按键:[四击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} else { // 有[三击]后,如果在 设定的时间间隔(KEY_INTERVAL*10ms=30*10ms=300ms)内,没有[单击],则判定为 [三击] key_time1++; // 计数 时间间隔if(key_time1 >= KEY_INTERVAL) // 超过 时间间隔{ key_return = T_KEY; // 返回 有效按键:[三击]key_state1 = KEY_STATE_0; // 返回 按键状态0,等待新的有效按键} } break; default: // 特殊情况:key_state是其他值得情况,清零key_state。这种情况一般出现在 没有初始化key_state,第一次执行这个函数的时候key_state1 = KEY_STATE_0;break;}return = key_return; // 返回 按键值
}
Pillar Peng
2016.3.30 - 14:04
友情链接:
第一部分:【按键】[独立按键] - 1:单击
第二部分:【按键】[独立按键] - 2:双击
【按键】[独立按键] - 3:三击 以及 N击相关推荐
- AD按键-矩阵按键-独立按键:
原理:利用数组分压+AD采集: 优点:一个IO口可以做成多个按键,节省IO口(矩阵键盘在>4时优点才能体现出来):可备用作为AD基准输入. 缺点:不能做成组合按键(或者电阻要精确选择):且离IO ...
- 单片机:独立按键的应用实验
一. 实验目的: (一) 熟悉单片机 I/O 口的输入控制: (二) 熟悉开发板上单片机 I/O 口与数码管. 独立按键的电路连接: (三) 掌握 keil C 软件的使用: (四) 掌握独立按键的应 ...
- 51单片机之按键(独立按键矩阵按键)
难的东西学不会是因为简单的知识没学好 基础不牢,地动山摇 按键 1.1基础温习 (1)按键的物理结构 (2)单片机引脚有两种状态(I/O)输入[读]或者输出[写] (3)上拉电阻的作用.通过这个电阻把 ...
- 蓝桥杯单片机学习4——独立按键矩阵按键
上期学习了数码管的静态显示,这次我们来学习独立按键&矩阵按键 独立按键 原理很简单,当作为独立按键使用时,跳线帽的23接在一起,此时按键如果按下,则按键连接的IO口电平会被拉低,通过捕获IO的 ...
- 【按键】[独立按键] - 1: 单击,双击,三击以及N击
此按键程序的实现的功能是单个独立按键的[单击],[长按],[双击],[三击]以及[多击].本文分为三个部分, 第一个部分是说[单击],[长按]的程序: 第二部分是讲[双击]: 第三部分是讲[三击],[ ...
- (三)51单片机基础——独立按键与数码管
从小就对电器元件比较感兴趣吧,经常拿坏的电器里面的芯片拆下来玩,甚至那些没坏的电器,比如我家的电视,也会希望它能坏掉,我好去看看里面是什么样子的,为什么能播放节目--,所以我第一眼看到51单片机的时候 ...
- AutoLeaders控制组—51单片机学习笔记(LED控制、独立按键、数码管)
本篇内容是观看B站江科大自化协UP主的教学视频所做的笔记,对其中内容有所引用,并结合自己的单片机板块进行了更改调整. 以下笔记内容以一个视频为一个片段(内容较多,可能不适合速食,望见谅) 根据测试,目 ...
- 51单片机入门教程_独立按键控制系列
一.老规矩 先上代码 #include <REGX52.H>void main() {while(1){if(P3_1==0 || P3_0==0) //如果K1按键或K2按键按下{P2_ ...
- verilog基础-状态机之FPGA独立按键消抖设计与验证(熟练testbench的写法)
独立按键消抖设计与验证 本实验主要是为了锻炼状态机的思维模式以及熟练掌握TB的写法 本节主要收获了:define的用法,另外就是,顶层的input在TB中是reg的真正含义,其实就是把激励当做寄存器来 ...
最新文章
- 图模型+Bert香不香?完全基于注意力机制的图表征学习模型Graph-Bert
- 今日 Paper | 高效骨干搜索;学习扩充;最小人脸检测器;​DEPARA等
- VTK修炼之道70:体绘制讨论_光照阴影、VTKLODProp3D
- 贾扬清谈云原生-让数据湖加速迈入3.0时代
- 安卓案例:利用SQLiteDatabase操作数据库与表
- poj3254(状压dp入门第一道题,很详细)
- 【后缀数组】洛谷P3809模板题
- 图形数据库 Neo4j(2) ----Java
- 不同VPC路由器通过静态路由、动态路由(OSPF)实现网络互通实战
- 网络安全等级保护定级备案
- 中专计算机专业包括哪些方面,中专计算机有什么专业?
- SU2 CFD代码阅读
- webfreer下载及设置
- 医院洁净手术室设计装修要点SICOLAB
- 有财学院http://www.godgold.com/learn/title_asp/index.html
- android 电商app组件化,APICloud AVM多端开发案例深度解析(一)--生鲜电商app开发
- SQL智能提示插件--SQLPrompt_7.2.0.241(破解 )
- 《等着我吧,我会回来》 苏·西蒙诺夫
- 06.破解Windows7密码
- 探讨【查找】的经典题目(很重视细节)