Verilog功能模块 —— 按键消抖_verilog按键消抖-优快云博客
上面的博客提到了一种不需要复位按键的verilog去抖,这对于按键比较紧张的FPGA来说很有用,但是是用sv写的,下面给出v的版本:
/*
! 模块功能: 消除按键抖动, 得到正确的按键输入的电平
* 思路:
1.先检测按键未按下时的电平
必须保证在复位完成后的50ms(可通过参数INTI_MS修改)内,按键一直处于未按下的状态,否则未按下电平的检测会出错
2.当按键电平为按下电平时,开始一个40ms(可通过参数KEEP_MS修改)的计数器,计数到最大值则认为按键按下
3.当按键电平为松开电平时,开始一个40ms(可通过参数KEEP_MS修改)的计数器,计数到最大值则认为按键松开
*/
module myKeyEliminateJitter
#(
parameter KEY_DOWN_OUTPUT_HIGH_LEVEL = 1, // 按键按下输出的电平
parameter CLKFREQ = 100, // 计时时钟频率, 注意修改
parameter INTI_MS = 50, // 初始检测未按下电平, 需要持续多少MS才视为检测成功, 默认50ms, 通常无需修改
parameter KEEP_MS = 40 // 检测到按键按下/松开需要持续多少MS才视为有效, 默认40ms, 通常无需修改
)(
output reg key_down, // 按键按下
input key_in, // 按键输入
input clk
);
//++ 按键输入同步 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg key_in_r1;
reg key;
always @(posedge clk) begin
key_in_r1 <= key_in;
key <= key_in_r1;
end
//-- 按键输入同步 ------------------------------------------------------------
//++ 按键未按下时的电平检测 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg key_r1;
always @(posedge clk) begin
key_r1 <= key;
end
reg auto_detection_success;
// 50ms计数器
localparam CNT_50MS_MAX = CLKFREQ * 1000 * INTI_MS;
reg [$clog2(CNT_50MS_MAX+1)-1 : 0] cnt_50ms;
always @(posedge clk) begin
// 按键信号一改变就重新计数, 初始情况下这不应该发生, 只是为了应对电磁干扰或机械振动等极端条件
if (auto_detection_success)
cnt_50ms <= cnt_50ms;
else if (key_r1 != key)
cnt_50ms <= 0;
else if (cnt_50ms < CNT_50MS_MAX)
cnt_50ms <= cnt_50ms + 1'b1;
else
cnt_50ms <= 0;
end
always@(*) begin
if (cnt_50ms == CNT_50MS_MAX) // 按键信号从复位开始50ms内输入未发生变化
auto_detection_success = 1'b1;
else
auto_detection_success = 1'b0;
end
reg auto_detection_success_r1;
always @(posedge clk) begin
auto_detection_success_r1 <= auto_detection_success;
end
wire auto_detection_success_pedge;
assign auto_detection_success_pedge = auto_detection_success && ~auto_detection_success_r1;
reg key_up_value; // 存储按键未按下时的引脚电平
always @(posedge clk) begin
if (auto_detection_success_pedge)
key_up_value <= key;
else
key_up_value <= key_up_value;
end
//-- 按键未按下时的电平检测 ------------------------------------------------------------
//++ 按键消抖 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 检测按下电平, 如果按下保持40ms, 则认为键正常按下
localparam CNT_40MS_MAX = CLKFREQ * 1000 * KEEP_MS;
reg [$clog2(CNT_40MS_MAX+1)-1 : 0] key_down_cnt_40ms;
always @(posedge clk) begin
if (auto_detection_success_r1 && key == ~key_up_value) // 按键按下了
if (key_down_cnt_40ms < CNT_40MS_MAX)
key_down_cnt_40ms <= key_down_cnt_40ms + 1'b1;
else
key_down_cnt_40ms <= key_down_cnt_40ms;
else
key_down_cnt_40ms <= 0;
end
// 检测按键松开, 如果松开保持40ms, 则认为键正常松开
reg [$clog2(CNT_40MS_MAX+1)-1 : 0] key_up_cnt_40ms;
always @(posedge clk) begin
if (auto_detection_success_r1 && key == key_up_value) // 按键松开了
if (key_up_cnt_40ms < CNT_40MS_MAX)
key_up_cnt_40ms <= key_up_cnt_40ms + 1'b1;
else
key_up_cnt_40ms <= key_up_cnt_40ms;
else
key_up_cnt_40ms <= 0;
end
always @(posedge clk) begin
if (key_down_cnt_40ms == CNT_40MS_MAX) // 按下计数值保持最大值不变说明键处于按下的状态
key_down <= KEY_DOWN_OUTPUT_HIGH_LEVEL;
else if (key_up_cnt_40ms == CNT_40MS_MAX) // 松开计数值保持最大值不变说明键处于松开的状态
key_down <= ~KEY_DOWN_OUTPUT_HIGH_LEVEL;
else
key_down <= key_down;
end
//-- 按键消抖 ------------------------------------------------------------
endmodule