Verilog 按键去抖模块代码详解:一步步解读
这是一个用 Verilog 语言写的按键去抖模块。模块的输入包括时钟信号 i_clk、复位信号 i_rst_n 和一个 10 位的按键输入信号 i_key,其中按下为 0,松开为 1。模块的输出为一个 10 位的键值信号 o_key_val。
module key_debounce(
input i_clk,
input i_rst_n,
input [9:1] i_key, // 按下为 0,松开为 1
output reg [9:1] o_key_val // 键值
);
//++++++++++++++++++++++++++++++++++++++
reg [9:1] key_samp1, key_samp1_locked;
// 将 i_key 采集至 key_samp1
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp1 <= 9'h1FF;
else
key_samp1 <= i_key;
// 将 key_samp1 锁存至 key_samp1_locked
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp1_locked <= 9'h1FF;
else
key_samp1_locked <= key_samp1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [9:1] key_changed1;
// 当 key_samp1 由 1 变为 0 时
// key_changed1 由 0 变为 1,只维持一个时钟周期
assign key_changed1 = key_samp1_locked & (~key_samp1);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [19:0] cnt;
// 一旦有按键按下,cnt 立即被清零
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
cnt <= 20'h0;
else if(key_changed1)
cnt <= 20'h0;
else
cnt <= cnt + 1'b1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [9:1] key_samp2, key_samp2_locked;
// 只有当按键不变化(不抖动),且维持 20ms 以上时
// 才将 i_key 采集至 key_samp2
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp2 <= 9'h1FF;
else if(cnt == 20'hF_FFFF) // 0xFFFFF/50M = 20.9715ms
key_samp2 <= i_key;
// 将 key_samp2 锁存至 key_samp2_locked
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp2_locked <= 9'h1FF;
else
key_samp2_locked <= key_samp2;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [9:1] key_changed2;
// 当 key_samp2 由 1 变为 0 时
// key_changed2 由 0 变为 1,只维持一个时钟周期
assign key_changed2 = key_samp2_locked & (~key_samp2);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
// 每次按键稳定后,输出键值
// 按下为 0,松开为 1
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
o_key_val <= 9'h1FF;
else
o_key_val <= ~key_changed2;
//--------------------------------------
endmodule
接下来是代码的解释:
reg [9:1] key_samp1, key_samp1_locked;
定义了两个 10 位的寄存器 key_samp1 和 key_samp1_locked,用于存储第一次采集到的按键信号和锁存后的按键信号。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp1 <= 9'h1FF; else key_samp1 <= i_key;
通过 always 块实现 i_key 信号的采样,当 i_rst_n 为低电平时,将 key_samp1 初始化为全 1,否则将 i_key 赋值给 key_samp1。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp1_locked <= 9'h1FF; else key_samp1_locked <= key_samp1;
通过 always 块实现 key_samp1 信号的锁存,当 i_rst_n 为低电平时,将 key_samp1_locked 初始化为全 1,否则将 key_samp1 赋值给 key_samp1_locked。
wire [9:1] key_changed1;
定义一个 10 位的 wire 类型变量 key_changed1,用于表示按键状态是否发生变化。
assign key_changed1 = key_samp1_locked & (~key_samp1);
通过 assign 语句实现按键状态的检测,当 key_samp1 由 1 变为 0 时,key_changed1 由 0 变为 1,只维持一个时钟周期。
reg [19:0] cnt;
定义一个 20 位的寄存器 cnt,用于计数按键状态维持的时间。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) cnt <= 20'h0; else if(key_changed1) cnt <= 20'h0; else cnt <= cnt + 1'b1;
通过 always 块实现按键状态的计数,当 i_rst_n 为低电平时,将 cnt 初始化为 0,当按键状态发生变化时,将 cnt 清零,否则 cnt 加 1。
reg [9:1] key_samp2, key_samp2_locked;
定义了两个 10 位的寄存器 key_samp2 和 key_samp2_locked,用于存储第二次采集到的按键信号和锁存后的按键信号。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp2 <= 9'h1FF; else if(cnt == 20'hF_FFFF) key_samp2 <= i_key;
通过 always 块实现第二次按键信号的采样,当 i_rst_n 为低电平时,将 key_samp2 初始化为全 1,否则当按键状态维持 20ms 以上时,将 i_key 赋值给 key_samp2。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp2_locked <= 9'h1FF; else key_samp2_locked <= key_samp2;
通过 always 块实现 key_samp2 信号的锁存,当 i_rst_n 为低电平时,将 key_samp2_locked 初始化为全 1,否则将 key_samp2 赋值给 key_samp2_locked。
wire [9:1] key_changed2;
定义一个 10 位的 wire 类型变量 key_changed2,用于表示按键状态是否发生变化。
assign key_changed2 = key_samp2_locked & (~key_samp2);
通过 assign 语句实现按键状态的检测,当 key_samp2 由 1 变为 0 时,key_changed2 由 0 变为 1,只维持一个时钟周期。
always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) o_key_val <= 9'h1FF; else o_key_val <= ~key_changed2;
通过 always 块实现键值的输出,当 i_rst_n 为低电平时,将 o_key_val 初始化为全 1,否则将 key_changed2 取反后赋值给 o_key_val,表示按键被按下时输出 0,松开时输出 1。
最终,这个模块实现了按键去抖的功能,通过采样、计数、锁存和状态检测等步骤,得到稳定的按键状态,并输出对应的键值。
原文地址: https://www.cveoy.top/t/topic/jnzx 著作权归作者所有。请勿转载和采集!