按键边沿检测▷▷▷
边沿检测经常用于按键输入检测电路中,按键按下时输入信号 key 变为低电平,按键抬起变为高电平。当输入的信号为理想的高低电平时(不考虑毛刺和抖动),边沿检测就发挥了很重要的作用。
由于输入的信号为一个连续值,我们需要通过时钟进行采样。根据采样定理,采样时钟的频率需要至少为被采信号频率的 2 倍。
设计的边沿检测电路的功能为:检测到 1 个下降沿或上升沿时,对应的下降沿输出信号 edge_pos、edge_neg 分别输出 1 个脉冲(即一个时钟周期的高电平)。下面以下降沿进行分析。
分析:在边沿检测的过程中,通过 1 个寄存器来寄存上一个时钟沿的输入值 D ,当寄存器输出 Q 与输入 D 的值分别为1、0时,证明检测到下降沿。
如上图所示,在第 2 个时钟周期的低电平期间,D 由高变低,在第 3 个时钟周期的上升沿,Q由高变低。在 D 由高变低的时刻,Q 为 1 ,D 为 0 ,证明检测到下降沿。
按键边沿捕获模块:
测试验证模块:
按一次按键,对应的LED灯就会亮。LED亮代表检测到了下降沿。
Signaltap 波形图:
按键硬件消抖电路▷▷▷
友晶科技很多板子的按键(都是按下为低电平)其实是已经有硬件消抖电路的, 这样的板子的按键的值 直接input 进来后 直接用就可以。
比如DE2-115 DE1-SOC DE10-Standard 等等。这里都用74HC245芯片来消抖:
按键Verilog消抖电路▷▷▷
如果没有硬件上的消抖,我们可以手写Verilog代码替代消抖电路。
模板一
模板一的Verilog消抖的原理主要为按键按下或松开后延时 1ms—20ms 采样(这个时间是根据按键的机械特性自行决定)。
假设时钟是50M,按键消抖的思路是检测到按下时延时 50000个时钟周期,再检测,如果状态仍为按下,则确认是按下的;如果状态为弹起的,则确认是干扰,无按键按下。
按键消抖的Verilog实现的模板一如下:
module key_debounce //按键消抖模块
( input clk, //系统时钟
input rst_n, //系统复位
input key[0], //按键输入
output reg key_value, //有效的按键值
);
reg [31:0]cnt;//计数器
reg value;//中间寄存器
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 0; //初始状态下寄存器清零
key_value <= 0; //有效按键值清零
value <= 0; //中间寄存器清零
end
else begin
if(cnt == 50000) begin
cnt <= 0;//每隔0.001秒检测一次 将key的值寄存到value寄存器当中(如果系统时钟是50MHz)
value <= key[0];
if(value == 1 && key[0] == 0) //按键按下为0,平时为1
key_value <= 1;
end
else begin
cnt <= cnt + 1;
key_value <= 0;
end
end
end
endmodule
模板二
模板二的检测原理是只有按键按下的状态持续50000个周期(这个时间可以自己再定义)以上,才认定是按键被按下了一次,否则算作是干扰被忽略掉。
模板二实现的是多个按键的去抖。
按键消抖的Verilog实现的模板二如下:
module debounce (
clk,
reset_n,
data_in,
data_out
);
parameter WIDTH = 32; // set to be the width of the bus being debounced
parameter POLARITY = "HIGH"; // set to be "HIGH" for active high debounce or "LOW" for active low debounce
parameter TIMEOUT = 50000; // number of input clock cycles the input signal needs to be in the active state
parameter TIMEOUT_WIDTH = 16; // set to be ceil(log2(TIMEOUT))
input wire clk;
input wire reset_n;
input wire [WIDTH-1:0] data_in;
output wire [WIDTH-1:0] data_out;
reg [TIMEOUT_WIDTH-1:0] counter [0:WIDTH-1];
wire counter_reset [0:WIDTH-1];
wire counter_enable [0:WIDTH-1];
// need one counter per input to debounce
genvar i;
generate for (i = 0; i < WIDTH; i = i+1)
begin: debounce_counter_loop
always @ (posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
counter[i] <= 0;
end
else
begin
if (counter_reset[i] == 1) // resetting the counter needs to win
begin
counter[i] <= 0;
end
else if (counter_enable[i] == 1)
begin
counter[i] <= counter[i] + 1'b1;
end
end
end
if (POLARITY == "HIGH")
begin
assign counter_reset[i] = (data_in[i] == 0);
assign counter_enable[i] = (data_in[i] == 1) & (counter[i] < TIMEOUT);
assign data_out[i] = (counter[i] == TIMEOUT) ? 1'b1 : 1'b0;
end
else
begin
assign counter_reset[i] = (data_in[i] == 1);
assign counter_enable[i] = (data_in[i] == 0) & (counter[i] < TIMEOUT);
assign data_out[i] = (counter[i] == TIMEOUT) ? 1'b0 : 1'b1;
end
end
endgenerate
endmodule
往期阅读: