按键消抖(边沿检测)

1、按键消抖(边沿检测)

 1 //仅支持单次模式的按键检测电路设计
 2 module key_single(
 3     input  wire         clk_in,
 4     input  wire         rst_n,
 5     input  wire         key_i,
 6     output reg          pulse_ctrl_o
 7 );
 8 
 9     //两级触发器避免亚稳态,得到key_cur_r     
10     reg key_syn_r;//两级触发器避免亚稳态--第1级
11     reg key_cur_r;//两级触发器避免亚稳态--第2级
12     
13     always@(posedge clk_in or negedge rst_n)begin
14         if(!rst_n)begin
15             key_syn_r  <= 1'b1;//按键正常(或松开)是高电平
16             key_cur_r  <= 1'b1;
17         end
18         else begin
19             key_syn_r  <= key_i;//按键正常(或松开)是高电平
20             key_cur_r  <= key_syn_r;
21         end
22     end
23     
24     //边沿检测电路
25     reg key_last_r;//将key_cur_r延缓一拍,相当于记录key_cur_r上次值
26     
27     always@(posedge clk_in or negedge rst_n)begin
28         if(!rst_n)
29             key_last_r <= 1'b1;
30         else 
31             key_last_r <= key_cur_r;//按键正常是高电平
32     end
33         
34     //下降沿检测
35     reg edge_neg_r; 
36     always@(posedge clk_in or negedge rst_n)begin
37         if(!rst_n)
38             edge_neg_r    <= 1'b0;
39         else begin
40                 //上一时刻为1,且当前时刻为0,代表下降沿
41             if(key_last_r == 1'b1 && key_cur_r==1'b0)
42                 edge_neg_r <= 1'b1;//大概率仅维持1个时钟宽度
43             else
44                 edge_neg_r <= 1'b0;
45         end
46     end
47     
48     //上升沿检测
49     reg edge_pos_r; 
50     always@(posedge clk_in or negedge rst_n)begin
51         if(!rst_n)
52             edge_pos_r    <= 1'b0;
53         else begin
54                 //上一时刻为0,且当前时刻为1,代表下降沿
55             if(key_last_r == 1'b0 && key_cur_r==1'b1)
56                 edge_pos_r <= 1'b1;//仅维持1个时钟宽度
57             else
58                 edge_pos_r <= 1'b0;
59         end
60     end    
61     
62     //延时计数器  
63     //cnt_20ms_r常置为0,edgepos_r或edgeneg_r只要有一个为1,cnt_20ms_r置为1  
64     //cnt_20ms_r只要不为0,就自动累加,加到0就保持在0,当前自加的过程中,  
65     //edge_pos_r或edge_neg_r只要有一个为1立即重新置为1,相当于重启  
66     reg [19:0]cnt_20ms_r;//1_058_575*20ns≈20ms 
67     always@(posedge clk_in or negedge rst_n)begin
68         if(!rst_n)
69             cnt_20ms_r    <= 20'd0;
70         else begin
71             if((edge_neg_r || edge_pos_r)==1'b1)//重启
72                 cnt_20ms_r <=20'd1;
73             else if(cnt_20ms_r == 20'd0 )     //停止
74                 cnt_20ms_r <= cnt_20ms_r;
75             else
76                 cnt_20ms_r <= cnt_20ms_r+20'd1;//启动后则一直加满,则回到0停止
77         end
78     end
79     
80     // 根据cnt_20ms_r值和当前按键值判定:按键是否按下或者松开
81     always@(posedge clk_in or negedge rst_n)begin
82         if(!rst_n)
83             pulse_ctrl_o    <= 1'b0;
84         else begin//20'd999999也可换做附近值如20'hfffff 
85             if(cnt_20ms_r==20'd999999 && key_last_r==1'b0 && key_cur_r==1'b0)
86                 pulse_ctrl_o <= 1'b1;//一个脉冲宽度
87             else
88                 pulse_ctrl_o <= 1'b0;
89         end
90     end
91     
92 endmodule

2、仿真测试

 1 `timescale 1ns/1ns        // 时间单位1ns/精度1ns
 2 
 3 module key_single_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_single key_single_inst(
13         .clk_in            (clk),
14         .rst_n            (rst_n),
15         .key_i            (key_in),
16         .pulse_ctrl_o    (key_out)
17     );
18 
19     // 时钟生成 (50MHz, 周期20ns)
20     initial begin
21          clk = 0;
22          forever #10 clk = ~clk;
23     end
24 
25     // 测试激励
26     initial begin
27          rst_n = 0;                 // 初始化
28          key_in = 1;                 // 初始状态为松开
29          #200; rst_n = 1;            // 复位操作
30 
31          // 模拟按键按下(20us后按下)
32          #2_000_000; key_in = 0;
33          repeat(5)    key_bounce; // 模拟10次按键抖动(按下过程)
34 
35          // 模拟稳定按下(100ns后按下,保持40us)
36          #100; key_in = 0;
37          #30_000_000;
38 
39          // 模拟按键释放(200ns后释放)
40          #200; key_in = 1;
41          repeat(3)    key_bounce; // 模拟8次按键抖动(释放过程)
42 
43          #100; key_in = 1;        // 最终保持释放状态(100ns后松开,保持50us)
44          #2_000_000;
45          $stop;                        // 停止仿真
46     end
47 
48     task key_bounce;                    // 按键抖动任务定义
49         begin
50             @(posedge clk); #2;        // 同步到时钟上升沿后2ns
51             key_in = 0;                  // 模拟按下
52             #({$random}%1024);        // 随机延时(0-1023ns)
53             @(posedge clk); #2;        // 再次同步时钟
54             key_in = 1;                 // 模拟释放
55             #({$random}%1024);        // 随机延时(0-1023ns)
56         end
57     endtask
58 
59 endmodule

 

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