按键消抖之终极解决方案

按键消抖之终极解决方案

1.按键消抖的原理

图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.脉冲输出,此时每按下一次按键,输出一个脉冲信号。

图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篇
日博365官网手机版

《惊心食人族》影评10篇

⌛ 07-01 👁️ 7799
北宋时期历史大事年表
日博365官网手机版

北宋时期历史大事年表

⌛ 07-06 👁️ 3693