zoukankan      html  css  js  c++  java
  • 按键消抖

    摘要:

    本节将单 Bit 数据的异步信号同以及边沿检测方法引入到 FPG A中常用 的按键消抖设计,并在仿真测试吉利文件中引入随机数发生函数

    消抖的实现分为硬件实现和软件实现,

    产生的抖动次数以及间隔时间均是不可预期的,这就需要通过
    滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持
    续20ms 以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,也可
    以通过软件的方式完成。其中硬件电路消除抖动适用于按键数目较少的场合。

    下面先来讲硬件实现:

    使用基于与非门的RS 触发器来消除抖动的电路图,如图 3.7-4 所示。假设
    初始状态开关与B 接通,此时S=1,R=0,触发器置0 即Q=0;当B 切换到A 的
    过程中,存在开关既没有与A 也没有与B 接触,此时S=1,R=1,触发器保持即
    Q=0;当切换到A 瞬间时有R=1,S=0,触发器置1 即Q=1。此时即使当A 出现抖
    动即S=1,触发器的状态仍能保持当前状态即Q=1 不变。整个过程波形图如图所
    示Q。同理,可以分析出开关由A 切换到B 时触发器的状态变化。

     请注意:这只适合于单刀双掷开关。

    而对于两脚和四脚的按键目前常用的是RC电路和555定时器组成的单稳态触发器。

    接下来是软件实现:

     在按键没有按下时,处于高电平状态。

    代码实现:

    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 key_state;//按键状态
        //因为按键是异步信号,先同步到系统时钟域
        reg key_in_r,key_in_rr;
        always@(posedge clk or negedge rst_n)
            if(!rst_n)begin    
                key_in_r<=0;
                key_in_rr<=0;
            end
            else begin
                key_in_r<=key_in;
                key_in_rr<=key_in_r;
            end
        //按键检测用到上升沿和下降沿检测
        reg key_in_rr_1,key_in_rr_11;
        wire pedge,nedge;
        always@(posedge clk or negedge rst_n)
            if(!rst_n)begin
                key_in_rr_1<=0;
                key_in_rr_11<=0;
            end
            else begin
                key_in_rr_1<=key_in_rr;
                key_in_rr_11<=key_in_rr_1;
            end
        assign pedge=(!key_in_rr_11) && key_in_rr_1;
        assign nedge=key_in_rr_11 && (!key_in_rr_1);
        //抖动计数包含使能模块和计数模块(按20ms来滤除,需要计数到1000000-1)
        reg [19:0]cnt;
        reg cnt_full;
        reg en_cnt;
        always@(posedge clk or negedge rst_n)
            if(!rst_n)
                cnt<=20'd0;
            else if(en_cnt)
                cnt<=cnt+1'b1;
            else
                cnt<=20'd0;
        always@(posedge clk or negedge rst_n)
            if(!rst_n)
                cnt_full<=1'b0;
            else if(cnt==20'd999_999)
                cnt_full<=1'b1;
            else
                cnt_full<=1'b0;
        //状态机
        localparam IDLE=4'b0001,FILTER0=4'b0010,DOWN=4'b0100,FILTER1=4'b1000;
        reg [3:0]state;
        always@(posedge clk or negedge rst_n)
            if(!rst_n)begin
                en_cnt<=1'b0;
                state<=IDLE;
                key_flag<=1'b0;
                key_state<=1'b1;
            end
            else begin
                case(state)
                    IDLE:begin
                        key_flag=1'b0;
                        if(nedge)begin
                            en_cnt<=1'b1;
                            state<=FILTER0;
                        end
                        else
                            state<=IDLE;
                    end
                    FILTER0:begin
                        if(cnt_full)begin
                            en_cnt<=1'b0;
                            state<=DOWN;
                            key_flag<=1'b1;
                            key_state<=1'b0;
                        end
                        else if(pedge)begin
                            state<=IDLE;
                            en_cnt<=1'b0;
                        end
                        else
                            state<=FILTER0;
                    end
                    DOWN:begin
                        key_flag<=1'b0;
                        if(pedge)begin
                            en_cnt<=1'b1;
                            state<=FILTER1;
                        end
                        else
                            state<=DOWN;
                    end
                    FILTER1:begin
                        if(cnt_full)begin
                            en_cnt<=1'b0;
                            state<=IDLE;
                            key_flag<=1'b1;
                            key_state<=1'b1;
                        end
                        else if(nedge)
                            state<=DOWN;
                        else
                            state<=FILTER1;
                    end
                    default:state<=IDLE;
                endcase
            end
            
        
    endmodule 
    View Code

    tb:

    `timescale 1ns/1ns
    module key_filter_tb;
        reg clk;
        reg rst_n;
        reg key_in;
        wire key_flag;//按键状态切换标志
        wire key_state;//按键状态
        key_filter key_filter(
                    .clk(clk),
                    .rst_n(rst_n),
                    .key_in(key_in),
                    .key_flag(key_flag),
                    .key_state(key_state)
                    );
        initial clk=0;
        always #10 clk=~clk;
        
        initial begin
            rst_n=0;
            #21;
            rst_n=1;
            //模拟抖动在20ms以内
            key_in=0;#1000;
            key_in=1;#2000;
            key_in=0;#1400;
            key_in=1;#2600;
            key_in=0;#1300;
            key_in=1;#200;
            //产生一个低电平大于20ms,代表稳定按下
            key_in=0;#20_000_100;
            #30_000_000;
            //模拟释放抖动20ms以内
            key_in=1;#1000;
            key_in=0;#2000;
            key_in=1;#1400;
            key_in=0;#2600;
            key_in=1;#1300;
            key_in=0;#200;
            //产生一个高电平大于20ms,代表稳定释放
            key_in=1;#20_000_100;
            #30_000_000;
            $stop;
        end
    endmodule
    View Code

    tb_task:

    `timescale 1ns/1ns
    module key_filter_tb_task;
        reg clk;
        reg rst_n;
        reg key_in;
        wire key_flag;//按键状态切换标志
        wire key_state;//按键状态
        key_filter key_filter(
                    .clk(clk),
                    .rst_n(rst_n),
                    .key_in(key_in),
                    .key_flag(key_flag),
                    .key_state(key_state)
                    );
        integer myrand;
        initial clk=0;
        always #10 clk=~clk;
        task press_key;
            begin
                repeat(50)begin
                    myrand={$random}%65536;
                    #myrand key_in=~key_in;
                end
                key_in=0;
                #50_000_000;
                repeat(50)begin
                    myrand={$random}%65536;
                    #myrand key_in=~key_in;
                end
                key_in=1;
                #50_000_000;
            end
        endtask
        initial begin
            rst_n=0;
            key_in=1;
            #21;
            rst_n=1;
            press_key;#10000;
            press_key;#10000;
            press_key;#10000;
            $stop;
        end
    endmodule
    View Code

    波形图:

     抖动放大:

    YKJIAO
  • 相关阅读:
    数据库期末考试复习
    函数 初识
    文件操作
    深浅copy 和 集合
    数据编码补充
    字典的增删改查和嵌套
    面试题 和 python 2与3的期区别
    英文练习
    初识数据类型
    测试基础-系统测试(2)
  • 原文地址:https://www.cnblogs.com/ajiaoa/p/12780559.html
Copyright © 2011-2022 走看看