FPGA系统性学习笔记连载_Day15【按键检测、按键消抖】 【原理及verilog仿真】篇

2021-04-07 11:47:50 浏览数 (1)

FPGA系统性学习笔记连载_Day15【按键检测、按键消抖】 【原理及verilog仿真】篇

本系列为FPGA系统性学习学员学习笔记整理分享,如有学习或者购买开发板意向,可加交流群联系群主。

连载《叁芯智能fpga设计与研发-第15天》 【按键检测、按键消抖】 【原理及verilog仿真】

原创作者:紫枫术河 转载请联系群主授权,否则追究责任

本篇文章,记录按键检测原理、按键消抖原理,及verilog代码实现及仿真。

一、按键检测

按键事件:分为按下事件、抬起事件,判断按键的状态是否改变,需要比较连续2个clk的电平是否发生改变,我用一张图,形象展示按键的检测原理。

1.1、从图中可以看出,我们在2个时钟的上升沿采集了2个电平值,A、B

1.2、如果 A=1,B=0,则为按下事件,A=0,B=1,则为抬起事件。

1.3、所以我们需要连续2次对按键信号进行采样,然后比较这2次的采样值是否发生改变,来确定是按下还是抬起事件;

1.4、用 assign key_down = A&(~B),检测按下,用assign key_up = (~A)&B,检测抬起。

1.5、上面的采集判断,相当于下面这个电路,他只对信号进行了一级缓存,如下图

1.6、上面的电路,检测到下降沿后,我们逻辑产生的脉冲宽度是随机的,最大一个clk,这个宽度是不能作为FPGA其他逻辑的输入信号,因为会导致亚稳态

1.7、为了解决上面的问题,我们采用2级缓存,如下电路

从这个图,可以看出我们2个寄存器,用这2个寄存器缓存的值来判断,就能避免脉宽变窄的问题

1.8、verilog代码实现

代码语言:javascript复制
module edge_check(
    input   signal,
    input       clk,
    input       rst_n,
     
    output  flag_up,
    output  flag_down
);
 
    reg [1:0] signal_temp;
     
    always@(posedge clk,negedge rst_n)
    begin
        if(!rst_n)
            signal_temp <= 2'b0;
        else
            begin
                signal_temp[0] <= signal;
                signal_temp[1]  <= signal_temp[0];
            end
    end
             
    assign flag_down  = signal_temp[1]&(~signal_temp[0]);
    assign flag_up  = (~signal_temp[1])&signal_temp[0];
             
endmodule

1.9、代码仿真

从图中看出,下降沿、上升沿检测正确

二、按键消抖

由于按键的机械按键,按下的过程会有抖动,这个抖动时间在5-20ms,我们取10ms进行判断

1.1、verilog代码,

edge_check.v key_fiter.v

edge_check为按下的边缘检测模块,用来启动key_fiter.v模块内的一个定时计数模块

1.2、edge_check.v

代码语言:javascript复制
module edge_check(
    input   signal,
    input       clk,
    input       rst_n,
     
    output  flag_up,
    output  flag_down
);
 
    reg [1:0] signal_temp;
     
    always@(posedge clk,negedge rst_n)
    begin
        if(!rst_n)
            signal_temp <= 2'b0;
        else
            begin
                signal_temp[0] <= signal;
                signal_temp[1]  <= signal_temp[0];
            end
    end
             
    assign flag_down  = signal_temp[1]&(~signal_temp[0]);
    assign flag_up  = (~signal_temp[1])&signal_temp[0];
             
endmodule

1.3、key_fiter.v

代码语言:javascript复制
module key_fiter(
    input       signal,
    input       clk,
    input   rst_n,
     
    output  reg flag_up,
    output  reg flag_down
);
 
    parameter   T_10ms = 500_000;
     
    reg [18:0]  cnt;
    wire              flag_up_r;
    wire              flag_down_r;
     
    edge_check edge_check_inst(
        .signal     (signal),
        .clk            (clk),
        .rst_n      (rst_n),
         
        .flag_up        (flag_up_r),
        .flag_down  (flag_down_r)
    );
 
    reg start_timer;
    reg clr_timer; 
     
    always@(posedge clk,negedge rst_n)
    begin
        if(!rst_n)
            begin
                cnt <= 0;
                flag_up <= 1'b0;
                flag_down <= 1'b0;  
            end
        else if(clr_timer)
            begin
                cnt <= 0;
                flag_up <= 1'b0;
                flag_down <= 1'b0;  
            end    
        else if(start_timer)
            begin
                if(signal == 1'b0)  //信号线0,10ms计数
                    begin
                        if(cnt >= T_10ms -1)
                            begin
                                cnt <= 1'b0;
                                flag_down <= 1'b1;
                            end
                        else
                            begin
                                cnt <= cnt   1'b1;
                                flag_down <= 1'b0;
                            end
                    end
                else                        //信号线1,10ms计数
                    begin
                        if(cnt >= T_10ms -1)
                            begin
                                cnt <= 1'b0;
                                flag_up <= 1'b1;
                            end
                        else
                            begin
                                cnt <= cnt   1'b1;
                                flag_up <= 1'b0;
                            end
                    end                
            end
        else
            begin
                cnt <= 0;
                flag_up <= 1'b0;
                flag_down <= 1'b0;  
            end
    end
     
    //判断启动定时器和清零计时器值
    always@(posedge clk,negedge rst_n)
    begin
        if(!rst_n)
            begin
                start_timer <=0;
                clr_timer <=0;
            end
        else if(flag_down_r || flag_up_r)
            begin
                start_timer <= 1'b1;
                clr_timer   <= 1'b1;
            end
        else   
            begin
                clr_timer   <= 1'b0;
                if(cnt == T_10ms -1)
                        start_timer <= 1'b0;
                else   
                    start_timer <= start_timer;
            end
    end
     
endmodule

1.4、仿真模块 key_fiter_tb.v

代码语言:javascript复制
`timescale 1ns/1ps

module key_fiter_tb;
 
        reg         signal;
        reg     clk;
        reg     rst_n;
         
        wire        flag_up;
        wire        flag_down;
 
        key_fiter key_fiter(
            .signal     (signal),
            .clk            (clk),
            .rst_n      (rst_n),
             
            .flag_up        (flag_up),
            .flag_down  (flag_down)
        );
 
        always #10 clk = ~clk;
         
        initial begin
            clk = 0;
            signal = 1;
            rst_n = 0;
            #20;
            rst_n = 1;
             
            //模拟按下
            signal = 1;
            //延时15ms
            #15000000; 
            repeat(50)
                begin
                    //抖动
                    signal = 0;
                    #10;
                    signal = 1;
                    #15;           
                    //抖动
                    signal = 0;
                    #17;
                    signal = 1;
                    #26;   
            end
            signal = 0;
             
            //延时15ms
            #15000000;
            //模拟抬起
            signal = 1;
            repeat(50)
                begin
                    //抖动
                    signal = 0;
                    #10;
                    signal = 1;
                    #15;           
                    //抖动
                    signal = 0;
                    #17;
                    signal = 1;
                    #26;   
            end
            signal = 1;
            #500;  
     
            //延时15ms
            #15000000;     
            $stop;
        end
     
 
endmodule

1.5、仿真结果

从图中看,按键按下和抬起,都有很长时间的抖动,但是按键的有效脉冲都是在10ms后才给出的。

【QQ交流群】

群号:173560979,进群暗语:FPGA技术江湖粉丝。

多年的FPGA企业开发经验,各种通俗易懂的学习资料以及学习方法,浓厚的交流学习氛围,QQ群目前已有1000多名志同道合的小伙伴,无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有。

【微信交流群】

现微信交流群已建立09群,人数已达数千人,欢迎关注“FPGA技术江湖”微信公众号,可获取进群方式。

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

江湖偌大,继续闯荡,愿大侠一切安好,有缘再见!

0 人点赞