4.EP4CE10F17的按键设计

前记:师夷长技以自强


1.基本概念

轻触按键:开发板上经常用到的一种按键,内置一个反作用弹簧,理想情况下是产生一个低电平的脉冲信号。但由于机械构件接触的时候肯定会产生抖动,一般情况下抖动的总时间会持续20ms。

状态机:有一个状态变量有有限中取值情况,每一种取值对应硬件电路的所处的一种状态执行相应的程序的程序。设计状态机的关键是合理分状态,并明确状态的跳转条件。

2.基本原理

2.1状态机设计

把抖动的过程考虑在内的话,可以把按键模块分为四个状态:

IDLE-未按下时空闲状态

FILTER0-按下抖动滤除状态

DOWN-按下稳定状态

FILTER1-释放抖动滤除状态

其中的IDLE和DOWN两个状态都是只有一个出度的,而FILTER0和FILTER1只有一个出度。对应的状态转换条件如下

转移编号转移条件
1Nedge(检测到下降沿)
2Cnt_full(检测到下降沿启动计数,若计数值满时仍为低)
3Pedge(检测到上升沿)
4Pedge(在按下消抖过程中如果检测到上升沿)
5Nedge(在松开消抖过程中如果检测到下降沿)
6Cnt_full(检测到上升沿启动计数,若计数值满时仍为高)

2.2 单bit异步信号同步设计

按键的输入信号key_in对于FPGA内部信号来说是一个异步信号,若直接使用容易出现时序违例导致亚稳态。这里采用触发器对信号打两拍的方式对系统时钟进行同步。

2.3 边缘检测

利用寄存器在时钟信号的控制下,输入状态即为下一时刻输出状态这一特性来判断。

2.4 测试思路

借助系统函数$random产生随机长度的电平信号,重复一定次数即可模拟按键的抖动过程。需要注意的一点是应该把抖动电平脉冲时间控制在20ms内,所以应该是25'd20_000_000,为了节省仿真时间可以只产生16位的随机数也就是0~65535.

3. 模块设计

3.1 模块接口框图

Key_flag:当按键状态变化时产生一个高电平

Key_state:1-常态,0-按下

3.2 代码设计

需要强调的是,对状态比较完美的编码是每种状态的编码值只有一个1。

module key_filter(
    input Clk,
    input Rst_n,
    input Key_in,
    output reg key_flag,
    output reg key_state
);
    reg [1:0]r_Key_in;
    wire Neg_edge;
    wire Pos_edge;
    reg [3:0]state;
    reg [19:0]Cnt_Clk;
    reg en_Cnt;
    always@(posedge Clk,negedge Rst_n)
    if(!Rst_n)
        Cnt_Clk <= 0;
    else if(en_Cnt)
        if(Cnt_Clk == 20'd999_999)
            Cnt_Clk <= 0;
        else
            Cnt_Clk <= Cnt_Clk + 20'b1;
    else
        Cnt_Clk <= 0;
        
    always@(posedge Clk,negedge Rst_n)
    if(!Rst_n)
        r_Key_in <= 0;
    else
        r_Key_in <= {r_Key_in[0],Key_in};
        
    assign Neg_edge = r_Key_in[1] & ~r_Key_in[0];
    assign Pos_edge = ~r_Key_in[1] & r_Key_in[0];
    
    localparam
        IDLE =    4'b0001,
        FILTER0 = 4'b0010,
        DOWN =    4'b0100,
        FILTER1 = 4'b1000;
        
    always @ (posedge Clk,negedge Rst_n)
    if(!Rst_n)begin
        state <= IDLE;
        en_Cnt <= 0;
        key_flag <= 0;
        key_state <= 1;
    end
    else begin
        case(state)
            IDLE:
                begin
                    key_flag <= 0;
                    if(Neg_edge)begin
                        en_Cnt <= 1;
                        state <= FILTER0;
                    end
                end
            FILTER0:
                begin
                    if(Pos_edge)begin
                        en_Cnt <= 0;
                        state <= IDLE;
                    end
                    else if(Cnt_Clk == 20'd999_999)begin
                        en_Cnt <= 0;
                        state <= DOWN;
                        key_flag <= 1;
                        key_state <= 0;
                    end
                end
            DOWN:
                begin
                    key_flag <= 0;
                    if(Pos_edge)begin
                        en_Cnt <= 1;
                        state <= FILTER1;
                    end
                end
            FILTER1:
                begin
                    if(Neg_edge)begin
                        en_Cnt <= 0;
                        state <= DOWN;
                    end
                    else if(Cnt_Clk == 20'd999_999)begin
                        en_Cnt <= 0;
                        state <= IDLE;
                        key_flag <= 1;
                        key_state <= 1;
                    end
                end
        endcase
    end
    
endmodule
 

3.3 仿真

为了模拟按键的实际电平变化过程,特意写了一个名为press_key的task,里面调用系统函数random产生产生随机数。

`timescale 1ns/1ns
module key_filter_tb();
    reg Clk;
    reg Rst_n;
    reg Key_in;
    wire key_flag;
    wire key_state;
    key_filter key_filter1(
        .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;
    
    reg [15:0]myrand;
    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'b1;
        #100
        Rst_n = 1'b1;
        #30000
        press_key;
        #10000
        press_key;
        #10000
        press_key;
        #10000
        $stop;
    end
endmodule

3.4 仿真结果

在后续的FPGA设计总,key_filter都可作为一个用户输入模块来使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值