本次案例是按着小梅哥的思路来写的,部分截图和文字来自其教学视频。
1、状态机的设定
2、模块代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:Lclone
//
// Create Date: 2023/01/14 20:44:54
// Design Name:
// Module Name: key_filter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module key_filter(
input Clk, //时钟信号
input Rst_n, //复位信号
input Key_in, //按键输入信号
output reg Key_press, //按键按下信号
output reg Key_release //按键释放信号
);
reg [1:0] key_reg;
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
key_reg <= 0;
else
key_reg <= {key_reg[0],Key_in};//打拍子,为了捕获上升沿和下降沿
end
reg key_nedge;
reg key_pedge;
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
begin
key_nedge <= 0;
key_pedge <= 0;
end
else if(key_reg == 2'b10 )//下降沿捕获
key_nedge <= 1'b1;
else if(key_reg == 2'b01 )//上降沿捕获
key_pedge <= 1'b1;
else
begin
key_nedge <= 0;
key_pedge <= 0;
end
end
parameter CNT_20MS = 1_000_000;
reg [19:0] cnt_20ms;
reg [ 1:0] state;
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
begin
state <= 0;
cnt_20ms <= 0;
Key_press <= 0;
Key_release <= 0;
end
else case(state)
0:
begin
Key_release <= 0;
if(key_nedge == 1)
state <= 1;
else
state <= 0;
end
1:
begin
if(cnt_20ms < CNT_20MS & key_pedge == 1)
begin
state <= 0;
cnt_20ms <= 0;
end
else if(cnt_20ms == CNT_20MS - 1)
begin
state <= 2;
cnt_20ms <= 0;
Key_press <= 1;
end
else
cnt_20ms <= cnt_20ms + 1'b1;
end
2:
begin
Key_press <= 0;
if(key_pedge == 1)
state <= 3;
else
state <= 2;
end
3:
begin
if(cnt_20ms < CNT_20MS & key_nedge == 1)
begin
state <= 2;
cnt_20ms <= 0;
end
else if(cnt_20ms == CNT_20MS - 1)
begin
state <= 0;
cnt_20ms <= 0;
Key_release <= 1;
end
else
cnt_20ms <= cnt_20ms + 1'b1;
end
default:;
endcase
end
endmodule
3、仿真
(1)$random函数的使用
rand = {$random(seed)} % 10_000_000;
- 表示生成0-9_999_999范围内的随机数并赋值给rand;
- 1个seed数对应着1个的随机数,可以通过设定seed的值来复现该随机数;
- 可以去掉花括号后使生成的范围变成(-9_999_999) - 9_999_999;
(2)仿真代码
`timescale 1ns / 1ps
module key_filter_tb();
reg clk_50m;
initial clk_50m <= 1;
always #10 clk_50m <= ~clk_50m;
reg rst_n;
initial begin
rst_n <= 0;
#200
rst_n <= 1;
end
reg key_in;
wire key_press;
wire key_release;
key_filter key_filter_inst(
.Clk (clk_50m),
.Rst_n (rst_n),
.Key_in (key_in),
.Key_press (key_press),
.Key_release (key_release)
);
initial begin
key_in <= 1;
#400
press_key(1);
#20
press_key(2);
#20
press_key(3);
end
reg [31:0]rand;
task press_key;
input [3:0]seed;
begin
key_in = 1;
#20_000_000;
repeat(5)begin
rand = {$random(seed)} % 10_000_000;
#rand key_in = ~key_in;
end
key_in = 0;
#40_000_000;
repeat(5)begin
rand = {$random(seed)} % 10_000_000;
#rand key_in = ~key_in;
end
key_in = 1;
#40_000_000;
end
endtask
endmodule
(3)仿真结果
第一次按下
第二次按下
第三次按下
实验结果:每次按下都能准确发出Key_press和Key_release信号,实验初步验证成功。
4、参考文献
[1]【零基础轻松学习FPGA】小梅哥Xilinx FPGA基础入门到项目应用培训教程】 https://www.bilibili.com/video/BV1va411c7Dz/?p=20&share_source=copy_web&vd_source=c6135c3b3a9878c08e2ddc91acdf6853&t=0