实现按键消抖&双击点亮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
仿真以及上板结果
上板符合预期,双击点亮,再次双击关闭。