按键消抖原因
使用机械弹性开关,当机械触点闭合/断开时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定的接通,在断开时也不会马上断开。而是会在闭合/断开的瞬间伴随一连串的抖动,为避免这种现象带来的问题,需要进行按键消抖。
硬件消抖
在按键个数较少时可以使用硬件方法消除抖动。下图所示为使用RS触发器进行硬件消抖,当按键未按下时,输出为0;当按键按下时,输出为1。此时,即使按键因为弹性抖动而产生瞬时断开(抖动从B跳开),只要按键不回到原始状态A,双稳态触发器的状态也不会改变(保持为0),从而不产生抖动的波形。
软件消抖
原理
在按键数量较多的情况下,通常采用软件方法进行消抖。在检测到按键闭合后开始执行一个延时程序,根据抖动的时间通常为5~10ms,去产生一个20ms的延时,让前沿抖动消失后再一次检测按键的状态。如果仍保持闭合状态电平,则认定为真正有键按下。
消抖过程中的问题
1、消除机械抖动导致的毛刺影响
由于机械抖动关系,在按键按下和松开过程中会出现按键抖动现象,从而导致计数器技术时间难以确定。
解决方法:计数器的开始由最后一次低电平(按键信号)决定。若按键信号key_in在到达产生flag之前置高,则将前一次脉冲认为是抖动,计数器清零,在下一次key_in置低后计数器再开始计数。换言之就是:系统检测到key_in为低电平的时候开始计数,检测到高电平时计数器清零。
2、按键时间过久导致出现多个flag信号的现象
假设使用50MHz的晶振进行计算,计数器计数20ms需要计数999_999个周期,当计数器计满后习惯清零,从而导致flag信号到达999_999后产生1个时钟周期宽度的flag信号,同时计数器归零。当按键时间过久,可能导致计数器存在多个0→999_999→0状态,同时,每次到达999_999后会产生一个按键被按下的flag,从而导致出现多个flag的脉冲。
解决方法:计数器的归零由按键信号决定,当按键信号为高电平(表示按键被松开)时,计数器才从999_999归零。
同时为确保flag信号仅保持一个周期,当计数器为999_998时,产生flag信号。否则flag会一直保持高电平直到计数器归零。
按键消抖代码
1 module key_fileter #(parameter CNT_MAX=20'd999_999)( 2 input sys_clk , // 系统时钟50MHz 3 input sys_rst_n , // 全局复位 4 input key_in , // 按键输入信号 5 output reg key_flag // 为1时表示消抖后检测到按键被按下,为0表示没有检测到被按下 6 ); 7 reg [19:0] cnt_20ms; 8 // cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,开始计数 9 always @(posedge sys_clk or negedge sys_rst_n) 10 if(!sys_rst_n) 11 cnt_20ms <= 20'b0; 12 else if(key_in) 13 cnt_20ms <= 20'b0; 14 else if(cnt_20ms==CNT_MAX && key_in) 15 cnt_20ms <= cnt_20ms; 16 else 17 cnt_20ms <= cnt_20ms + 1'b1; 18 19 // key_flag:当计数满20ms后产生按键有效标志位,且key_flag在999_999时拉高,维持一个周期的高电平 20 always @(posedge sys_clk or negedge sys_rst_n) 21 if(!sys_rst_n) 22 key_flag <= 1'b0; 23 else if(cnt_20ms== CNT_MAX-1'b1) 24 key_flag <= 1'b1; 25 else 26 key_flag <= 1'b0; 27 28 endmodule
key_filter
按键消抖testbench
1 `timescale 1ns/1ns 2 3 module tb_key_filter(); 4 // I/O port 5 reg sys_clk; 6 reg sys_rst_n; 7 reg key_in; 8 wire key_flag; 9 10 reg [21:0] tb_cnt; 11 12 // 为缩短仿真时间,将参数化的时间值改小,但位宽定参数名的宽度 13 parameter CNT_1MS = 20'd19; 14 parameter CNT_11MS = 21'd69; 15 parameter CNT_41MS = 22'd149; 16 parameter CNT_51MS = 22'd199; 17 parameter CNT_60MS = 22'd249; 18 19 // 初始化系统时钟、复位信号和输入信号 20 initial 21 begin 22 sys_clk <= 1'b1; 23 sys_rst_n <= 1'b0; 24 key_in <= 1'b0; 25 #20 26 sys_rst_n <= 1'b1; 27 end 28 29 // 定义系统时钟,50MHz 30 always #10 sys_clk = ~sys_clk; 31 32 // tb_cnt: 按键过程计数器,用于模拟按键抖动过程 33 always @(posedge sys_clk or negedge sys_rst_n) 34 if(!sys_rst_n) 35 tb_cnt <= 22'b0; 36 else if(tb_cnt==CNT_60MS) // 计数器计到60MS是完成一次按键从按下到释放的过程 37 tb_cnt <= 22'b0; 38 else 39 tb_cnt <= tb_cnt + 1'b1; 40 41 // key_in:产生输入随机数,模拟按键情况 42 always @(posedge sys_clk or negedge sys_rst_n) 43 if(!sys_rst_n) 44 key_in <= 1'b1; // 按键未按下时的状态为高电平 45 else if((tb_cnt>=CNT_1MS && tb_cnt<=CNT_11MS) || (tb_cnt>=CNT_41MS && tb_cnt<=CNT_51MS)) 46 key_in <= {$random} % 2; 47 else if(tb_cnt>=CNT_11MS && tb_cnt<=CNT_41MS) 48 key_in <= 1'b0; 49 else 50 key_in <= 1'b1; 51 52 //--------------------key_filter_inst------------------------------ 53 key_fileter #( 54 .CNT_MAX (20'd24 ) 55 ) 56 key_fileter_inst( 57 .sys_clk (sys_clk ), 58 .sys_rst_n (sys_rst_n ), 59 .key_in (key_in ), 60 .key_flag (key_flag ) 61 ); 62 63 endmodule
tb_key_filter
波形图
时钟频率为50MHz,当key_in保持24个周期的低电平时,产生一个周期的key_flag脉冲。波形图如下图所示。
今天的文章按键消抖分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/56975.html