按键消抖(边沿检测)
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
浙公网安备 33010602011771号