六、状态机实现多个按键消抖

1、fsm_key_filter_more.v

//原来代码有bug,现在改了

module fsm_key_filter_more #(parameter KEY_W = 4 ,TIME_20MS = 20'd999_999)(
    input   wire                    clk   ,
    input   wire                    rst_n ,
    input   wire [KEY_W-1:0]        key   ,

    output  reg  [KEY_W-1:0]        key_flag
);

//定义状态变量独热码编码
localparam IDLE         = 4'b0001,//表示空闲状态
           FILETER_DOWN = 4'b0010,//按键按下前抖动状态
           HOLD_DOWN    = 4'b0100,//按键稳定时的状态
           FILETER_UP   = 4'b1000;//按键松手后,后抖动状态

//定义状态
reg [3:0] state_c ;
reg [3:0] state_n ;

//定义状态转移条件
wire [3:0]  idle_2_F_D;
wire [3:0]  F_D_2_H_D ;
wire [3:0]  F_D_2_idle;
wire [3:0]  H_D_2_F_U ;
wire [3:0]  F_U_2_idle;
wire [3:0]  F_U_2_H_D ;

//计数器模块定义
reg [19:0] cnt_20ms ;
wire       add_cnt  ;
wire       end_cnt  ;

//寄存按键信号同步打拍,检测下降沿与上升沿
reg  [KEY_W-1:0]      key_r0   ;//同步信号寄存
reg  [KEY_W-1:0]      key_r1   ;//打拍信号
wire [KEY_W-1:0]      nedge    ;//下降沿信号
wire [KEY_W-1:0]      pedge    ;//上升沿信号


integer               i        ;//循环变量

//状态机设计三段式时序逻辑描述状态转移
//第一段
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state_c <= IDLE ;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段,组合逻辑,更据状态转移图描述状态转移条件
always@(*)begin
    case(state_c)
        IDLE : begin
            if(idle_2_F_D)begin//条件成立状态转移
                state_n = FILETER_DOWN;
            end
            else begin
                state_n = IDLE ;
            end
        end
        FILETER_DOWN :begin
            if(F_D_2_idle)begin
                state_n = IDLE;
            end
            else if(F_D_2_H_D)begin
                state_n = HOLD_DOWN;
            end
            else begin
                state_n = FILETER_DOWN;
            end
        end
        HOLD_DOWN : begin
            if(H_D_2_F_U)begin
                state_n = FILETER_UP ;
            end
            else begin
                state_n = HOLD_DOWN ;
            end
        end
        FILETER_UP : begin
             if(F_U_2_H_D)begin
                state_n = HOLD_DOWN ;
            end
            else if(F_U_2_idle)begin
                state_n = IDLE ;
            end
            else begin
                state_n = FILETER_UP ;
            end
        end
        default : state_n = IDLE ;
    endcase
end

assign idle_2_F_D = (state_c == IDLE        ) && nedge  ;
assign F_D_2_idle = (state_c == FILETER_DOWN) && pedge  ;
assign F_D_2_H_D  = (state_c == FILETER_DOWN) && end_cnt;
assign H_D_2_F_U  = (state_c == HOLD_DOWN   ) && pedge  ;
assign F_U_2_idle = (state_c == FILETER_UP  ) && end_cnt;
assign F_U_2_H_D  = (state_c == FILETER_UP  ) && nedge  ;   

//第三段,描述输出
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
        // for(i=0; i < KEY_W; i = i+1)begin //通过for循环赋初值
        //     key_flag[i] <= 1'b0;  
        // end  
        key_flag <= {KEY_W{1'b0}};
   end 
   else if(F_D_2_H_D)begin
     key_flag <= ~key_r1;//key_r1更稳定一点,打拍后的信号,当然key也可以
   end
   else begin
        // for(i=0; i < KEY_W; i = i+1)begin 
        //     key_flag[i] <= 1'b0;  
        // end
        key_flag <= {KEY_W{1'b0}};
   end
end

//20ms计数器模块
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 20'd0;
    end
    else if(add_cnt)begin
        if(end_cnt)begin
            cnt_20ms <= 20'd0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'd1;
        end
    end
    else begin
        cnt_20ms <= 20'd0;/*888888888888*/
    end
end

assign add_cnt = (state_c == FILETER_DOWN) || (state_c == FILETER_UP);
assign end_cnt = (add_cnt && cnt_20ms == TIME_20MS ); //|| F_D_2_idle || F_U_2_H_D;
//end_cnt ==end_cnt = (add_cnt && cnt_20ms == TIME_20MS )|| F_D_2_idle || F_U_2_H_D;会产生bug,
//从0010-->0001时会使end_cnt满足条件,这样会使得F_D_2_H_D满足从而给输出标志信号置位,事实上此时还处于抖动状态

//延时打拍按键信号
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin //也可以key_r0 <= {KEY_W{1'b1}};
        // for(i = 0; i < KEY_W; i = i + 1)begin
        //     key_r0[i] <= 1'b1;
        //     key_r1[i] <= 1'b1;
        // end
        key_r0 <= {KEY_W{1'b1}};
        key_r1 <= {KEY_W{1'b1}};
    end
    else begin
        key_r0 <= key ;//同步
        key_r1 <= key_r0;//打拍
    end
end

//nedge pedge
assign nedge = key_r1 & (~key_r0);//检测下降沿
assign pedge = (~key_r1) & key_r0;//检测上升沿

endmodule

2、fsm_key_filter_more_tb.v

`timescale 1ns/1ns
module fsm_key_filter_more_tb();

parameter KEY_W = 4;
reg                 clk   ;
reg                 rst_n ;
reg   [KEY_W-1:0]   key   ;

wire  [KEY_W-1:0]   key_flag;

parameter CYCLE = 20;
defparam u_fsm_key_filter_more.TIME_20MS = 100;

//模拟复位信号与时钟信号初始化
initial begin
    clk    = 1'b1;
    rst_n <= 1'b0;
    #15
    rst_n <= 1'b1;
end

//模拟时钟信号
always #(CYCLE/2) clk = ~clk;

//模拟按键按下抖动过程
initial begin
    key = 4'b1111;//按键赋初值
    #(1000*CYCLE)
//按下时的抖动
//第一个按键按下
    key[0] = 1'b0;
    #(295)
    key[0] = 1'b1;
    #(330)
    key[0] = 1'b0;
    #(166)
    key[0] = 1'b1;
    #(111)
    key[0] = 1'b0;
    #(1000*CYCLE)//稳定状态

//按键松手时的后抖动
    key[0] = 1'b1;
    #(10*CYCLE)
    key[0] = 1'b0;
    #(8*CYCLE)
    key[0] = 1'b1;
    #(9*CYCLE)
    key[0] = 1'b0;
    #(15*CYCLE)
    key[0] = 1'b1;
    #(1000*CYCLE)
    
//第二个按键按下

    key[1] = 1'b0;
    #(15*CYCLE)
    key[1] = 1'b1;
    #(20*CYCLE)
    key[1] = 1'b0;
    #(9*CYCLE)
    key[1] = 1'b1;
    #(6*CYCLE)
    key[1] = 1'b0;
    #(1000*CYCLE)//稳定状态

//按键松手时的后抖动
    key[1] = 1'b1;
    #(10*CYCLE)
    key[1] = 1'b0;
    #(8*CYCLE)
    key[1] = 1'b1;
    #(9*CYCLE)
    key[1] = 1'b0;
    #(15*CYCLE)
    key[1] = 1'b1;
    #(1000*CYCLE)
    
//第三个按键按下
    key[2] = 1'b0;
    #(15*CYCLE)
    key[2] = 1'b1;
    #(20*CYCLE)
    key[2] = 1'b0;
    #(9*CYCLE)
    key[2] = 1'b1;
    #(6*CYCLE)
    key[2] = 1'b0;
    #(1000*CYCLE)//稳定状态

//按键松手时的后抖动
    key[2] = 1'b1;
    #(10*CYCLE)
    key[2] = 1'b0;
    #(8*CYCLE)
    key[2] = 1'b1;
    #(9*CYCLE)
    key[2] = 1'b0;
    #(15*CYCLE)
    key[2] = 1'b1;
    #(1000*CYCLE)
    
//第四个按键按下
    key[3] = 1'b0;
    #(15*CYCLE)
    key[3] = 1'b1;
    #(20*CYCLE)
    key[3] = 1'b0;
    #(9*CYCLE)
    key[3] = 1'b1;
    #(6*CYCLE)
    key[3] = 1'b0;
    #(1000*CYCLE)//稳定状态

//按键松手时的后抖动
    key[3] = 1'b1;
    #(10*CYCLE)
    key[3] = 1'b0;
    #(8*CYCLE)
    key[3] = 1'b1;
    #(9*CYCLE)
    key[3] = 1'b0;
    #(15*CYCLE)
    key[3] = 1'b1;
    #(1000*CYCLE)
    $stop;
end

fsm_key_filter_more u_fsm_key_filter_more(
    . clk      (clk),
    . rst_n    (rst_n),
    . key      (key),
 
    . key_flag (key_flag)
);


endmodule

3、do.do

vlib work
vmap work work
                                            
#编译testbench文件					       	
vlog    fsm_key_fliter_more_tb.v
#编译 	设计文件					       	 
vlog ../src/fsm_key_filter_more.v

                                                
#指定仿真顶层 							     
vsim -novopt work.fsm_key_filter_more_tb 
#添加信号到波形窗 							  
add wave -position insertpoint sim:/fsm_key_filter_more_tb//*

run 20000000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值