基于状态机的按键消抖模块设计
1、按键消抖的Verilog设计(状态机设计)
1 module key_debounce ( 2 input clk, // 时钟信号(假设50MHz) 3 input rst_n, // 低电平复位信号 4 input key_in, // 按键输入信号(0表示按下,1表示松开) 5 output reg key_out // 消抖后的按键输出信号(0表示有效按下) 6 ); 7 8 // 定义四个状态 9 parameter IDLE = 2'b00; // 按键松开状态(稳定松开) 10 parameter PRESS_ON = 2'b01; // 按下抖动状态 11 parameter WAIT = 2'b10; // 按键按下状态(稳定按下) 12 parameter PRESS_OFF = 2'b11; // 释放抖动状态 13 14 reg [1:0] current_state; // 当前状态 15 reg [1:0] next_state; // 下一个状态 16 17 // 20ms计数器(假设50MHz时钟,20ms需要计数1,000,000次) 18 reg [19:0] delay_cnt; 19 parameter DELAY_20MS = 20'd1_000_000; 20 21 // 第一段:状态寄存器(同步时序逻辑) 22 always @(posedge clk or negedge rst_n) begin 23 if (!rst_n) 24 current_state <= IDLE; 25 else 26 current_state <= next_state; 27 end 28 29 // 第二段:状态转移逻辑(组合逻辑) 30 always @(*) begin 31 case (current_state) 32 IDLE: begin 33 if (key_in == 1'b0) // 检测到按键按下 34 next_state = PRESS_ON; 35 else 36 next_state = IDLE; 37 end 38 PRESS_ON: begin 39 if (key_in) // 抖动导致返回松开状态 40 next_state = IDLE; 41 else if (delay_cnt == DELAY_20MS) // 稳定按下 42 next_state = WAIT; 43 else 44 next_state = PRESS_ON; 45 end 46 WAIT: begin 47 if (key_in) // 检测到按键释放 48 next_state = PRESS_OFF; 49 else 50 next_state = WAIT; 51 end 52 PRESS_OFF: begin 53 if (!key_in) // 抖动导致返回按下状态 54 next_state = WAIT; 55 else if (delay_cnt == DELAY_20MS) // 稳定松开 56 next_state = IDLE; 57 else 58 next_state = PRESS_OFF; 59 end 60 default: next_state = IDLE; 61 endcase 62 end 63 64 // 第三段:输出逻辑和计数器控制(时序逻辑) 65 always @(posedge clk or negedge rst_n) begin 66 if (!rst_n) begin 67 key_out <= 1'b1; 68 delay_cnt <= 20'd0; 69 end 70 else begin 71 case (current_state) 72 IDLE: begin 73 key_out <= 1'b1; // 输出松开状态 74 delay_cnt <= 20'd0; // 清零计数器 75 end 76 PRESS_ON: begin 77 key_out <= 1'b1; // 抖动期间仍认为按键松开 78 if (delay_cnt < DELAY_20MS) 79 delay_cnt <= delay_cnt + 1'b1; 80 else 81 delay_cnt <= 20'd0; 82 end 83 WAIT: begin 84 key_out <= 1'b0; // 输出有效按下信号 85 delay_cnt <= 20'd0; // 清零计数器 86 end 87 PRESS_OFF: begin 88 key_out <= 1'b0; // 抖动期间仍认为按键按下 89 if (delay_cnt < DELAY_20MS) 90 delay_cnt <= delay_cnt + 1'b1; 91 else 92 delay_cnt <= 20'd0; 93 end 94 default: begin 95 key_out <= 1'b1; 96 delay_cnt <= 20'd0; 97 end 98 endcase 99 end 100 end 101 endmodule
2、仿真测试
1 `timescale 1ns/1ns // 时间单位1ns/精度1ns 2 3 module key_debounce_tb(); 4 5 // 测试信号定义 6 reg clk; // 50MHz时钟 7 reg rst_n; // 低电平复位 8 reg key_in; // 按键输入(0:按下, 1:松开) 9 wire key_out; // 消抖后输出 10 11 // 实例化被测模块 12 key_debounce uut ( 13 .clk (clk), // 连接时钟 14 .rst_n (rst_n), // 连接复位 15 .key_in (key_in), // 连接按键输入 16 .key_out (key_out) // 连接消抖输出 17 ); 18 19 // 重定义消抖时间为100个时钟周期(便于仿真观察) 20 defparam uut.DELAY_20MS = 20'd100; // 原设计20ms对应1,000,000周期 21 22 // 时钟生成 (50MHz, 周期20ns) 23 initial begin 24 clk = 0; 25 forever #10 clk = ~clk; 26 end 27 28 // 测试激励 29 initial begin 30 rst_n = 0; // 初始化 31 key_in = 1; // 初始状态为松开 32 #200; rst_n = 1; // 复位操作 33 34 // 模拟按键按下(20us后按下) 35 #20_000; key_in = 0; 36 repeat(10) key_bounce; // 模拟10次按键抖动(按下过程) 37 38 // 模拟稳定按下(100ns后按下,保持40us) 39 #100; key_in = 0; 40 #40_000; 41 42 // 模拟按键释放(200ns后释放) 43 #200; key_in = 1; 44 repeat(8) key_bounce; // 模拟8次按键抖动(释放过程) 45 46 #100; key_in = 1; // 最终保持释放状态(100ns后松开,保持50us) 47 #50_000; 48 $stop; // 停止仿真 49 end 50 51 task key_bounce; // 按键抖动任务定义 52 begin 53 @(posedge clk); #2; // 同步到时钟上升沿后2ns 54 key_in = 0; // 模拟按下 55 #({$random}%1024); // 随机延时(0-1023ns) 56 @(posedge clk); #2; // 再次同步时钟 57 key_in = 1; // 模拟释放 58 #({$random}%1024); // 随机延时(0-1023ns) 59 end 60 endtask 61 62 endmodule
浙公网安备 33010602011771号