下冰雹 · 2022年03月09日

【安路 EG4S20 版本】按键消抖实验

按键抖动和消抖原理

本例中,我们通过按键来触发计数器动作,按一下数字自增1,同时显示在数码管的最低位上。为了体现按键消抖的必要性,我们通过拨动开关sw0来选择消抖前后的信号来驱动计数器,观察消抖前后的计数效果。
在消抖前,有时按下一次按键后,数码管显示的16进制数并未按预期的增加1,而是增加了2甚至3,这就是按键抖动造成的结果。一般来说,按键为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,按键开关在闭合时不会马上稳定地接通,在断开时也不会立刻断开,而是在闭合和断开的瞬间都伴随有一连串的抖动,如下图所示。因此,如果不对按键抖动进行处理,就可能对按键信号造成误判,例如将按下一次按键误判为多次按下按键。
image.png
按键抖动示意图
为了消除按键抖动的影响,就需要进行按键消抖。按键消抖有多种实现方法,相关的网络资源也有很多,这里将分享其中一种按键消抖方法。由于按键抖动的时间一般在10-20毫秒,而按键稳定的时间一般为数百毫秒,所以可以设置多个寄存器,延时读取按键的电平信号,再综合判断按键是否按下。
image.png
按键消抖示意图

代码解析

下面给出了按键消抖模块的参考代码。按键消抖模块中设置了三个寄存器构成移位寄存器,每20ms进行一次移位,寄存器key_reg0寄存的是当前的按键信号电平,寄存器key_reg1寄存的是20ms前的按键信号电平,而寄存器key_reg2寄存的是40ms前的按键信号电平。消抖后的按键信号为key_deb,当key_deb为1时代表按键按下,key_deb为0时代表按键未按下,代码中通过assign语句对key_deb进行赋值。这里判定两种情况下按键被按下,一是三个按键寄存器都为低电平,这显然是按键稳定的情况,第二种是40ms前的按键信号为高电平,而20ms前的按键信号和当前的按键信号为低电平。


module key_filter(clk,rstn,key_in,key_deb);
    input clk;
    input rstn;
    input key_in;
    output key_deb;
​
    //50M/50 = 1M
    parameter DIVCLK_CNTMAX_20ms = 499999;
    wire clk_20ms;

​
    //例化时钟分频模块,得到周期为20ms的时钟
    clock_division #(
        .DIVCLK_CNTMAX(DIVCLK_CNTMAX_20ms)
    )
    my_clock_0(
        .clk_in(clk),
        .divclk(clk_20ms)
    );

​
     reg key_reg0;
     reg key_reg1;
     reg key_reg2; 
     always@(posedge clk_20ms or negedge rstn) begin

        if(~rstn) begin
            key_reg0 <= 1'b1;
            key_reg1 <= 1'b1;
            key_reg2 <= 1'b1;
        end
        else begin
            key_reg0 <= key_in;
            key_reg1 <= key_reg0;
            key_reg2 <= key_reg1;
        end
    end        

    assign key_deb = (~key_reg0 & ~key_reg1 & ~key_reg2) | (~key_reg0 & ~key_reg1 & key_reg2);

endmodule

注意上述按键消抖模块的代码中,最后的assign语句使用的全是按位运算符,所以当需要对多个按键进行消抖时,只拓宽key_in和key_deb的位宽即可。
接下来我们将时钟分频模块、按键消抖模块、按键测试模块和数码管显示模块通过顶层文件“连接”在一起,进行上板实验。其顶层参考代码如下所示,注意消抖后的按键信号key_deb是高电平代表按键按下,低电平代表未按下,所以需要取反接入按键测试模块。


module key_filter_test(clk_50M,key_row,key_col,sw,seg_sel,seg_led);

    input clk_50M;

    input sw;

    input key_col;

    output key_row;

    output [3:0] seg_sel;    

    output [7:0] seg_led;    

    //行列键盘的第三行输出低电平

    assign key_row = 1'b0;        

    //50M/1K = 5K

    parameter DIVCLK_CNTMAX_1ms = 24999;

    wire clk_1ms;

    //例化时钟分频模块,得到周期为1ms的时钟

    clock_division #(

        .DIVCLK_CNTMAX(DIVCLK_CNTMAX_1ms)

    )

    my_clock_0(

        .clk_in(clk_50M),

        .divclk(clk_1ms)

    );               

    wire key_deb;

    key_filter key_filter(

        .clk(clk_50M),

        .rstn(1'b1),

        .key_in(key_col),

        .key_deb(key_deb)

    );

    wire key_in;    

wire[3:0] seg_reg;    

    //使用sw0选择消抖前后的信号进入按键测试模块

    assign key_in = sw? ~key_deb : key_col;

    key_test key_test(

        .key_in(key_in),

        .seg_reg(seg_reg)

    );

 //例化数码管显示模块 

  Digitron4bits mydigitron        

   (.clk_1K(clk_1ms),  

    .ina(seg_reg),   

  .inb(),   

  .inc(), 

  .ind(), 

  .dot(3'b000),

  .seg_sel(seg_sel),

  .seg_led(seg_led));  

endmodule

上板验证

下面还给出了该消抖测试的具体管脚约束。
按键消抖测试的管脚约束


set_pin_assignment { clk_50M } { LOCATION = R7; }

set_pin_assignment { sw } { LOCATION = A9; }

set_pin_assignment { seg_sel[0] } { LOCATION = C9; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_sel[1] } { LOCATION = B6; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_sel[2] } { LOCATION = A5; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_sel[3] } { LOCATION = A3; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[0] } { LOCATION = A4; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[1] } { LOCATION = A6; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[2] } { LOCATION = B8; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[3] } { LOCATION = E8; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[4] } { LOCATION = A7; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[5] } { LOCATION = B5; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[6] } { LOCATION = A8; IOSTANDARD = LVCMOS33; }

set_pin_assignment { seg_led[7] } { LOCATION = C8; IOSTANDARD = LVCMOS33; }

set_pin_assignment { key_row } { LOCATION = E10; IOSTANDARD = LVCMOS33; }

set_pin_assignment { key_col } { LOCATION = E11; IOSTANDARD = LVCMOS33; }

将sw0拨向上方,可以看到,经过按键消抖后,每次按下按键,数码管显示的16进制数都只增加了1,与预期的功能一致,也证明这种按键消抖的方法是可行的。

END

文章来源:https://www.yuque.com/yingmuketang/01/ix30k4

推荐内容

更多内容请关注走进FPGA专栏
推荐阅读
关注数
1615
内容数
27
本专栏将以【安路EG4S开发板】为例,从基础板卡信息及使用教程,基础实验设计与实现及综合性实验设计与实现带大家学习FPGA。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息