硬件消抖:RS触发器
软件消抖:
模拟按键产生的高低电平毛刺,低电平有效,由于毛刺的存在,当确定按下时间时,若是每次都按照消抖的最大时间10ms考虑,在高频情况下,会浪费一定的时间,而按5ms考虑又无法保证按键已经消抖。所以这里添加一个计数器,当检测到低电平就开始计数,检测到高电平就将其清零。输出采用信号标志,当计数器计数到最大值时升高一个系统时钟周期的电平,其余时间保持低电平。
module key_filter #(parameter CNT_MAX = 20'd999_999) //端口内的参数只能在这里定义 ( input wire sys_clk, input wire res_n, input wire key_in, output reg key_flag //输出为消抖后的按键脉冲信号 ); reg [19:0] cnt_20ms; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) cnt_20ms <= 20'd0; else if(key_in == 1'b1) cnt_20ms <= 20'd0; else if(cnt_20ms == CNT_MAX) cnt_20ms <= CNT_MAX; else cnt_20ms <= cnt_20ms + 1'b1; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) key_flag <= 1'b0; else if(cnt_20ms == CNT_MAX - 20'd1) key_flag <= 1'b1; else key_flag <= 1'b0; endmodule
`timescale 1ns/1ns module tb_key_filter(); reg sys_clk; reg res_n; reg key_in; reg [7:0] tb_cnt; // 使用计数器来确定按键的毛刺以及按下状态的时间确定模拟 wire key_flag; initial begin sys_clk <= 1'b1; res_n <= 1'b0; #20 res_n <= 1'b1; end always #10 sys_clk = ~sys_clk; always@(posedge sys_clk or negedge res_n) // 初始化tb_cnt的状态 if(res_n == 1'b0) tb_cnt <= 8'd0; else if(tb_cnt == 8'd249) tb_cnt <= 8'd0; else tb_cnt <= tb_cnt + 8'd1; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) key_in <= 1'b1; else if((tb_cnt >= 8'd19)&&(tb_cnt <= 8'd49)||(tb_cnt >= 8'd149)&&(tb_cnt <= 8'd199)) key_in <= {$random}%2; // 利用随机数取余模拟按键的毛刺状态,利用tb_cnt来确认毛刺的产生 else if((tb_cnt < 8'd19)||(tb_cnt > 8'd199)) key_in <= 1'b1; else key_in <= 1'b0; // 在tb_cnt其他的计数时间里保持按键按下状态,即低电平 key_filter #(.CNT_MAX(20'd24)) // 减少计数时间 key_filter_1 ( .sys_clk(sys_clk), .res_n(res_n), .key_in(key_in), .key_flag(key_flag) ); endmodule
触摸按键控制LED灯 -- 边沿检测方法
电阻式耐用性差现在已经很少用了,电容式弥补了电阻式耐用性的缺点,红外扫描常应用在环境较差的场合,表面声波常应用在公共场合下 。
module touch_ctrl_led ( input wire sys_clk, input wire res_n, input wire touch_key, output reg led ); reg touch_key_1; reg touch_key_2; wire touch_flag; //利用key1,key2,flag,形成一个边沿检测 always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) begin touch_key_1 <= 1'b1; touch_key_2 <= 1'b1; end else begin touch_key_1 <= touch_key; touch_key_2 <= touch_key_1; end assign touch_flag = ((touch_key_1 == 1'b0)&&(touch_key_2 == 1'b1)); //检测下降沿 // assign touch_flag = ((touch_key_1 == 1'b1)&&(touch_key_2 == 1'b0)); //检测上升沿 always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) led <= 1'b1; else if(touch_flag == 1'b1) led <= ~led; else led <= led; endmodule
`timescale 1ns/1ns module tb_touch_ctrl_led(); reg sys_clk; reg res_n; reg touch_key; wire led; initial begin sys_clk = 1'b1; res_n <= 1'b0; touch_key <= 1'b1; #20 res_n <= 1'b1; #200 touch_key <= 1'b0; #1000 touch_key <= 1'b1; #3000 touch_key <= 1'b0; #1000 touch_key <= 1'b0; end always #10 sys_clk = ~sys_clk; touch_ctrl_led touch_ctrl_led_1 ( .sys_clk(sys_clk), .res_n(res_n), .touch_key(touch_key), .led(led) ); endmodule