原创 [博客大赛]按键消抖之终极解决方案

2014-4-25 10:31 6899 14 24 分类: FPGA/CPLD 文集: FPGA编码
1.按键消抖的原理

5000179125_1398243521525.jpg

                 图1.按键抖动示意图

我们平常所用的按键为机械弹性开关,由于触点的弹性作用,按键在闭合时不会马上稳定的接通,而是有一段时间的抖动,在断开时也不会立即断开。抖动时间由按键的机械特性所决定,一般为5ms~10ms。所以我们在做按键检测时都要加一个消抖的过程。

按键消抖主要有两种方案:

一是延时重采样;二是持续采样。

从理论上来说,延时(如10ms)重采样的准确率肯定低于持续采样。
 
2.按键消抖的方法
(1)延时重采样
延时重采样的意思是,当第一次检测到键值由'1'变为'0'时,再延时一段时间(如10ms),再次采样,确认是否仍是'0';若是'0'则认为此时键值为'0',否则,重新执行检测过程。
这个方案在特权同学的《深入浅出玩转FPGA》的p191有例程;
 
该方案的缺陷:a.如果延时太短,有可能两次采样时都处于抖动时间,因此可能引起误判;
                         b.如果延时太长,可能检测不出按键变换
 
(2)持续采样
持续采样的原理是,当检测到按键处于某电平(如'0')时,在之后的N个时钟周期内连续检测此按键的电平,如果一直不变,则读出此按键的电平值(如'0')。
持续采样的优点:a.样本足够多,减少误判的可能性。
                            b.对于按键按下('1'->'0'),按键释放('0'->'1')都可以检测。
持续采样的缺点:持续检测的时间太长(大于按键按下和释放的时间差),则可能无法检测按键的变换。
 
1)单个按键的检测
按键检测的输出有两种方式:1.电平输出,此时按键功能犹如拨码开关。
                                              2.脉冲输出,此时每按下一次按键,输出一个脉冲信号。
 
 20140423164018734.jpg
                     图2.按键检测输出波形示意图
如图2所示,Key_out1的输出与Key_in的变换趋势相同,只是滤除了抖动成分;
Key_out2则是每当按键按下后输出一个高电平脉冲。在大多数的应用中会用到Key_out2所示功能。
 
输出为电平(用于电平判断事件,类似于开关选择)
module key_scan
#(parameter DURATION = 1200)//the number of clk period
(
input wire clk, //120MHz
input wire rst_n,
input wire key_in,
 
output reg key_out
);
 
//key jitter filter
reg[11:0] low_cnt;
reg[11:0] high_cnt;
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  begin
       low_cnt <= 0;
       high_cnt <= 0;
  key_out <= 1'b1;
  end
 else
       begin
       if(key_in == 1'b0)
           begin
           high_cnt <= 0;
           if(low_cnt == DURATION)
               begin low_cnt <= low_cnt; key_out <= 1'b0; end
           else
               low_cnt <= low_cnt + 1'b1;
           end
       else //key_in == 1'b1
           begin
           low_cnt <= 0;
           if(high_cnt == DURATION)
               begin high_cnt <= high_cnt;key_out <= 1'b1; end
           else
               high_cnt <= high_cnt + 1'b1;
           end
       end
end
endmodule
 
输出为脉冲(用于脉冲触发事件)
module key_scan
#(parameter DURATION = 1200)//the number of clk period
(
input wire clk, //120MHz
input wire rst_n,
input wire key_in,
 
output wire key_out
);
 
//key jitter filter
reg[11:0] low_cnt;
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
        low_cnt <= 0;
 else
        begin
        if(key_in == 1'b0)
            begin
            if(low_cnt == DURATION)
                low_cnt <= low_cnt;
            else
                low_cnt <= low_cnt + 1'b1;
            end
        else //key_in == 1'b1
            low_cnt <= 0;
        end
end
 
assign key_out = (low_cnt == DURATION -1)? 1'b1 : 1'b0;
 
endmodule
 
2)多个独立按键的扫描(扫描得出键值)
功能:a.可以检测出哪些键按下了,甚至哪些键同时按下了。
           b.键值更新后,输出一个脉冲信号,提升更新完成;
           c.键值保持到下一次更新完成。
 
module key_counter_scan
#(
 parameter KEY_WIDTH = 4
)
(
 //global clock
 input clk,
 input rst_n,
 
 //key interface
 input [KEY_WIDTH-1:0] key_data,
 
 //user interface
 output reg key_flag,
 output reg [KEY_WIDTH-1:0] key_value //H Valid
);
 
//-----------------------------------
//Register key_data for compare
reg [KEY_WIDTH-1:0] key_data_r;
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_data_r <= {KEY_WIDTH{1'b1}};
 else
  key_data_r <= key_data;
end
 
 
//-----------------------------------
//continue 20ms
localparam DELAY_TOP = 20'd1000_000;
//localparam DELAY_TOP = 20'd1000; //Just for test
reg [19:0] delay_cnt;
//-----------------------------------
//Key scan via counter detect.
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  delay_cnt <= 0;
 else
  begin
  if((key_data == key_data_r) && (key_data != {KEY_WIDTH{1'b1}})) //20ms counter jitter
   begin
   if(delay_cnt < DELAY_TOP)
    delay_cnt <= delay_cnt + 1'b1;
   else
    delay_cnt <= DELAY_TOP;
   end
  else
   delay_cnt <= 0;
  end
end
 
 
//-----------------------------------
//the complete of key_data capture
wire key_trigger = (delay_cnt == DELAY_TOP - 1'b1) ? 1'b1 : 1'b0;
 
//-----------------------------------
//output the valid key_value via key_trigger
always@(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_value <= {KEY_WIDTH{1'b0}};
 else if(key_trigger)
  key_value <= ~key_data_r;
 else
  key_value <= key_value;
end
 
//---------------------------------
//Lag 1 clock for valid read enable
always@(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_flag <= 0;
 else
  key_flag <= key_trigger;
end
 
endmodule
 

文章评论10条评论)

登录后参与讨论

用户1069018 2015-9-28 16:27

谢谢分享.

用户1844767 2015-7-15 08:39

顶!d=====( ̄▽ ̄*)b

2005jiangxu_694877046 2015-6-15 21:17

感谢分享!已经收藏。

用户424046 2015-6-15 07:25

不错,学习了!

用户620312 2014-5-9 10:59

不错

用户1754563 2014-5-1 10:32

很棒

gaon2_614908070 2014-4-30 14:18

这帖子不错,简单的问题,详细的分析,只有精细化,才能做出优秀的产品。

用户1009097 2014-4-28 20:30

无抖动的信号, 如是缓慢单调上升或者下降的电平, 到了某些FPGA/EPLD上也可能会出现如同抖动的系列电平变化。 这个方式也同样适用。

用户1752346 2014-4-26 08:36

好分享~!

残弈悟恩 2014-4-24 01:34

好,顶起。
相关推荐阅读
用户468654 2014-04-27 19:30
MinGW编译cpp文件
1.下载MinGW:http://ishare.iask.sina.com.cn/f/23746562.html?retcode=0 2.安装MinGW; 3.添加环境变量中的Path值等...
用户468654 2014-04-26 23:21
“mspdb80.dll无法找到”之解决之道
我在电脑是安装了VS2010。在用命令行调用cl.exe来编译cpp文件时遇到如题的问题。最后查了网络资料。解决方法如下: 1>直接从C:\Program Files\Microsoft...
用户468654 2014-04-25 10:31
[博客大赛]74HC595-串行并出驱动器
1.芯片原理图 2.时序图(Timing diagram) 3.根据时序图编写74hc595的verilog驱动程序 功能:根据输入的8bits数据d...
用户468654 2014-04-23 16:25
【转】FPGA学习的一些误区
作者:某人(摘自网络,不知道作者) 我常年担任多个有关FPGA学习研讨的QQ群管理员,长期以来很多新入群的菜鸟们总是在重复的问一些非常简单但是又让新手困惑不解的问题。作为管理员经常要给这些菜鸟们...
用户468654 2014-04-23 16:13
何為"阻抗匹配"?
  阻抗匹配(Impedance matching)是微波电子学里的一部分,主要用于传输线上,来达至所有高频的微波信号皆能传至负载点的目的,不会有信号反射回来源点,从而提升能源效益。   大体...
我要评论
10
14
关闭 站长推荐上一条 /2 下一条