按键去抖的原因及其分类就不罗嗦了。
在这里解释一段代码,代码是网上找的,看了半天没懂,无奈查了半天想了半天,终于明白了。。。
module sw_debounce(
clk,rst_n,sw1,sw2,sw3,//outputled_d3,led_d4,led_d5);input clk;input rst_n;input sw1,sw2,sw3; //Active lowoutput led_d3;output led_d4;output led_d5;// ---------------------------------------------------------------------------// 通过降采样对sw1~sw3 的输入做低通滤波,将其高频分量滤除,得到low_sw 值// ---------------------------------------------------------------------------reg [19:0] cnt;always @ (posedge clk or negedge rst_n)if (!rst_n)cnt <= 20'd0;elsecnt <= cnt + 1'b1;reg [2:0] low_sw;always @(posedge clk or negedge rst_n)if (!rst_n)low_sw <= 3'b111;else if (cnt == 20'hfffff) //每隔20MS 检测一次按键low_sw <= {sw3,sw2,sw1};// ---------------------------------------------------------------------------// ---------------------------------------------------------------------------reg [2:0] low_sw_r; //将low_sw 信号锁存一个时钟周期,延时不是真的“锁存”always @ ( posedge clk or negedge rst_n )if (!rst_n)low_sw_r <= 3'b111;elselow_sw_r <= low_sw;wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);//当检测到按键有下降沿变化时,代表该按键被按下,按键有效reg d1;reg d2;reg d3;always @ (posedge clk or negedge rst_n)if (!rst_n)begind1 <= 1'b0;d2 <= 1'b0;d3 <= 1'b0;endelsebeginif ( led_ctrl[0] ) d1 <= ~d1;if ( led_ctrl[1] ) d2 <= ~d2;if ( led_ctrl[2] ) d3 <= ~d3;endassign led_d5 = d1 ? 1'b1 : 1'b0;assign led_d3 = d2 ? 1'b1 : 1'b0;assign led_d4 = d3 ? 1'b1 : 1'b0;具体原理:通常,按键抖动会产生10--20MS 的毛刺,因此要做的实际上就是在20MS 中采样一次,当检测到按键下降沿的时候,就认定按下,其他状态忽略。采用 50MHz 晶振,时钟周期是20ns,else if (cnt == 20'hfffff) //每隔20MS 检测一次按键low_sw <= {sw3,sw2,sw1};reg [2:0] low_sw_r; //将low_sw 信号锁存一个时钟周期,延时不是真的“锁存”always @ ( posedge clk or negedge rst_n )if (!rst_n)low_sw_r <= 3'b111;elselow_sw_r <= low_sw;wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);//当检测到按键有下降沿变化时,代表该按键被按下,按键有效个人觉得,锁存一个时钟周期, 在 FPGA 里的应用实在是太多了,几乎所有的程序都要用到,作用无非是防止竞争冒险,将一个信号延迟一个时钟周期(low_sw_r[2:0]),原来的信号取反(~low_sw[2:0]),2个信号与一下,便可以检测到一个下降沿的变化,从而产生一个宽度为一个时钟周期(20ns)的脉冲,然后将这个脉冲作为控制信号去控制别的进程。。。
上面都是转自一位兄弟的博客,我在这里解释一下关于锁存一个周期是怎么回事。这里的所存是通过非阻塞语句实现的,always块是并行的,同时执行,非阻塞语句的赋值是先计算所有等式右边的表达式的值,然后一齐赋值,在计算期间,也就是在always块结束以前,等式左边等待赋值的变量仍然保持原来的值,这样,第二级锁存器low_sw_r所存的永远是low_sw上一次的值,这样就实现了将信号锁存一个周期。
wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);这是一个很经典的下降沿检测语句。因为非阻塞赋值语句,low_sw_r中是low_sw上一个周期的值,将其与这个周期的low_sw取反后相与,就得到按键是否按下的检测结果,0是没按下,1是按下。
转载自:http://www.cnblogs.com/lamapig/archive/2010/10/03/1841537.html