按键消抖
Posted ajiaoa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了按键消抖相关的知识,希望对你有一定的参考价值。
摘要:
本节将单 Bit 数据的异步信号同以及边沿检测方法引入到 FPG A中常用 的按键消抖设计,并在仿真测试吉利文件中引入随机数发生函数
消抖的实现分为硬件实现和软件实现,
产生的抖动次数以及间隔时间均是不可预期的,这就需要通过
滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持
续20ms 以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,也可
以通过软件的方式完成。其中硬件电路消除抖动适用于按键数目较少的场合。
下面先来讲硬件实现:
使用基于与非门的RS 触发器来消除抖动的电路图,如图 3.7-4 所示。假设
初始状态开关与B 接通,此时S=1,R=0,触发器置0 即Q=0;当B 切换到A 的
过程中,存在开关既没有与A 也没有与B 接触,此时S=1,R=1,触发器保持即
Q=0;当切换到A 瞬间时有R=1,S=0,触发器置1 即Q=1。此时即使当A 出现抖
动即S=1,触发器的状态仍能保持当前状态即Q=1 不变。整个过程波形图如图所
示Q。同理,可以分析出开关由A 切换到B 时触发器的状态变化。
请注意:这只适合于单刀双掷开关。
而对于两脚和四脚的按键目前常用的是RC电路和555定时器组成的单稳态触发器。
接下来是软件实现:
在按键没有按下时,处于高电平状态。
代码实现:
module key_filter( clk, rst_n, key_in, key_flag, key_state ); input clk; input rst_n; input key_in; output reg key_flag;//按键状态切换标志 output reg key_state;//按键状态 //因为按键是异步信号,先同步到系统时钟域 reg key_in_r,key_in_rr; always@(posedge clk or negedge rst_n) if(!rst_n)begin key_in_r<=0; key_in_rr<=0; end else begin key_in_r<=key_in; key_in_rr<=key_in_r; end //按键检测用到上升沿和下降沿检测 reg key_in_rr_1,key_in_rr_11; wire pedge,nedge; always@(posedge clk or negedge rst_n) if(!rst_n)begin key_in_rr_1<=0; key_in_rr_11<=0; end else begin key_in_rr_1<=key_in_rr; key_in_rr_11<=key_in_rr_1; end assign pedge=(!key_in_rr_11) && key_in_rr_1; assign nedge=key_in_rr_11 && (!key_in_rr_1); //抖动计数包含使能模块和计数模块(按20ms来滤除,需要计数到1000000-1) reg [19:0]cnt; reg cnt_full; reg en_cnt; always@(posedge clk or negedge rst_n) if(!rst_n) cnt<=20‘d0; else if(en_cnt) cnt<=cnt+1‘b1; else cnt<=20‘d0; always@(posedge clk or negedge rst_n) if(!rst_n) cnt_full<=1‘b0; else if(cnt==20‘d999_999) cnt_full<=1‘b1; else cnt_full<=1‘b0; //状态机 localparam IDLE=4‘b0001,FILTER0=4‘b0010,DOWN=4‘b0100,FILTER1=4‘b1000; reg [3:0]state; always@(posedge clk or negedge rst_n) if(!rst_n)begin en_cnt<=1‘b0; state<=IDLE; key_flag<=1‘b0; key_state<=1‘b1; end else begin case(state) IDLE:begin key_flag=1‘b0; if(nedge)begin en_cnt<=1‘b1; state<=FILTER0; end else state<=IDLE; end FILTER0:begin if(cnt_full)begin en_cnt<=1‘b0; state<=DOWN; key_flag<=1‘b1; key_state<=1‘b0; end else if(pedge)begin state<=IDLE; en_cnt<=1‘b0; end else state<=FILTER0; end DOWN:begin key_flag<=1‘b0; if(pedge)begin en_cnt<=1‘b1; state<=FILTER1; end else state<=DOWN; end FILTER1:begin if(cnt_full)begin en_cnt<=1‘b0; state<=IDLE; key_flag<=1‘b1; key_state<=1‘b1; end else if(nedge) state<=DOWN; else state<=FILTER1; end default:state<=IDLE; endcase end endmodule
tb:
`timescale 1ns/1ns module key_filter_tb; reg clk; reg rst_n; reg key_in; wire key_flag;//按键状态切换标志 wire key_state;//按键状态 key_filter key_filter( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); initial clk=0; always #10 clk=~clk; initial begin rst_n=0; #21; rst_n=1; //模拟抖动在20ms以内 key_in=0;#1000; key_in=1;#2000; key_in=0;#1400; key_in=1;#2600; key_in=0;#1300; key_in=1;#200; //产生一个低电平大于20ms,代表稳定按下 key_in=0;#20_000_100; #30_000_000; //模拟释放抖动20ms以内 key_in=1;#1000; key_in=0;#2000; key_in=1;#1400; key_in=0;#2600; key_in=1;#1300; key_in=0;#200; //产生一个高电平大于20ms,代表稳定释放 key_in=1;#20_000_100; #30_000_000; $stop; end endmodule
tb_task:
`timescale 1ns/1ns module key_filter_tb_task; reg clk; reg rst_n; reg key_in; wire key_flag;//按键状态切换标志 wire key_state;//按键状态 key_filter key_filter( .clk(clk), .rst_n(rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); integer myrand; initial clk=0; always #10 clk=~clk; task press_key; begin repeat(50)begin myrand={$random}%65536; #myrand key_in=~key_in; end key_in=0; #50_000_000; repeat(50)begin myrand={$random}%65536; #myrand key_in=~key_in; end key_in=1; #50_000_000; end endtask initial begin rst_n=0; key_in=1; #21; rst_n=1; press_key;#10000; press_key;#10000; press_key;#10000; $stop; end endmodule
波形图:
抖动放大:
以上是关于按键消抖的主要内容,如果未能解决你的问题,请参考以下文章