原创 按键消抖之脉冲边沿检测

2013-10-2 16:22 3010 6 8 分类: FPGA/CPLD

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。

20120914102545474.jpg
                                   图为: 单片机边沿检测
20120914102546723.jpg
                                   图为:FPGA脉冲边沿检测

 

module sw_debounce(
    clk,rst_n,
sw1_n,sw2_n,sw3_n,
  led_d1,led_d2,led_d3
    );
 
input   clk; //主时钟信号,50MHz
input   rst_n; //复位信号,低有效
input   sw1_n,sw2_n,sw3_n; //三个独立按键,低表示按下
output  led_d1,led_d2,led_d3; //发光二极管,分别由按键控制
 
//---------------------------------------------------------------------------
reg[2:0] key_rst;  
 
always @(posedge clk  or negedge rst_n)
    if (!rst_n) key_rst <= 3'b111;
    else key_rst <= {sw3_n,sw2_n,sw1_n};
 
reg[2:0] key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
 
always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) key_rst_r <= 3'b111;
    else key_rst_r <= key_rst;
   
//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期 
wire[2:0] key_an= key_rst_r & (~key_rst);
/*
key_rst     1 1 1 0 0 1
~key_rst    0 0 0 1 1 0
key_rst_n     1 1 1 0 0 1
key_an        0 0 1 0 0
*/
//---------------------------------------------------------------------------
reg[19:0]  cnt; //计数寄存器
 
always @ (posedge clk  or negedge rst_n)
    if (!rst_n) cnt <= 20'd0; //异步复位
else if(key_an) cnt <=20'd0;
    else cnt <= 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中 cnt == 20'hfffff
      low_sw <= {sw3_n,sw2_n,sw1_n};
      
//---------------------------------------------------------------------------
reg  [2:0] low_sw_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
 
always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) low_sw_r <= 3'b111;
    else low_sw_r <= low_sw;
/*
low_sw 111 111 111 110 110 110  
~low_sw     000 000 000 001 001 001
low_sw_r        111 111 111 110 110 110
led_ctrl   000 000 000 001 000 000 
   */
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期 
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) begin
        d1 <= 1'b0;
        d2 <= 1'b0;
        d3 <= 1'b0;
      end
    else begin //某个按键值变化时,LED将做亮灭翻转
        if ( led_ctrl[0] ) d1 <= ~d1;
        if ( led_ctrl[1] ) d2 <= ~d2;
        if ( led_ctrl[2] ) d3 <= ~d3;
      end
 
assign led_d3 = d1 ? 1'b1 : 1'b0; //LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;
  
endmodule

具体原理:通常,按键抖动会产生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;
else
low_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是按下。

下面是百度搜索按键消抖verilog模块:个人觉得没有特权同学说的那么规范,好像缺少了好多东西,没有实在感,可能习惯看特权同学的verilog书写规范吧,觉得下面的verilog语言好别扭。(仅供参考)

 

Verilog HDL语言实现按键消抖
assign key_done = (dout1 | dout2 | dout3); //按键消抖输出
  always @(posedge count[17])
  begin
  dout1 <= key_in;
  dout2 <= dout1;
  dout3 <= dout2;
  end
  always @(negedge key_done[0])
  begin
  keyen = ~keyen; //将琴键开关转换为乒乓开关
  end
程序中所用的方法是不断检测按键值。每当Count[17]上升沿到来,就进行检测输入信号。其中dout1,dout2,dout3分别为当前、上个Count[17]上升沿、上上个Count[17]上升沿输入数值。正常情况下为1,假如连续三次为0,三个信号作或运算,使得key_done信号为0,出现下降沿,这样就认为是有按键。
边沿检测法RTL和MAP Viewer,感受下边沿检测的魅力
rtl.jpg

 

tri.jpg
边沿检测应用
 
边沿检测技术在项目应用中,非常低广泛。如要有效捕获信号跳变沿,边沿检测技术的应用是必不可少的。大致归纳了一下,有如下几个方面
 
(1)将时钟边沿使能转换为边沿检测使能,使时钟同步化。
 
(2)捕获信号的突变(UART,SPI等信号使能突变)
 
(3)逻辑分析仪中信号的边沿检测。
PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户971939 2015-8-18 15:30

写的太好了 一个点就点透了

用户971939 2015-8-18 15:28

看特权的讲解看了N遍 看的糊涂 这下明白了 多谢
相关推荐阅读
用户443437 2015-04-17 08:57
(多图)结合FPGA与DSP的仿人假手控制系统设计
仿人假手作为肢残患者重获人手功能的主要对象,具有重大的社会需求。理想的假手应具有人手的仿生特征,主要体现在假手构造、控制方式与环境感知3 个方面,但由于其有限的体积和复杂的传感器系统,对控制系统提出了...
用户443437 2014-05-10 14:37
熬了几个通宵,答辩终于搞掂
当答辩的评分老师问我:你有FPGA开发板了,外设也是买的,你还需要做什么硬件部分?其实那时我真的想问一下:老师,你其实懂不懂FPGA设计的?外设是谁来驱动的?它不同于ARM,可以用软件来驱动,做一...
用户443437 2014-04-25 09:52
[博客大赛]我也开始玩起Qsys
好久没有发博客了,前一段时间是毕业前的阵痛期,十分的纠结,关于FPGA的东西都不想理了,可以说是力不从心吧,实现就是这样,社会不急切需求这方面的人才,我该何去何从?恋上简单,爱上很难,且行且珍惜。...
用户443437 2014-04-10 17:23
[博客大赛]关于SD卡的读取问题
  最近一直在调试SD卡的读取数据,用串口调试助手来对比数据的正确,但是一直都读不出数据来,代码是特权同学的SD控制模块,我只是做了一些定制,用串口来调试,代码是没有问题的,纠结了我很久...
用户443437 2014-03-21 09:27
口头承诺真的成为了空谈
这几天终于把该忙的工作的搞掂了,接下来的时间都属于我自己的,可以安心去规划一下自己的未来发展方向了,之前一份做彩屏接口研发的工作是最适合我,本人也是对图像的处理感兴趣,第一份工作找的就是自己喜欢的...
用户443437 2014-03-15 11:06
【我要崛起】第五章 器件时序图的分析,状态机最重要。(1)(更新)
这一章出得有点慢,终于挤到时间出来好好地写,在总结之前要说明一点的是,这一章我分三个小节来叙说,第一小节是从基础的器件开始,咱们的字符型LCD1602的datasheet分析;到第二节的TFT-L...
EE直播间
更多
我要评论
2
6
关闭 站长推荐上一条 /3 下一条