基于状态机的按键消抖模块设计

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
View Code

 

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
View Code

 

posted @ 2026-06-04 18:01  FPGA9161  阅读(11)  评论(0)    收藏  举报