FPGA学习verilog实现按键消抖&双击点亮LED

实现按键消抖&双击点亮LED

设计目标:实现双击点亮LED,要求每次点击都做按键消抖处理(包含前抖动和后抖动,20ms认为电平稳定;2秒内按下两次按键认为是双击)

时序图

时序

代码

key_fliter模块

module key_filter (
    input wire clk,
    input wire rst_n,
    input wire key_in,
    output reg key_flag
);

parameter CNT_MAX = 20'd999_999;
reg [19:0]cnt = 20'd0;


//计数器
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cnt <= 20'd0;
    end
    else if (key_in == 1'b1) begin
        cnt <= 20'd0;
    end
    else if (key_in == 1'b0 && cnt ==  CNT_MAX) begin
        cnt <= cnt;  //计数到999_999并且输入低电平时保持
    end
    else begin
        cnt <= cnt + 20'd1;
    end
end

/*
计数到999_998的原因是为了保证flag信号为一个脉冲信号,
如果计数到999999的话那么flag会是一个长长的电平信号。那不是我们想要的。
*/

//key_flag 判定
always @(posedge clk or negedge rst_n ) begin
    if(~rst_n)begin
        key_flag <= 1'b0;
    end
    else if (cnt == CNT_MAX - 20'd1) begin
        key_flag <= 1'b1;
    end
    else begin
        key_flag <= 1'b0;
    end
end


endmodule

led模块

 module led (
    input wire clk,
    input wire rst_n,
    input wire led_in,
    output reg led_out
);

// //单击控制LED灯的亮灭
// always @(posedge clk or negedge rst_n) begin
//     if (~rst_n) begin
//         led_out <= 1'b0;
//     end
//     else if(led_in == 1'd1)begin
//         led_out <= ~led_out;
//     end
//     else begin
//         led_out <= led_out;
//     end
// end



reg [31:0]cnt;
reg [2:0]cnt1;

//计数器20ms
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cnt <= 32'd0;
    end
    else if (led_in == 1'd1) begin
        cnt <= 32'd0;
    end
    else begin
        cnt <= cnt + 32'd1;
    end
end

//计数flag信号
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        cnt1 <= 3'd0;
    end
    else if (cnt1 == 3'd2) begin
        cnt1 <= 3'd0;
    end
    else if (led_in == 1'd1) begin
        cnt1 <= cnt1 + 3'd1;
    end
    else begin
        cnt1 <= cnt1;
    end
end


//双击控制LED灯的亮灭
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        led_out <= 1'b1;
    end
    else if(cnt <= 32'd99_999_999 && cnt1 == 3'd2)begin
        led_out <= ~led_out;
    end
    else begin
        led_out <= led_out;
    end
end

endmodule 

顶层模块

module fled (
    input wire clk,
    input wire rst_n,
    input wire key_in,
    output wire led_out
);

wire key_flag;

key_filter key_filter_1(
    .clk(clk),
    .rst_n(rst_n),
    .key_in(key_in),
    .key_flag(key_flag)
);

led led_1(
    .clk(clk),
    .rst_n(rst_n),
    .led_in(key_flag),
    .led_out(led_out)
);

endmodule

tb 文件

`timescale 1ns/1ps
module fled_tb ();

parameter CNT_1MS = 32'd50_000,
          CNT_11MS = 32'd500_000,
          CNT_41MS = 32'd2000_000,
          CNT_51MS = 32'd2500_000,
          CNT_60MS = 32'd3000_000;

reg clk;
reg rst_n;
reg key_in;
reg [21:0]tb_cnt;
wire led_out;

initial begin
    clk = 1'd0;
    rst_n = 1'd0;
    key_in = 1'd0;
    #2
    rst_n = 1'd1; 
end

always #10 clk = ~clk;

 //tb_cnt:按键过程计数器,通过该计数器的计数时间来模拟按键的抖动过程
always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0) begin
        tb_cnt <= 22'b0;
    end
        
    else if(tb_cnt == CNT_60MS)begin//计数器计数到 CNT_60MS 完成一次按键从按下到释放的整个过程
        tb_cnt <= 22'b0;
    end   
    else begin
        tb_cnt <= tb_cnt + 1'b1; 
    end
        
 
 //key_in:产生输入随机数,模拟按键的输入情况
 always@(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        key_in <= 1'b1; //按键未按下时的状态为为高电平
    else if((tb_cnt >= CNT_1MS && tb_cnt <= CNT_11MS)
            || (tb_cnt >= CNT_41MS && tb_cnt <= CNT_51MS))//在该计数区间内产生非负随机数 0、1 来模拟 10ms 的前抖动和 10ms 的后抖动
        key_in <= {$random} % 2; 
    else if(tb_cnt >= CNT_11MS && tb_cnt <= CNT_41MS)
        key_in <= 1'b0;
    else
        key_in <= 1'b1;




fled fled_1(
    .clk(clk),
    .rst_n(rst_n),
    .key_in(key_in),
    .led_out(led_out)
);


endmodule

仿真以及上板结果

仿真上板符合预期,双击点亮,再次双击关闭。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值