一、边缘检测
(一)基本原理
在数字电路中,上升沿和下降沿检测是常见的任务,通常用于触发时钟边沿、捕捉输入信号的变化等。要对信号的边沿进行检测,则需要知道边沿前后的电平状态,若在边沿前一时刻是低电平,后一时刻是高电平,则是上升沿,反之则是下降沿。
使用两个寄存器in1和in2对输入信号in进行存储,在非阻塞赋值中,由于是先运算再赋值,所以in1和in2信号分别在in信号上分别延后1个和2个时钟周期。
always @ (posedge sys_clk or negedge sys_rst_n)
begin
if (!sys_rst_n)
begin
in1 <= 0;
in2 <= 0;
end
else
begin
in1<=in;
in2<=in1;
end
end
假设在第n个时钟周期对复位信号无效,则在第n+1个时钟周期上升沿来到时in1信号被赋值为in,在第n+2个时钟周期上升沿来到时in2信号被赋值为in。由此实现对输入信号打两拍的操作。
检测上升沿:assign pos = in & (~in2);
检测下降沿:assign neg = ~in & in2;
检测双边沿:assign both =(in & (~in2)) | (~in & in2);
(二)具体实现
以按键控制流水灯为例,当按键按下时会出现一个脉冲,系统检测到该脉冲时控制LED灯亮,再次按下按键时,灯灭,默认为灯灭。因此需要进行一次上升沿检测。具体代码如下:
module edge_detect(
input sys_clk ,
input sys_rst_n ,
input key ,
output led
);
reg key1,key2;
wire key_en;
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
begin
key1 <= 1'b0;
key2 <= 1'b0;
end
else if
begin
key1 <= key;
key2 <= key1;
end
end
assign key_en = (key1) & (~key2);
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
led <= 0;
else if
begin
if(key_en == 1)
led = ~led;
end
end
进行测试文件的编写,可以直接通过Quartus自动编写一部分,操作流程如下:
测试文件如下:
`timescale 1 ns/ 1 ps
module touch_led_vlg_tst();
reg key;
reg sys_clk;
reg sys_rst_n;
// wires
wire led;
touch_led i1 (
.key(key),
.led(led),
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n)
);
initial
begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
key = 1'b1;
#100 sys_rst_n = 1'b1;
#300 key = 1'b0;
#300 key = 1'b1;
end
assign #10 sys_clk <= ~sys_clk;
endmodule
二、按键消抖
按键消抖是指在处理数字输入时,防止由于物理按键的机械性能引起的抖动或干扰而产生的错误输入。物理按键可能在按下或释放时发生短暂的弹跳,导致多个触发事件。
由于FPGA对于电平信号十分敏感,因此输入进板子的信号如上所示。在消抖后,需要按键信号变为下面这种形式。
对于按键消抖,本文介绍两种方法,一是对按键信号进行打拍,检测新的按键信号与原信号电平信号是否相等,二是构建一个计数器,当按键电平被按下后,看电平状态是否能保持20ms,若能,则表示按键已处于稳定状态。
(一)
以按键控制蜂鸣器为例,当按键被按下时,蜂鸣器响
当检测到一个按键信号下降脉冲沿,则计时20ms,我们认为按键的抖动时间最长为20ms。代码如下:
module key_debounce(
input sys_clk ;
input sys_rst_n;
input key ;
output beep ;
);
reg key1;
reg key2;
reg [19:0]cnt ;
wire neg ;
wire add_cnt;
wire end_cnt;
//打拍
always @(posedge clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
key <= 1'b1;
key1 <= 1'b1;
key2 <= 1'b1;
end
else begin
key1 <= key;
key2 <= key1;
end
end
//进行下降沿检测
assign neg = ~key1 & key2;
//计数器
always @(posedge clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 20'd0;
else if(add_cnt) begin
if(end_cnt)
cnt <= 20'd0;
else
cnt <= cnt+1;
end
end
assign add_cnt = neg;
assign end_cnt =add_cnt & cnt==1_000_000-1;
//按键确认按下
always @(posedge clk or negedge sys_rst_n)begin
if(!sys_rst_n)
beep = 1'b0;
else if(end_cnt)
beep = 1'b1;
end
endmodule
(二)
与第一种方法不同的是,只要按键信号与寄存器信号不同,就重新进行计数。
module key_debounce(
input sys_clk,
input sys_rst_n,
input key,
output reg beep
);
reg [31:0] delay_cnt;
reg key_reg;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
delay_cnt <= 32'd0;
end
else begin
key_reg <= key;
if(key_reg != key)
delay_cnt <= 32'd1000000;
else if(key_reg == key) begin
if(delay_cnt > 32'd0)
delay_cnt <= delay_cnt - 1'b1;
else
delay_cnt <= delay_cnt;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
beep = 1'b0;
end
else begin
if(delay_cnt == 32'd1) begin
beep = 1'b1;
end
else begin
beep = 1'b0;
end
end
end
endmodule
##本文参考正点原子视频,如有侵权,请联系删除。