FPGA学习11按键消抖实验

    科技2022-08-17  113

    FPGA学习 11 按键消抖实验

    key_filter.v文件

    //定义按键函数端口 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 ; //定义状态机 localparam IDEL = 4'b0001 , FILTER0 = 4'b0010 , DOWN = 4'b0100 , FILTER1 = 4'b1000 ; //定义使用到的寄存器 reg [3:0]state ; reg key_tmpa , key_tmpb ; wire pedge , nedge ; reg en_cnt_20ms ; reg [19:0]cnt_20ms ; reg cnt_20ms_full ; //对外部输入的异步信号进行同步处理 reg key_in_sa,key_in_sb; always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin key_in_sa <= 1'b0; key_in_sb <= 1'b0; end else begin key_in_sa <= key_in; key_in_sb <= key_in_sa; end //获取上一个周期的 按键电平 和当前 按键电平 //目的: 获取 按键状态判断是否是下降沿还是上升沿到来 always@(posedge Clk or negedge Rst_n) if(Rst_n == 1'b0) begin key_tmpa <= 1'b0 ; key_tmpb <= 1'b0 ; end else begin //每次时钟上升沿到来,两个寄存器读取上一次的数据的值 key_tmpa <= key_in_sb; key_tmpb <= key_tmpa; end // 下降沿和上升沿到来标志位信号 assign nedge = ((~key_tmpa ) & ( key_tmpb)) ; //判断下降沿是否到来? //分析:: 下降沿到来的清况是高电平-->低电平的变换过程,也就是说: // key_tmp1 存的前1个时刻的电平, // key_tmp0 存的是当前时刻的电平, // 要满足该条件,也就是说, key_tmp0 <= 0 且 key_tmp1 <= 1; 此时得到 nedge = 1 ; assign pedge = key_tmpa & (!key_tmpb) ; //判断上升沿是否到来? //状态机判断 always@(posedge Clk or negedge Rst_n) if(!Rst_n) //复位状态下,状态机处于空闲状态 begin state <= IDEL ; //复位状态下,状态机处于空闲模式下 en_cnt_20ms <= 1'b0 ; //消抖计数器关闭 key_flag <= 1'b0 ; //按键成功标志位清零 key_state <= 1'b1 ; //默认输入按键状态高电平 end else begin case(state) IDEL : begin key_flag <= 1'b0 ; //空闲状态下,按键成功标志位永远为0 if(nedge == 1'b1) begin //按键检测到下降沿到来 state <= FILTER0 ;//进入下一个状态 en_cnt_20ms <= 1 ;//开启使能计数20ms end else state <= IDEL ; //未检测到下降沿到来,信号状态保持不变 end FILTER0: if(cnt_20ms_full == 1'b1) begin //看消除抖动20ms后,且20ms内没有检测到上升沿到来 key_flag <= 1'b1; //表示按键 已经成功按下 key_state<= 1'b0; //此时输入按键按下状态为低电平 state <= DOWN ; //进入下一状态 en_cnt_20ms <= 0 ; //20ms计数器关闭 end else if(pedge == 1'b1) begin //没有满20ms 的时候,同时判断是否有上升沿的到来,表明此时是抖动信号,非按键按下信号 state <= IDEL ; en_cnt_20ms <= 0 ;//计数器关闭 end else //没有到 20ms消抖时间,且没有检测到上升沿到来,状态机保持不变 state <= FILTER0 ; DOWN: begin key_flag <= 1'b0; //检测到有按键按下,发送一个脉冲信号,有上一个状态的高电平变为低电平 if(pedge == 1'b1) begin //上一个状态时间超过20ms,此时需要一直等待上升沿的到来(做为按键松开的标志) state <= FILTER1 ; //进入到下一状态 en_cnt_20ms <= 1'b1 ;//开启下一次20ms 延时计数 end else //没有,则保持该状态 state <= DOWN ; end FILTER1: if(cnt_20ms_full == 1'b1) begin//表示 20ms计数又一次到来,此时按键稳定 key_flag <= 1'b1; //表示上升沿的信号稳定的信号,随后到 IDEL又会设置为低电平,产生一个脉冲信号 key_state<= 1'b1; //此时输入按键按下状态为低电平 en_cnt_20ms <= 1'b0 ; //关闭20ms定时器 state <=IDEL ; end else if(nedge)begin // 20ms内又出现了下降沿,表示这是一次抖动 en_cnt_20ms <= 1'b0 ; state <= DOWN ; end else //20ms内状态保持不变 state <=FILTER1 ; default: begin state <= IDEL ; key_flag <= 1'b0; key_state<= 1'b1; //默认状态时 1 高电平 en_cnt_20ms <= 1'b0 ; //默认状态时 0 低电平 end endcase end // 定时器启动 always块 always@(posedge Clk or negedge Rst_n) if(!Rst_n) cnt_20ms <= 20'd0; else if(en_cnt_20ms) cnt_20ms <= cnt_20ms + 1'b1; else // 关闭时,计时器清零 cnt_20ms <= 20'd0; //定时器20ms always块 50Mhz 20ms = cnt_20ms : 1_000_000 always@(posedge Clk or negedge Rst_n) if(Rst_n == 1'b0) cnt_20ms_full <= 0 ; else if(cnt_20ms == 20'd999_999) cnt_20ms_full <= 1'b1 ; //计数值计满标志位 else cnt_20ms_full <= 1'b0 ; // 未挤满,输出0 endmodule

    key_filter_tb .v (Ver.01)文件

    `timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg Clk ; reg Rst_n ; reg key_in ; wire key_flag ; wire key_state ; key_filter key_filter0( .Clk(Clk) , .Rst_n(Rst_n) , .key_in(key_in) , .key_flag(key_flag) , //检测按键成功信号 .key_state(key_state) //实时的信号 ); initial Clk = 1 ; // always#(`clk_period*10) Clk = ~Clk ; always#(`clk_period/2) Clk = ~Clk ; initial begin Rst_n = 1'b0 ; key_in = 1'b1 ; #(`clk_period * 10); Rst_n = 1'b1 ; #(`clk_period * 10 +1); key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #(`clk_period * 10000_000); //按下稳定 key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #(`clk_period * 10000_000); //按下稳定 key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #(`clk_period * 10000_000); //按下稳定 key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #(`clk_period * 10000_000); //按下稳定 $stop ; // end endmodule

    key_filter_tb .v (Ver.02)文件

    `timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg Clk ; reg Rst_n ; reg key_in ; wire key_flag ; wire key_state ; key_filter key_filter0( .Clk(Clk) , .Rst_n(Rst_n) , .key_in(key_in) , .key_flag(key_flag) , //检测按键成功信号 .key_state(key_state) //实时的信号 ); initial Clk = 1 ; // always#(`clk_period*10) Clk = ~Clk ; always#(`clk_period/2) Clk = ~Clk ; /* initial begin Rst_n = 1'b0 ; key_in = 1'b1 ; #(`clk_period * 10); Rst_n = 1'b1 ; #(`clk_period * 10 +1); key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #(`clk_period * 10000_000); //按下稳定 key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #(`clk_period * 10000_000); //按下稳定 key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #(`clk_period * 10000_000); //按下稳定 key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #1100; key_in = 0 ; #1200; key_in = 1 ; #(`clk_period * 10000_000); //按下稳定 $stop ; // end */ initial begin Rst_n = 1'b0 ; key_in = 1'b1 ; #(`clk_period * 10); Rst_n = 1'b1 ; #(`clk_period * 10 +1); press_key; #10000; press_key; #10000; $stop ; end reg [16:0]myrand ; task press_key; begin repeat(50)begin //产生的 0-65535 的随机数 myrand = {$random}%65536 ; // random 是系统的一个32位的随机数,取余得到余数 #myrand key_in = ~key_in ; end key_in = 0 ; #5000_0000; repeat(50)begin //产生的 0-65535 的随机数 myrand = {$random}%65536 ; // random 是系统的一个32位的随机数,取余得到余数 #myrand key_in = ~key_in ; end key_in = 1 ; #5000_0000; end endtask endmodule

    key_filter_tb .v (Ver.03)文件 (配合 key_model.v文件使用)

    `timescale 1ns/1ns `define clk_period 20 module key_filter_tb; reg Clk; reg Rst_n; wire key_in; wire key_flag; wire key_state; key_filter key_filter0( .Clk(Clk), .Rst_n(Rst_n), .key_in(key_in), .key_flag(key_flag), .key_state(key_state) ); key_model key_model0(.key(key_in)); initial Clk= 1; always#(`clk_period/2) Clk = ~Clk; initial begin Rst_n = 1'b0; #(`clk_period*10) Rst_n = 1'b1; #(`clk_period*10 + 1); end endmodule

    key_model.v 文件

    `timescale 1ns/1ns module key_model(key); //input press; output reg key; reg [15:0]myrand; initial begin key = 1'b1; press_key; #10000; press_key; #10000; press_key; #10000; $stop; end task press_key; begin repeat(50)begin myrand = {$random}%65536;//0~65535; #myrand key = ~key; end key = 0; #25000000; repeat(50)begin myrand = {$random}%65536;//0~65535; #myrand key = ~key; end key = 1; #25000000; end endtask endmodule
    Processed: 0.031, SQL: 9