FPGA按键消抖

引出抖动问题

下图为按钮的抽象物理模型

如图所示,按钮的一端与FPGA芯片的管脚相连,而另一端接地。显然

  • 当按钮没有被按下时,FPGA的管脚为高电平
  • 当按钮被按下时,FPGA的管脚直接与地相连,为低电平

此时,理想中按下按键的波形如下图

但是,理想很美好现实很骨感,通过示波器实际看到的波形可能是这样的

从按键按下到低电平稳定,或者从按键释放到高电平稳定中间产生的电平上下浮动,称为抖动。

通过测量,抖动的时间不会超过20ms,但是FPGA的系统时钟通常为50MHz,也就是20ns,以20ns的周期来采样管脚上的电平状态。

20ns远小于20ms,由于抖动的存在,FPGA会认为按钮多次按下,而且按下的次数是不可预期的,所以对按键的消抖是很有必要的。

使用状态机解决问题

在解决问题之前,我们先来考虑该模块的输入与输出

  • 作为时序逻辑的设计模块,时钟与复位的输入必不可少
  • 该模块对按键进行消抖,所以按键的输入必不可收

输出的话,考虑消抖之后的电平,以及按键使能,如下图

模块抽象图:

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 ket_state;
endmodule

我们可以将整个波形分为以下四种状态

  • 空闲状态(IDEL):表示按键未被按下或释放按键后稳定的状态

  • 按下抖动状态(FILTER1):表示按键被按下后到低电平稳定之间的状态

  • 稳定状态(DOWN):表示按键被按下抖动之后低电平稳定的状态

  • 按下抖动状态(FILTER2):表示按键被释放后到高电平稳定之间的状态

localparam
	IDEL		=	4'b0001,
	FILTER1	=	4'b0010,
	DOWN		=	4'b0100,
	FILTER2	=	4'b1000;
reg [3:0] state;

在抖动状态下,我们需要检测上升沿或下降沿来使状态发生跳转,此时我们可以使用边缘检测电路。

边缘检测的原理是在每一个时钟上升沿到来时,检测一次管脚的电平,并且使用两个寄存器寄存此时的电平状态,先使用一个寄存器reg1寄存此时的电平状态,再使用一个寄存器reg2来寄存reg1的状态。

我们可以通过前后两个时钟上升沿到来时,电平的状态来判断此时是上升沿或是下降沿,电路图如下

  • reg1=0 reg2=1时,可以判断为上升沿,所以reg1取反和reg2相与得1
  • reg1=1 reg2=0时,可以判断为下降沿,所以reg1reg2取反相与得1
reg key_tmp1, key_tmp2;
wire pedge, nedge;

always @(posedge clk or negedge rst_n) begin
  if(~rst_n) begin
    key_tmp1 <= 1'b0;
    key_tmp2 <= 1'b0;
  end
  else begin
    key_tmp1 <= key_in;
    key_tmp2 <= key_tmp1;
  end
end

assign pedge = (~key_tmp0) & key_tmp1;
assign nedge = key_tmp0 & (~key_tmp1);

前面说到,根据示波器实际测量抖动状态一般不会超过20ms。

当状态处于抖动时,为了稳妥起见,20ms之后检测到下降沿才能将状态转换为稳定状态。

因此,我们需要一个计数电路,计数次数为20ms / 20ns = 1_000_000

reg [19:0] cnt;
reg cnt_en;

always @(posedge clk or negedge rst_n) begin
  if(~rst) begin
  	cnt <= 20'd0;
  end
  else if(en_cnt) begin
    cnt <= cnt + 1'b1;
  end
  else begin
    cnt <= 20'd0;
  end
end

reg cnt_full;

always @(posedge clk or negedge rst_n) begin
  if(~rst) begin
  	cnt_full <= 1'b0;
  end
  else if(cnt == 20'd999_999) begin
    cnt_full <= 1'b1;
  end
  else begin
    cnt_full <= 1'b0;
  end
end

下面分析一下各状态之间的跳转条件

  • IDEL

    该状态本身就处于高电平,所以不会有遇到上升沿的情况,只要不遇到下降沿状态就一直保持IDEL

    当遇到下降沿时,状态跳转到FILTER1,同时计数使能,处于按下抖动状态。

  • FILTER1

    该状态下需要根据是否计满数和是否有上升沿来做出不同的反应

    当计满数时,表示抖动状态已过,状态跳转至DOWN,并且计数不使能,key_flag为高电平,key_state为低电平

    当上升沿到来时,表示依旧有抖动存在,状态跳转会IDEL,并且计数不使能

    而两者情况都不存在时,状态保持在FILTER1

  • DOWN

    该状态与IDEL类似,但是由于是从FILTER1跳转而来,所以需要将key_flag置为低电平

    该状态本身就处于低电平,所以不会有遇到下降沿的情况,只要不遇到上升沿状态就一直保持DOWN

    当遇到上升沿时,状态跳转到FILTER2,同时计数使能,处于释放抖动状态。

  • FILTER2

    该状态与FILTER1类似

    当计满数时,表示抖动状态已过,状态跳转至IDEL,并且计数不使能,key_state为高电平

    当下降沿到来时,表示依旧有抖动存在,状态跳转会DOWN,并且计数不使能

    而两者情况都不存在时,状态保持在FILTER2

当然,这里的状态只有四种,而使用四位的格雷码还有另外12种状态,所以对于处于其他状态的情况下,将信号重置,并且状态跳转为IDEL

always @(posedge clk or negedge rst_n) begin
  if(~rst_n) begin
    state 		<= IDEL;
    cnt_en 		<= 1'b0;
    key_flag 	<= 1'b0;
    key_state   <= 1'b1;
  end
  else begin
    case(state)
      IDEL: begin
        if(nedge) begin
          cnt_en 	<= 1'b1;
        	state 	<= FILTER1;
        end
        else begin
          state 	<= IDEL;
        end
      end
      FILTER1: begin
        if(cnt_full) begin
          cnt_en 		<= 1'b0;
          state			<= DOWN;
          key_flag 	    <= 1'b1;
          key_state     <= 1'b0;
        end
        else if(pedge) begin
          cnt_en 	<= 1'b0;
          state		<= IDEL;
        end
        else begin
          state		<= FILTER1;
        end
      end
      DOWN: begin
        key_flag 	<= 1'b0;
        if(pedge) begin
          cnt_en 	<= 1'b1;
        	state 	<= FILTER2;
        end
        else begin
        	state 	<= DOWN;
        end
      end
      FILTER2: begin
        if(cnt_full) begin
          cnt_en 		<= 1'b0;
          state			<= IDEL;
          key_state     <= 1'b1;
        end
        else if(nedge) begin
          cnt_en 		<= 1'b0;
          state			<= DOWN;
        end
        else begin
          state 		<= FILTER2;
        end
      end
      default: begin
        cnt_en 		<= 1'b0;
        state 		<= IDEL;
        key_flag 	<= 1'b0;
    	key_state <= 1'b1;
      end
    endcase
  end
end

仿真

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你高数作业写了吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值