最近一直在学习FPGA,今天在学习延时语句时,发现了连续赋值的一个特点。在连续赋值语句中添加延时时,任何小于延迟值的输入变化都会被滤除而不会体现在输出上。比如 #10 B = A; 当A的变化小于10ns时,在输出B上不会体现出来。
突然就想到了利用这个特点是不是就可以消除按键抖动。延时时间为20ms,用连续赋值语句读取按键输入口的值,那么当按键在20ms内抖动时,输出值不会有变化,只有当按键值稳定20ms以上时,输出值才会有变化。这样不就实现了按键消抖了吗?说干就干,就先测试一下这种方法是否可行。
首先验证连续赋值语句的延时功能。
编写代码如下:
`timescale 1ns/1ns
module assign_test(
a,
b
);
input [3:0] a;
output [3:0] b;
assign #8 b = a;
endmodule
延时8ns将a的值赋给b,只有a的值稳定时间大于等于8ns时,b上才有值输出,否则a的值在8ns以内变化时,输出b上无变化。
测试文件如下:
`timescale 1ns/1ns
module assign_test_tb;
reg [3:0] a;
wire [3:0] b;
assign_test assign_test(
.a (a),
.b (b)
);
initial begin
a = 4'd0;
#5;
a = 4'd1;
#8;
a = 4'd2;
#5;
a = 4'd3;
#3;
a = 4'd4;
#10;
a = 4'd5;
#5;
a = 4'd6;
#15;
a = 4'd7;
#5;
$stop;
end
endmodule
测试文件中a的值不停变化,看输出b的值怎样变化。
仿真波形如下:
由仿真波形可以看出,a的值从0变化到6,输出b的值依次为1、4、6,数字1的持续时间为8ns,数字4的持续时间为10ns,数字6的持续时间为15ns。其他数字的持续时间都小于8ns,所以输出b上检测不到。说明在连续赋值语句中使用延时可以获取到稳定的输出信号,滤除掉信号中间的干扰。
利用这个特点来测试按键消抖,代码如下:
`timescale 1ns/1ns
module key_check(
clk,
rst_n,
key_in,
led
);
parameter DELAY = 20_000_000; //20ms
input clk;
input rst_n;
input key_in; //按键输入信号
output reg led; //led指示灯
wire key_sta; //按键稳定输出后信号
assign #DELAY key_sta = key_in; //利用连续赋值语句的延时功能对按键进行消抖
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
led <= 1'b0;
else if(key_sta == 1'b0)
led <= 1'b1;
else
led <= 1'b0;
end
endmodule
在连续赋值语句前增加20ms的延时,只有按键输入信号稳定20ms以上时,才会输出到key_sta上,然后根据key_sta的电平状态改变led灯的亮灭。
测试代码如下
`timescale 1ns/1ns
module key_check_tb;
parameter T = 20; //20ns 50M时钟
reg clk;
reg rst_n;
reg key_in;
wire led;
key_check key_check(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.led (led)
);
initial clk = 1'b0;
always #(T/2) clk = ~clk;
initial begin
rst_n = 1'b0;
key_in = 1'b1;
#(20 * T + 1);
rst_n = 1'b1;
#(T * 100);
repeat(3) begin
//按下抖动
key_in = 1'b0;
#(T * 50_000);
key_in = 1'b1;
#(T * 10_000);
key_in = 1'b0;
#(T * 20_000);
key_in = 1'b1;
#(T * 30_000);
key_in = 1'b0;
#(T * 10_000);
key_in = 1'b1;
#(T * 10_000);
//按下
key_in = 1'b0;
#(T * 2000_000);
//弹起抖动
key_in = 1'b1;
#(T * 50_000);
key_in = 1'b0;
#(T * 10_000);
key_in = 1'b1;
#(T * 20_000);
key_in = 1'b0;
#(T * 30_000);
key_in = 1'b1;
#(T * 10_000);
key_in = 1'b0;
#(T * 10_000);
//弹起
key_in = 1'b1;
#(T * 5000_000);
end
$stop;
end
endmodule
测试代码中模拟了3次按键抖动,下面看输出波形
通过波形可以看出当key_in的状态发生改变并稳定20ms时,key_sta的状态才会发生改变。输入信号key_in上有抖动,而key_sta状态是稳定的,没有发生抖动。而led输出状态刚好和key_sta状态相反,符合设计要求。
看来通过对连续赋值语句加延时的方法可以进行按键消抖,理论上验证也是成功的。但是目前在网上没有搜到类似的方法,不知道这种方法在实际应用中会不会出现问题。如果看到这篇文章朋友有这方面经验的话,可以一起讨论下这种按键消抖方法的可行性。
更正
通过最近几天学习和查资料时明白了,延时语句如:#开头的延时语句,不可综合成硬件电路延时,综合工具会忽略所有延时代码,但不会报错。
如:a=#10 b;
这里的#10是用于仿真时的延时,在综合的时候综合工具会忽略它。也就是说,在综合的时候上式等同于a=b;
延时语句在前仿真时,可以看到延时效果。但是在后仿真时,会被忽略掉,不起任何作用。
前仿真波形:
后仿真波形
通过仿真波形可以明显看出,上述的按键延时方法在前仿真时起到了消抖的作用,但是在后仿真中没有起到消抖作用。所以这种按键消抖方法在实际项目中不能使用。