1、设计内容
按键消抖的原理:
通常我们所使用的开关为机械弹性开关,当我们按下或松开按键时,由于弹片的物理特性,不能立即闭合或断开,往往会在断开或闭合的短时间内产生机械抖动,消除这种抖动的过程即称为按键消抖。按键消抖可分为硬件消抖和软件消抖。硬件消抖主要使用RS触发器或电容等方法实现消抖,一般在按键较少时使用。软件消抖的原理主要为按键按下或松开后延时5ms—20ms采样,也可以在检测到按键状态稳定后采样,即避开抖动区域后再采样。
对于按键消抖电路,未按下时,FPGA检测到高电平;按下时,FPGA检测到低电平。
共分为4个状态,分别是:
①未按下时空闲态——IDEL
②按下抖动滤波状态——FILTER0
③按下稳定状态——DOWN
④释放抖动滤波状态——FILTER1
状态转移图:
2、知识点
2.1 定义状态值
localparam
状态1 = 4’b0001,
状态2 = 4‘b0010,
状态3 = 4’b0100,
状态4 = 4‘b1000;
2.2边沿检测
原理:脉冲边沿两侧发生了变化。
下降沿:高电平——>低电平
上升沿:低电平——>高电平
如图所示,边沿检测就是清楚边沿0和1的变化顺序,时间一般都是从左往右进行,那么对于下降沿会首先检测一个1随后检测到0说明是下降沿,反之首先检测一个0随后检测到1是上升沿。
思路:
①对输入脉冲信号进行两级寄存器锁存;
②对两级寄存器进行逻辑运算(异或),两个信号不相同则发生了边沿;
③若{先进reg,后进reg}=2’b10,则是下降沿;若{先进reg,后进reg}=2’b01,则是上升沿。
2.3 $random
$random函数调用时返回一个32位的随机数,它是一个带符号的整形数。
reg[23:0] rand;
rand=$random%60; //产生一个在 -59—59范围的随机数
reg[23:0] rand;
rand={$random} %60; //通过位拼接操作{}产生0—59范围的随机数
reg[23:0] rand;
rand = min+{$random}%(max-min+1);//产生一个在min, max之间的随机数
2.4 仿真模型概念
3、代码
module ley_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 [19:0]cnt;
reg en_cnt;
reg cnt_full;//计数满标志信号
reg key_tmp0,key_tmp1;
wire pedge,nedge;
always @(posedge clk or negedge Rst_n)
begin
if(!Rst_n)
begin
key_tmp0 <= 1'b0;
key_tmp1 <= 1'b0;
end
else
begin
key_tmp0 <= key_in;
key_tmp1 <= key_tmp0;
end
end
assign nedge = (!key_tmp0)&key_tmp1;
assign pedge = key_tmp0&(!key_tmp1);
always @(posedge clk or negedge Rst_n)
if(!Rst_n)
begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDEL:
begin
key_flag <= 1'b0;
if(nedge)begin
state <= FILTER0;
en_cnt<=1'b1;
end
else
state <= IDEL;
end
FILTER0:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
state <= DOWN;
en_cnt <= 1'b0;
end
else if(pedge)begin
state <= IDEL;
en_cnt <= 1'b0;
end
else
state <= FILTER0;
DOWN:
begin
key_flag <= 1'b0;
if(pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
begin
state <= DOWN;
end
end
FILTER1:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
end
else if(nedge)
begin
en_cnt <= 1'b0;
state <= DOWN;
end
else
begin
state <= FILTER1;
end
default:
begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
always @(posedge clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt+1'b1;
else
cnt <= 20'd0;
always @(posedge clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
endmodule
4、RTL电路
RTL电路
状态转移图
状态转移表
5、testbench代码
5.1 方法一
`timescale 1ns/1ns
`define clk_period 20
module ley_filter_tb;
reg clk;
reg Rst_n;
reg key_in;
wire key_flag;
wire key_state;
ley_filter ley_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/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 = 1'b0;
#1000;
key_in = 1'b1;
#2000
key_in = 1'b0;
#1400;
key_in = 1'b1;
#2600
key_in = 1'b0;
#1300;
key_in = 1'b1;
#200
key_in = 1'b0;
#20000100;
#50000100;
key_in = 1'b1;
#2000
key_in = 1'b0;
#1000;
key_in = 1'b1;
#2000
key_in = 1'b0;
#1400;
key_in = 1'b1;
#2600
key_in = 1'b0;
#1300;
key_in = 1'b1;
#20000100;
#50000100;
key_in = 1'b0;
#1000;
key_in = 1'b1;
#2000
key_in = 1'b0;
#1400;
key_in = 1'b1;
#2600
key_in = 1'b0;
#1300;
key_in = 1'b1;
#200
key_in = 1'b0;
#50000100;
key_in = 1'b0;
#1000;
key_in = 1'b1;
#2000
key_in = 1'b0;
#1400;
key_in = 1'b1;
#2600
key_in = 1'b0;
#1300;
key_in = 1'b1;
#200
key_in = 1'b0;
#50000100;
$stop;
end
endmodule
5.2 方法二
运用随机数发生函数random。
`timescale 1ns/1ns
`define clk_period 20
module ley_filter_tb;
reg clk;
reg Rst_n;
reg key_in;
wire key_flag;
wire key_state;
ley_filter ley_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/2) clk = ~clk;
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;
press_key;
$stop;
end
reg [15:0]myrand;
task press_key;
begin
repeat(50) begin
myrand = {$random}%65536; //0-65535
#myrand key_in = ~key_in;
end
key_in = 0;
#50000000;
repeat(50) begin
myrand = {$random}%65536; //0-65535
#myrand key_in = ~key_in;
end
key_in = 1;
#50000000;
end
endtask
endmodule
5.3 方法三
运用仿真模块。
`timescale 1ns/1ns
module key_model(key);
output reg key;
reg [15:0]myrand;
initial begin
key = 1'b1;
press_key;
#10000;
press_key;
#10000;
press_key;
$stop;
end
task press_key;
begin
repeat(50) begin
myrand = {$random}%65536; //0-65535
#myrand key = ~key;
end
key = 0;
#50000000;
repeat(50) begin
myrand = {$random}%65536; //0-65535
#myrand key = ~key;
end
key = 1;
#50000000;
end
endtask
endmodule
编写testbench文件
`timescale 1ns/1ns
`define clk_period 20
module ley_filter_tb;
reg clk;
reg Rst_n;
wire key_in;
wire key_flag;
wire key_state;
ley_filter ley_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
仿真
抖动