按键消抖的三种方案

首先,做两个假定,以方便后面的描述:

  • 假定按键的默认状态为0,被按下后为1
  • 假定按键抖动时长小于20ms,也即使用20ms的消抖时间

方案1:在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平

方案2:在20ms计时的过程中,有任何的电平变化都立即复位计时

方案3:在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动

1、方案1代码:

module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

    // 20ms parameter
    // localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000; // just for test

    // variable
    reg [20:0] cnt;
    reg key_cnt;
    
    // debounce time passed, refresh key state
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_out <= 0;
        //在20ms结束,才将key_in赋值给key_out
        else if(cnt == TIME_20MS - 1)
            key_out <= key_in;
    end

    // while in debounce state, count, otherwise 0
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt)
            cnt <= cnt + 1'b1;
        else
            cnt <= 0; 
    end
     
     always @(posedge clk or negedge nrst) begin
            if(nrst == 0)
                key_cnt <= 0;
            else if(key_cnt == 0 && key_in != key_out)
                key_cnt <= 1;
            else if(cnt == TIME_20MS - 1)
                key_cnt <= 0;
     end
endmodule

2、方案2代码:

module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

	//localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000;

    reg key_cnt;
    reg [20:0] cnt;

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_cnt <= 0;
        else if(cnt == TIME_20MS - 1)
            key_cnt <= 0;
        else if(key_cnt == 0 && key_out != key_in)
            key_cnt <= 1;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt) begin
        //发生任何抖动,都立即复位,是通过这里实现的
        //因为正常情况下,按下或者松开按键都会导致key_in发生变化
        //以至于和key_out不相等。所以,如果相等了,说明中间key_in发生了变化,
        //那么我们就需要重新复位,重新计数。还有一种情况就是,key_in压根一开始就没变化,
        //例如按键没有被按下或者松开,那么我们也一直复位,不让其计数,这样key_out也可以不变化了。
            if(key_out == key_in)
                cnt <= 0;
            else
                cnt <= cnt + 1'b1;
        end
        else
            cnt <= 0;
    end
     
     always @(posedge clk or negedge nrst) begin
            if(nrst == 0)
                key_out <= 0;
            //在20ms结束,才将key_in赋值给key_out
            else if(cnt == TIME_20MS - 1)
                key_out <= key_in;
     end
endmodule

3、方案3代码:

module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

	//localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000; // just for test

    reg key_cnt;
    reg [20:0] cnt;

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_cnt <= 0;
        else if(key_cnt == 0 && key_out != key_in)
            key_cnt <= 1;
        else if(cnt == TIME_20MS - 1)
            key_cnt <= 0;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt)
            cnt <= cnt + 1'b1;
        else
            cnt <= 0;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_out <= 0;
        //一开始,就直接将key_in赋值给key_out,也即方案3中说的:
        //只要一发生变化,就立即将输入赋给输出,然后保持20ms后输出。
        else if(key_cnt == 0 && key_out != key_in)
            key_out <= key_in;
    end
endmodule
posted @ 2020-09-24 11:33  耐心的小黑  阅读(433)  评论(0编辑  收藏  举报