基于状态机的按键消抖模块
本次案例是按着小梅哥的思路来写的,部分截图和文字来自其教学视频。
这次设计的是一个能把按键信号输入转换为一个按键信号下降沿和上升沿检测输出。
1、状态机的设定
- 空闲态:等待按键信号的下降沿,若出现则进入下一个状态。
- 按下滤波状态:进行20ms的计时,若在20ms的计时内出现的上升沿则表示按键还在抖动,回到空闲态:否则进入下一个状态,并生成按键按下信号。
- 等待释放状态:如果在该状态下出现上升沿信号进入释放滤波状态。
- 释放滤波状态:进行20ms的计时,若在20ms的计时内出现的下降沿则表示按键还在抖动,回到等待释放状态:否则进入空闲态,并生成按键释放信号。
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 [2:0] key_sync;
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
key_sync <= 0;
else
key_sync <= {key_sync[1:0],Key_in};//延迟3拍,减少亚稳态出现的概率的同时,还可以捕获上升沿和下降沿
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_sync[2:1] == 2'b10 )//下降沿捕获
key_nedge <= 1'b1;
else if(key_sync[2:1] == 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