系统的信号输入中,键盘因其结构简单而被广泛使用。因此,对键盘的输入(逻辑0或1)进行准确采样,避免错误输入是非常有必要的。理想的键盘输入特性如图1所示:按键没有按下时,输入为逻辑1,一旦按下则输入立刻变为逻辑0,松开时输入则立刻变为逻辑1。
图 1理想键盘输入特性
然而实际的键盘受制造工艺等影响,其输入特性不可能如图1完美。当按键按下时,在触点即将接触到完全接触这段时间里,键盘的通断状态很可能已经改变了多次。即在这段时间里,键盘输入了多次逻辑0和1,也就是输入处于失控状态。如果这些输入被系统响应,则系统暂时也将处于失控状态,这是我们要尽量避免的。在触点即将分离到完全分离这段时间也是一样的。实际键盘的输入特性如图2所示:
图 2实际键盘输入特性
我们可以看到:键盘在输入逻辑转换时,实际上是产生了瞬时的高频干扰脉冲。按键消抖的目的在于消除此干扰,以达到接近图1所示的理想输入特性。有两个阶段可以设法消除此干扰:1.在键盘信号输入系统之前(系统外);2.键盘信号输入系统以后(系统内)。
在信号输入系统之前将抖动干扰消除,可以节省系统资源,提高系统对其他信号的响应能力,也就是硬件消抖。一种比较巧妙的硬件消抖电路结构如图3所示:
图 3用基本SR锁存器构成的消抖电路
该电路利用基本SR锁存器的记忆作用消除开关触点振动所产生的影响。开关S每切换一次,输出端只有一次翻转,不存在抖动波形(读者可以根据SR锁存器功能自行分析,此处略)。但是使用SR锁存器消抖只适用于单刀双掷开关,实际应用当中常用的键盘多是两个接线端的按键。对此类按键的常用硬件消抖电路如图4所示:
图 4常用键盘硬件消抖电路
此电路利用电容平波,再经过施密特反相器整形之后就得到了没有毛刺的脉冲波。
软件消抖要占用系统资源,在系统资源充足的情况下使用软件消抖更加简单。软件消抖的实质在于降低键盘输入端口的采样频率,将高频抖动略去。实际应用中通常采用延时跳过高频抖动区间,然后再检测输入做出相应处理。一般程序代码如下:
if(value == 0) //一旦检测到键值 { Delay(); //延时20ms,有效滤除按键的抖动 if(value == 0) //再次确定键值是否有效 { …… //执行相应处理 } } |
这段软消抖程序从机理上看不会有什么问题,通常在软件程序不太"繁忙"的情况下也能够很好的消抖并做相应处理。但是如果在延时期间产生了中断,则此中断可能无法得到响应。
对于硬件资源丰富的FPGA系统,可以使用硬件来减轻软件工作量,通常称之为"硬件加速"。在按键信号输入到软件系统前用逻辑对其进行一下简单的处理即可实现所谓的"硬件消抖",verilog代码如下:
//对输入信号inpio硬件滤波,每20ms采样一次当前值 reg[18:0] cnt; //20ms计数器 always @(posedge clk_25m or negedge rst_n) if(!rst_n) cnt <= 19'd0; else if(cnt < 19'd500000) cnt <= cnt+1'b1; else cnt <= 19'd0; reg[1:0] inpior; //当前inpio信号锁存,每20ms锁存一拍 always @(posedge clk_25m or negedge rst_n) if(!rst_n) inpior <= 2'b11; else if(cnt == 19'h7ffff) inpior <= {inpior[0],inpior}; wire inpio_swin =inpior[0] | inpior[1]; //前后20ms两次锁存值都为0时才为0 |
该程序中设置了一个20ms计数器,通过间隔20ms对输入信号inpio采样两次,两次相同则认为键盘输入稳定,得到用硬件逻辑处理后的inpio_swin信号则是消抖处理过的信号。软件程序就不再需要delay()来滤波了,也不会出现使用纯软件处理出现的"中断失去响应"的情况了,这就是"硬件加速"的效果。
上述verilog代码采用间隔采样来达到消抖的目的,对于不同物理特性的键盘,最佳的间隔时间采样时间也不同,因此还存在一些不稳定因素。下面介绍一种更好的软消抖程序,同样采用"硬件加速",不同之处在于使用了有限状态机来实现,其VHDL代码如下:
LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ENTITY xiaod IS //端口描述:clk 输入检测时钟;reset 复位信号;din 原始按键信号输入; dout 去抖动输出信号 PORT ( clk : IN STD_LOGIC ; reset : IN STD_LOGIC ; din : IN STD_LOGIC ; dout : OUT STD_LOGIC ); END ENTITY; ARCHITECTURE RTL OF xiaod IS TYPE state IS( s0,s1,s2,s3); SIGNAL pre_s, next_s: state; BEGIN P0:PROCESS( reset, clk ) BEGIN if reset = '0' then pre_s <= s0; elsif rising_edge( clk ) then pre_s <= next_s; else null; end if; END PROCESS P0; P1:PROCESS( pre_s, next_s, din ) BEGIN case pre_s is when s0 => dout <= '1'; if din = '1' then next_s <= s0; else next_s <= s1; end if; when s1 => dout <= '1'; if din = '1' then next_s <= s0; else next_s <= s2; end if; when s2 => dout <= '1'; if din = '1' then next_s <= s0; else next_s <= s3; end if; when s3 => dout <= '0'; if din = '1' then next_s <= s0; else next_s <= s1; end if; end case; END PROCESS P1; END RTL; |
该VHDL代码描述了一个状态机,其状态转换图如图所示:
图 5状态转换图
该状态机有4个状态:S0、S1、S2、S3,其中前3个状态输出高电平,最后一个状态输出低电平。初始状态为S0,设按键未按下时为高电平,按下则为低电平。在按键按下到完全生效期间有一系列的抖动,对于持续时间为1-2个时钟周期的低电平抖动将被消除,对于持续时间为3个或以上时钟周期的低电平则认为按键有效,输出一个时钟周期的低电平脉冲(读者可以根据状态转换图画出相应的时序图进行分析)。如果持续输入为低电平,则每隔两个时钟周期输出一个低电平,此时认为按键处于"长按"输入状态,可以编程设置相应功能。在按键松开阶段其抖动也可以一样被消除。
适用于FPGA的按键消抖方法还有一些,如计数器型、D触发器型等,在此就不作介绍了。
通过上面一些按键消抖方法的介绍分析,我们可以看到,传统单片机等系统大多是串行处理,即顺序执行,只能并行处理一些中断程序。对于这样的系统,只能采用单纯软件或硬件消抖,但都不那么完美。而对于FPGA等并行处理的系统,其优势就很明显,只要片内逻辑资源够用,通过硬件加速软件消抖的处理,完全可以做到按键消抖并行化,不影响系统的实时性。
全部0条评论
快来发表一下你的评论吧 !