按键

简单的说,进入了电子,不管是学纯模拟,还是学单片机,DSP、ARM等处理器,或者是我们的FPGA,一般没有不用到按键的地方。按键:人机交互控制,主要用于对系统的控制,信号的释放等。因此在这里,FPGA上应用的按键消抖动,也不得不讲!

一、为什么要消抖动

在按键被按下的短暂一瞬间,由于硬件上的抖动,往往会产生几毫秒的抖动,在这时候若采集信号,势必导致误操作,甚至系统崩溃;同样,在释放按键的那一刻,硬件上会相应的产生抖动,会产生同样的后果。因此,在模拟或者数字电路中,我们要避免在最不稳定的时候采集信号,进行操作。

对此一般产用消抖动的原理。一般可分为以下几种:

(1)延时
(2)N次低电平计数
(3)低通滤波
在数字电路中,一般产用(1)(2)种方法。后文中将详细介绍。

二、各种消抖动

1. 模拟电路按键消抖动

对于模拟电路中,一般消抖动用的是电容消抖动或者施密特触发等电路,再次不做具体介绍。

“”

2. 单片机中按键消抖动

对于单片机中的按键消抖动,本节Bingo根据自己当年写过的单片机其中的一个代码来讲解,代码如下所示:

unsigned char key_sCAN(void)
{
if(key == 0) //检测到被按下
{
delay(5); //延时5ms,消抖
if(key != 0)
retrurn 0; //是抖动,返回退出
while(!key1); // 确认被按下,等下释放
delay(5); //延时5ms,消抖
while(!key1); //确认被释放
return 1; //返回按下信号
}
return 0; //没信号
}

针对以上代码,消抖动的顺序如下所示:

(1)检测到信号
(2)延时5ms,消抖动
(3)继续检测信号,确认是否被按下
a) 是,则开始等待释放
b) 否,则返回0,退出
(4)延时5ms,消抖动
(5)确认,返回按下信号,退出

当然在单片机中也可以循环计数来确认是否被按下。Bingo认为如此,太耗MCU资源,因此再次不做讲述。

3. FPGA中的按键消抖动

对于FPGA中的消抖动,很多教科书上都没有讲述。但Bingo觉得这个很有必要。对于信号稳定性以及准确性分析,按键信号必须有一个稳定的脉冲,不然对系统稳定性有很大的干扰。

此处Bingo用两种方法对FPGA中按键消抖动分析。其中第一种是通过状态机的使用直接移植以上MCU的代码,这个思想在FPGA状态机中很重要。第二种,通过循环n次计数的方法来确认是否真的被按下,这种方法很实用在FPGA这种高速并行器件中。

(1)利用状态机移植MCU按键消抖动
此模块由Bingo无数次修改测试最后成型的代码,在功能上可适配n个按键,在思想上利用单片机采用了单片机消抖动的思想。具体代码实现过程请有需要的自行分析,本模块移植方便,Verilog代码如下所示:

/*************************************************
* Module Name : key_scan_jitter.v
* Engineer : Crazy Bingo
* Target DevICe : EP2C8Q208C8
* Tool versions : QUARTus II 11.0
* Create Date : 2011-6-26
* Revision : v1.0
* DescripTIon :
**************************************************/
module key_scan_jitter
#(
parameter KEY_WIDTH = 2
)
(
input clk,
input rst_n,
input [KEY_WIDTH-1:0] key_data,
output key_flag,
output reg [KEY_WIDTH-1:0] key_value
);
reg [19:0] cnt; //delay_5ms(249999)
reg [2:0] state;
//-----------------------------------
always @(posedge clk or negedge rst_n)

本文转自网络,版权归原作者,如果您觉得不好,请联系我们删除!

围观 109

如何实现单片机用一个I/O采集多个按键信号

使用模数转换(ADC)的特点就可以实现单片机用一个I/O采集多个按键信号。

一、单片机的I/O口检测按键简说

我们知道,一般情况下单片机的一个I/O口作为普通I/O口的话,只能检测识别一个按键。

日常设计中,如果碰到按键数量较多的话,会采用行列式键盘,例如最常见的4X4矩阵键盘,这样可以实现用8个I/O口检测16个按键。

如何实现单片机用一个I/O采集多个按键信号?

还有就是键盘接口,典型的是我们计算机上用的键盘,其采用PS/2接口,现在一般计算机上用的是USB接口的键盘。

另外还有使用串口或者IIC、SPI接口的键盘芯片,这些使用常见的串口、IIC、SPI通信协议实现。

但是这些都一个以上的I/O口,不是真正的用多个按键。

如何实现单片机用一个I/O采集多个按键信号?

那么有没有更简单的办法,使用更少的I/O口资源检测更多的按键呢?

二、基于模数转换的AD键盘

我们知道按键检测实际上是检测连接按键的端口的高低电平值,在单个I/O口检测单个按键时,只是简单的判断连接按键的端口的电平是高电平(+5V)还是低电平(0V)。那么是否可以通过电平的微小变化来检测按键是否被按下呢?

下图为一个A/D键盘的原理图,从图中可以看出,当不同的按键被按下时,ADC端点处的电压不同,通过判断不同的电压值就可以判断出是那个按键被按下。

如何实现单片机用一个I/O采集多个按键信号?

对于具有AD转换功能的单片机来说,直接接到一个AD通道即可。对于没有AD转换功能的单片机,可采用一个AD转换模块。

对于这种按键,有以下缺点:

1、对于同一点处的电压值,A/D多次采样的结果不可能完全相同。

2)、电阻的误差。电阻值由于电阻的精度和环境温度的原因,误差较大,所以A/D键盘各个按键点的分压不准确。

3)、为尽量减少误差,可以采取增加电阻精度、增加温度补偿等方法,另外在软件处理时候要注意消除按键抖动等因素,还要对实际转换值和标准值给出误差补偿。

4)、如果按键按下,经过A/D转换,若实际转换值在允许误差范围之内(需要实际测量各点电压,并计算各点电压平均值),则认为按键按下,否则程序不响应。

5)、实际试验过程中,还要考虑电阻的累积误差,选用精度越高的电阻,可分辨的按键数目越多。

转自:畅学单片机

围观 526

1.按键分类与输入原理

按键按照结构原理科分为两类,一类是触点式开关按键,如机械式开关、导电橡胶式开关灯;另一类是无触点式开关按键,如电气式按键,磁感应按键等。前者造价低,后者寿命长。目前,微机系统中最常见的是触点式开关按键。

在单片机应用系统中,除了复位按键有专门的复位电路及专一的复位功能外,其他按键都是以开关状态来设置控制功能或输入数据的。当所设置的功能键或数字键按下时,计算机应用系统应完成该按键所设定的功能,键信息输入时与软件结构密切相关的过程。

对于一组键或一个键盘,总有一个接口电路与CPU相连。CPU可以采用查询或中断方式了解有无将按键输入,并检查是哪一个按键按下,将该键号送人累加器,然后通过跳转指令转入执行该键的功能程序,执行完成后再返回主程序。

2.按键结构与特点

微机键盘通常使用机械触点式按键开关,其主要功能式把机械上的通断转换为电气上的逻辑关系。也就是说,它能提供标准的TTL逻辑电平,以便于通用数字系统的逻辑电平相容。机械式按键再按下或释放时,由于机械弹性作用的影响,通常伴随有一定的时间触点机械抖动,然后其触点才稳定下来。其抖动过程如下图1所示,抖动时间的长短与开关的机械特性有关,一般为5-10ms。在触点抖动期间检测按键的通与断,可能导致判断出错,即按键一次按下或释放错误的被认为是多次操作,这种情况是不允许出现的。为了克服你、按键触点机械抖动所致的检测误判,必须采取消抖措施。按键较少时,可采用硬件消抖;按键较多式,采用软件消抖。

单片机独立按键和矩阵键盘概念及原理
图1 按键触点机械抖动

(1)按键编码

一组按键或键盘都要通过I/O口线查询按键的开关状态。根据键盘结构的不同,采用不同的编码。无论有无编码,以及采用什么编码,最后都要转换成为与累加器中数值相对应的键值,以实现按键功能程序的跳转。

(2)键盘程序

一个完整的键盘控制程序应具备以下功能:

a.检测有无按键按下,并采取硬件或软件措施消抖。

b.有可靠的逻辑处理办法。每次只处理一个按键,期间对任何按键的操作对系统不产生影响,且无论一次按键时间有多长,系统仅执行一次按键功能程序。

c.准确输出按键值(或键号),以满足跳转指令要求。

3.独立按键与矩阵键盘

(1)独立按键

单片机控制系统中,如果只需要几个功能键,此时,可采用独立式按键结构。

独立按键式直接用I/O口线构成的单个按键电路,其特点式每个按键单独占用一根I/O口线,每个按键的工作不会影响其他I/O口线的状态。独立按键的典型应用如图所示。独立式按键电路配置灵活,软件结构简单,但每个按键必须占用一个I/O口线,因此,在按键较多时,I/O口线浪费较大,不宜采用。独立按键如图2所示。

单片机独立按键和矩阵键盘概念及原理
图2 独立键盘

独立按键的软件常采用查询式结构。先逐位查询没跟I/O口线的输入状态,如某一根I/O口线输入为低电平,则可确认该I/O口线所对应的按键已按下,然后,再转向该键的功能处理程序。

(2)矩阵键盘

单片机系统中,若使用按键较多时如电子密码锁、电话机键盘等一般都至少有12到16个按键,通常采用矩阵键盘。

矩阵键盘又称行列键盘,它是用四条I/O线作为行线,四条I/O线作为列线组成的键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4*4个。这种行列式键盘结构能有效地提高单片机系统中I/O口的利用率。

矩阵键盘的工作原理

最常见的键盘布局如图3所示。一般由16个按键组成,在单片机中正好可以用一个P口实现16个按键功能,这也是在单片机系统中最常用的形式,4*4矩阵键盘的内部电路如图4所示。

单片机独立按键和矩阵键盘概念及原理
图3 矩阵键盘布局图

单片机独立按键和矩阵键盘概念及原理
图4 矩阵键盘内部电路图

当无按键闭合时,P3.0~P3.3与P3.4~P3.7之间开路。当有键闭合时,与闭合键相连的两条I/O口线之间短路。判断有无按键按下的方法是:第一步,置列线P3.4~P3.7为输入状态,从行线P3.0~P3.3输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。第二步,行线轮流输出低电平,从列线P3.4~P3.7读入数据,若有某一列为低电平,则对应行线上有键按下。综合一二两步的结果,可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。

识别按键的方法很多其中,最常见的方法是扫描法

按键按下时,与此键相连的行线与列线导通,行线在无按键按下时处在高电平。如果所有的列线都处在高电平,则按键按下与否不会引起行线电平的变化,因此必须使所有列线处在电平。这样,当有按键按下时,改键所在的行电平才回由高变低。才能判断相应的行有键按下。

独立按键数量少,可根据实际需要灵活编码。矩阵键盘,按键的位置由行号和列号唯一确定,因此可以分别对行号和列号进行二进制编码,然后两值合成一个字节,高4位是行号,低4位是列号。

4.键盘的工作方式

对键盘的响应取决于键盘的工作方式,键盘的工作方式应根据实际应用系统中的CPU的工作状况而定,其选取的原则是既要保证CPU能及时响应按键操作,又不要过多占用CPU的工作时间。通常键盘的工作方式有三种,编程扫描、定时扫描和中断扫描。

(1)编程扫描方式

编程扫描方式是利用CPU完成其它工作的空余时间,调用键盘扫描子程序来响应键盘输入的要求。在执行键功能程序时,CPU不再响应键输入要求,直到CPU重新扫描键盘为止。

(2)定时扫描方式

定时扫描方式就是每隔一段时间对键盘扫描一次,它利用单片机内部的定时器产生一定时间(例如10ms)的定时,当定时时间到就产生定时器溢出中断。CPU响应中断后对键盘进行扫描,并在有按键按下时识别出该键,再执行该键的功能程序。

(3)中断扫描方式

上述两种键盘扫描方式,无论是否按键,CPU都要定时扫描键盘,而单片机应用系统工作时,并非经常需要键盘输入,因此,CPU经常处于空扫描状态。

为提高CPU工作效率,可采用中断扫描工作方式。其工作过程如下:当无按键按下时,CPU处理自己的工作,当有按键按下时,产生中断请求,CPU转去执行键盘扫描子程序,并识别键号。

本文转载自: 单片机精讲吴鉴鹰

围观 364

在单片机系统里,按键是常见的输入设备,在本文江介绍几种按键硬件、软件设计方面的技巧。一般的在按键的设计上,一般有四种方案:
一是GPIO口直接检测单个按键,如图1.1所示;
二是按键较多则使用矩阵键盘,如图1.2所示;
三是将按键接到外部中断引脚上,利用按键按下产生的边沿信号进行按键检测,如图1.3所示;
四是利用单片机的ADC,在不同的按键按下后,能够使得ADC接口上的电压不同,根据电压的不同,则可以识别按键,如图1.4所示。

单片机按键设计的四个方案详解
图1.1方案一

单片机按键设计的四个方案详解
图1.2方案二

单片机按键设计的四个方案详解
图1.3方案三

单片机按键设计的四个方案详解
图1.4方案四

在以上四种设计上,各有优点和不足。第一种是最简单和最基础的,对于单片机初学者很容易理解和使用,但是缺点是,需要在主循环中不断检测按键是否按下,并且需要做消抖处理。若主循环中某个函数任务占用时间较长,则按键会有不同程度的“失灵”。第二种,优点是能够在有限的GPIO情况下,扩展尽可能多的按键。但缺点同上,需要不停检测按键是否按下。第三种方式是效率最高,不需要循环检测按键是否按下,但是缺点是,需要单片机有足够的外部中断接口以供使用;第四种的优点是,只需要单片机的一个ADC接口,一根线,就能对多个按键进行识别,缺点是按键一旦内部接触不良,则可能按键串位,且按键产生的抖动,会造成一定的识别错误。

在以上的三种常见按键设计的基础上,现在分享我学习和工作中总结的按键方案。

改进一:在原方案一的基础上,加上与门电路,使得任何一个按键按下,都能产生中断,然后在中断里面识别是哪个按键被按下。因此不需要循环扫描,大大提高了效率。方案如图1.5所示。只需要每个按键对应地增加一个二极管,利用二极管的线与特性,可以实现按下任何按键,都能产生中断信号,但是按键之间互不影响。二极管选用普通整流二极管即可,本人亲测可行。

单片机按键设计的四个方案详解
图1.5 改进一

改进二:在原有的ADC按键的基础上,也可用增加二极管的方式,实现按键中断,并在中断服务程序里进行AD转换,从而识别按键。电路如图1.6所示。

单片机按键设计的四个方案详解
图1.6 改进二

改进三:因为按键不可避免的有抖动,因此按键消抖可以通过硬件消痘和软件消抖。现在分享一个十分简单且有效的硬件消痘方法:给按键并联一个104左右的电容。软件上基本不用处理即可避免抖动。
改进四:在按键扫描检测的方案下,如果主循环中有某个函数占用时间较长,则按键会发生或长或短的“失灵”,现分享我的一个解决方案。将按键扫描放到定时器中断里面,这样就可周期性地检测按键按下情况,不受主循环的影响。并且,能解析出按键的不同状态,即按下、按住、弹起、为按下这四种状态,用以实现更丰富的功能。但需注意两点,一是定时器的定时时间,不可过长也不可过短,过长容易检测不到按下,过短会占用大量时间资源。二是中断服务程序需简单明了,只做检测用,通过全局变量传递,在主循环内完成按键响应,中断服务函数内尽量不要占用太多时间。

来源: 21ic.com

围观 517

一、基本知识

1.按键分类与输入原理

按键按照结构原理科分为两类,一类是触点式开关按键,如机械式开关、导电橡胶式开关灯;另一类是无触点式开关按键,如电气式按键,磁感应按键等。前者造价低,后者寿命长。目前,微机系统中最常见的是触点式开关按键。

在单片机应用系统中,除了复位按键有专门的复位电路及专一的复位功能外,其他按键都是以开关状态来设置控制功能或输入数据的。当所设置的功能键或数字键按下时,计算机应用系统应完成该按键所设定的功能,键信息输入时与软件结构密切相关的过程。

对于一组键或一个键盘,总有一个接口电路与CPU相连。CPU可以采用查询或中断方式了解有无将按键输入,并检查是哪一个按键按下,将该键号送人累加器,然后通过跳转指令转入执行该键的功能程序,执行完成后再返回主程序。

2.按键结构与特点

微机键盘通常使用机械触点式按键开关,其主要功能式把机械上的通断转换为电气上的逻辑关系。也就是说,它能提供标准的TTL逻辑电平,以便于通用数字系统的逻辑电平相容。机械式按键再按下或释放时,由于机械弹性作用的影响,通常伴随有一定的时间触点机械抖动,然后其触点才稳定下来。其抖动过程如下图1所示,抖动时间的长短与开关的机械特性有关,一般为5-10ms。在触点抖动期间检测按键的通与断,可能导致判断出错,即按键一次按下或释放错误的被认为是多次操作,这种情况是不允许出现的。为了克服你、按键触点机械抖动所致的检测误判,必须采取消抖措施。按键较少时,可采用硬件消抖;按键较多式,采用软件消抖。

51单片机之独立按键和矩阵键盘
图1 按键触点机械抖动

(1)按键编码

一组按键或键盘都要通过I/O口线查询按键的开关状态。根据键盘结构的不同,采用不同的编码。无论有无编码,以及采用什么编码,最后都要转换成为与累加器中数值相对应的键值,以实现按键功能程序的跳转。

(2)键盘程序

一个完整的键盘控制程序应具备以下功能:
a.检测有无按键按下,并采取硬件或软件措施消抖。
b.有可靠的逻辑处理办法。每次只处理一个按键,期间对任何按键的操作对系统不产生影响,且无论一次按键时间有多长,系统仅执行一次按键功能程序。
c.准确输出按键值(或键号),以满足跳转指令要求。

3.独立按键与矩阵键盘

(1)独立按键

单片机控制系统中,如果只需要几个功能键,此时,可采用独立式按键结构。

独立按键式直接用I/O口线构成的单个按键电路,其特点式每个按键单独占用一根I/O口线,每个按键的工作不会影响其他I/O口线的状态。独立按键的典型应用如图所示。独立式按键电路配置灵活,软件结构简单,但每个按键必须占用一个I/O口线,因此,在按键较多时,I/O口线浪费较大,不宜采用。独立按键如图2所示。

51单片机之独立按键和矩阵键盘
图2 独立键盘

独立按键的软件常采用查询式结构。先逐位查询没跟I/O口线的输入状态,如某一根I/O口线输入为低电平,则可确认该I/O口线所对应的按键已按下,然后,再转向该键的功能处理程序。

(2)矩阵键盘

单片机系统中,若使用按键较多时如电子密码锁、电话机键盘等一般都至少有12到16个按键,通常采用矩阵键盘。

矩阵键盘又称行列键盘,它是用四条I/O线作为行线,四条I/O线作为列线组成的键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4*4个。这种行列式键盘结构能有效地提高单片机系统中I/O口的利用率。

矩阵键盘的工作原理

最常见的键盘布局如图3所示。一般由16个按键组成,在单片机中正好可以用一个P口实现16个按键功能,这也是在单片机系统中最常用的形式,4*4矩阵键盘的内部电路如图4所示。

51单片机之独立按键和矩阵键盘
图3 矩阵键盘布局图

51单片机之独立按键和矩阵键盘
图4 矩阵键盘内部电路图

当无按键闭合时,P3.0~P3.3与P3.4~P3.7之间开路。当有键闭合时,与闭合键相连的两条I/O口线之间短路。判断有无按键按下的方法是:第一步,置列线P3.4~P3.7为输入状态,从行线P3.0~P3.3输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。第二步,行线轮流输出低电平,从列线P3.4~P3.7读入数据,若有某一列为低电平,则对应行线上有键按下。综合一二两步的结果,可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。

识别按键的方法很多其中,最常见的方法是扫描法

按键按下时,与此键相连的行线与列线导通,行线在无按键按下时处在高电平。如果所有的列线都处在高电平,则按键按下与否不会引起行线电平的变化,因此必须使所有列线处在电平。这样,当有按键按下时,改键所在的行电平才回由高变低。才能判断相应的行有键按下。

独立按键数量少,可根据实际需要灵活编码。矩阵键盘,按键的位置由行号和列号唯一确定,因此可以分别对行号和列号进行二进制编码,然后两值合成一个字节,高4位是行号,低4位是列号。

4.键盘的工作方式

对键盘的响应取决于键盘的工作方式,键盘的工作方式应根据实际应用系统中的CPU的工作状况而定,其选取的原则是既要保证CPU能及时响应按键操作,又不要过多占用CPU的工作时间。通常键盘的工作方式有三种,编程扫描、定时扫描和中断扫描。

(1)编程扫描方式

编程扫描方式是利用CPU完成其它工作的空余时间,调用键盘扫描子程序来响应键盘输入的要求。在执行键功能程序时,CPU不再响应键输入要求,直到CPU重新扫描键盘为止。

(2)定时扫描方式

定时扫描方式就是每隔一段时间对键盘扫描一次,它利用单片机内部的定时器产生一定时间(例如10ms)的定时,当定时时间到就产生定时器溢出中断。CPU响应中断后对键盘进行扫描,并在有按键按下时识别出该键,再执行该键的功能程序。

(3)中断扫描方式

上述两种键盘扫描方式,无论是否按键,CPU都要定时扫描键盘,而单片机应用系统工作时,并非经常需要键盘输入,因此,CPU经常处于空扫描状态。

为提高CPU工作效率,可采用中断扫描工作方式。其工作过程如下:当无按键按下时,CPU处理自己的工作,当有按键按下时,产生中断请求,CPU转去执行键盘扫描子程序,并识别键号。

来源: 玩转单片机

围观 2260

在单片机开发中,由于资源受限而没有平台的支持,每次开发都要重写很多代码,应用的千奇百怪的需求更是加剧了这种困难。解决问题的办法是,总结常见的需求,分析它,得出即高效有通用的解决方案。

今天我就来为大家提供一种按键的解决方案,它易用,高效,节省资源!

先给出这个按键模块解决方案的全部代码,稍后再来分析。

keyif.h内容:

1: #ifndef __KEY_IF_H__
2: #define __KEY_IF_H__
3: ////////////////////////////////////////////////////////////////////////////////
4: typedef unsigned char u8_t;
5:
6: #define KEY_NUM_MAX (8)
7:
8: #define KEY_STA_BEGIN (0)
9: #define KEY_STA_KEEP (1)
10: #define KEY_STA_END (2)
11:
12: #define KEY_PERIOD_MS (25)
13:
14: void kif_Init(void);
15: void kif_TickHook(void);
16:
17: /*
18: kindex 按键索引,从0开始,而小于KEY_NUM_MAX。
19: 返回值:按键被按下返回非零,按键被抬起返回零。
20: */
21: extern u8_t KeyRead(u8_t kindex);
22: /*
23: kindex 按键索引,从0开始,而小于KEY_NUM_MAX。
24: ksta 按键状态,取值为KEY_STA_系列宏。
25: ktick 按键计时,以KEY_PERIOD_MS时间为计数单位。
26: 返回值:如果本次按键操作已经处理妥当,就返回非零。
27: */
28: extern u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick);
29:
30: ////////////////////////////////////////////////////////////////////////////////
31: #endif /* __KEY_IF_H__ */
32:

keyif.c内容:

1: #include "keyif.h"
2: #include
3:
4: #if defined(KEY_NUM_MAX) && (KEY_NUM_MAX > 0)
5:
6: #if KEY_NUM_MAX > 255
7: #error 该模块为8位单片机优化,不支持255个以上按键。
8: #endif
9:
10: static u8_t kticks[KEY_NUM_MAX]; // 每个按键需要1字节计时器。
11: static u8_t kstats[(KEY_NUM_MAX+7)/8]; // 每个按键需要1BIT的状态标志。
12: static u8_t kvalid[(KEY_NUM_MAX+7)/8]; // 每个按键需要1BIT的有效标志。
13: ////////////////////////////////////////////////////////////////////////////////
14: //| |
15: //| 函数名称 |: kif_Init
16: //| 功能描述 |:
17: //| |:
18: //| 参数列表 |:
19: //| |:
20: //| 返 回 |:
21: //| |:
22: //| 备注信息 |:
23: //| |:
24: ////////////////////////////////////////////////////////////////////////////////
25: void kif_Init(void)
26: {
27: memset(kticks, 0, sizeof(kticks));
28: memset(kstats, 0, sizeof(kstats));
29: memset(kvalid, 0, sizeof(kvalid));
30: }
31:
32: ////////////////////////////////////////////////////////////////////////////////
33: //| |
34: //| 函数名称 |: kif_TickHook
35: //| 功能描述 |:
36: //| |:
37: //| 参数列表 |:
38: //| |:
39: //| 返 回 |:
40: //| |:
41: //| 备注信息 |:
42: //| |:
43: ////////////////////////////////////////////////////////////////////////////////
44: void kif_TickHook(void)
45: {
46: u8_t grp, msk;
47: u8_t now;
48: u8_t i;
49:
50: grp = 0;
51: msk = 1;
52: for(i = 0; i < KEY_NUM_MAX; ++i){
53: now = (KeyRead(i) ? msk : 0);
54: if((kstats[grp] ^ now) & msk){
55: // 按键状态发生变化。
56: if(now){
57: // 按键刚被按下。
58: kticks[i] = 0;
59: kstats[grp] |= msk;
60: kvalid[grp] |= msk;
61: if(KeyProc(i, KEY_STA_BEGIN, 0)){
62: kvalid[grp] &= ~msk;
63: }
64: }
65: else{
66: // 按键刚被抬起。
67: kticks[i] += 1;
68: kstats[grp] &= ~msk;
69: KeyProc(i, KEY_STA_END, kticks[i]);
70: }
71: }
72: else if(now){
73: // 按键保持按下状态。
74: kticks[i] += 1;
75: if(kvalid[grp] & msk){
76: // 按键处于有效状态。
77: if(KeyProc(i, KEY_STA_KEEP, kticks[i])){
78: kvalid[grp] &= ~msk;
79: }
80: }
81: }
82: // 处理用于加速执行的中间变量。
83: msk <<= 1;
84: if(msk == 0){
85: msk = 1;
86: grp++;
87: }
88: }
89: }
90:
91: #else /* KEY_NUM_MAX */
92:
93: void kif_Init(void){ ; }
94: void kif_TickHook(void){ ; }
95:
96: #endif /* KEY_NUM_MAX */
97:
example.c内容:

1: #include "keyif.h"
2:
3: // 读取按键物理状态的函数。
4: u8_t KeyRead(u8_t kindex)
5: {
6: switch(kindex){
7: case 0: // 按键#0
8: if(PIN_DOWN(0)){
9: return 1;
10: }
11: return 0;
12: case 1: // 按键#1
13: if(PIN_DOWN(1)){
14: return 1;
15: }
16: return 0;
17: case 2: // 按键#2
18: if(PIN_DOWN(2)){
19: return 1;
20: }
21: return 0;
22: case 3: // 按键#3
23: if(PIN_DOWN(3)){
24: return 1;
25: }
26: return 0;
27: }
28: return 0;
29: }
30:
31: // 按键事件处理函数。
32: u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick)
33: {
34: switch(kindex){
35: case 0: // 按键#0
36: if(ksta == KEY_STA_BEGIN){
37: // 按键被按下,TODO SOMETHING。
38:
39: return 0;
40: }
41: else if(ksta == KEY_STA_KEEP){
42: // 按键被保持,TODO SOMETHING。
43:
44: return 0;
45: }
46: else if(ksta == KEY_STA_END){
47: // 按键被松开,TODO SOMETHING。
48:
49: return 0;
50: }
51: break;
52: case 1: // 按键#1
53: if(ksta == KEY_STA_KEEP && ktick == 1000/KEY_PERIOD_MS){
54: // 按键按下保持了1000毫秒,TODO SOMETHING。
55:
56: return 1;
57: }
58: break;
59: case 2: // 按键#2
60: if(ksta == KEY_STA_KEEP && ktick == 1){
61: // 按键被按下,具备了间隔KEY_PERIOD_MS毫秒的去抖动时间,TODO SOMETHING。
62:
63: return 1;
64: }
65: break;
66: case 3: // 按键#3
67: if(ksta == KEY_STA_END){
68: // 响应按键松开事件,TODO SOMETHING。
69:
70: }
71: break;
72: }
73: return 0;
74: }
75:
76: ////////////////////////////////////////////////////////////////////////////////
77: //| |
78: //| 函数名称 |: main
79: //| 功能描述 |:
80: //| |:
81: //| 参数列表 |:
82: //| |:
83: //| 返 回 |:
84: //| |:
85: //| 备注信息 |:
86: //| |:
87: ////////////////////////////////////////////////////////////////////////////////
88: int main(void)
89: {
90: // 初始化KEYIF。
91: kif_Init();
92:
93: while(1){
94: delay(KEY_PERIOD_MS);
95: // 以KEY_PERIOD_MS毫秒为周期调用。
96: kif_TickHook();
97: }
98: }

上面的代码包括了3各文件,keyif.h、keyif.c、example.c。

其中example.c是使用举例,它提供了两个必须的函数,

u8_t KeyRead(u8_t kindex);
u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick);

先来分析KeyRead()函数:

它是读取物理按键的底层函数,该函数被KEYIF模块调用。当按键按下时,它必须返回非零;按键抬起时,返回零即可。例如我们通常把按键引脚设计为低电平为按下状态,那么当读取对应引脚电平状态时,如果读取的引脚为低电平则返回非零,高电平就返回零。

kindex参数是用于区别物理引脚的。例如:kindex为0时读取GPIO_B4,kindex为1时读取GPIO_A1,等等。

再来分析KeyProc()函数:

该函数是用户的按键响应函数,在该函数中用户可以加入一切按键响应代码。该函数被KEYIF模块调用。kindex参数是按键的索引,用于标识按键;ksta参数是按键的当前状态,取值为KEY_STA_BEGIN、KEY_STA_KEEP、KEY_STA_END其中之一。BEGIN代表按键刚被按下,KEEP代表按键保持在按下状态,END代表按键被松开了。ktick参数是KEEP状态的计时器,表示按键按下状态保持了多久,单位是KEY_PERIOD_MS毫秒。

KeyProc()函数的返回值决定了本次按键操作是否还会有后续的KEEP事件,如果用户不需要后续的KEEP事件,返回非零即可。当用户告诉KEYIF不需要后续KEEP事件后,按键保持在按下状态也不会产生KEEP事件,直到下一次按键操作。无论有无KEEP事件,BEGIN和END事件始终会有的,无法关闭!

example.c文件中给出了几个应用示例,其实这个按键框架能实现的操作远不止这些!用户自己根据需求可以轻易写出各种代码。

要使用KEYIF,必须在用户代码开始处调用kif_Init()函数来初始化KEYIF。然后在主循环或者时钟中断里,以KEY_PERIOD_MS毫秒为周期调用kif_TickHook()函数。

文章来源: 博客园

围观 197
订阅 RSS - 按键