电子说
在数字威廉希尔官方网站
中,开关用于用于产生高、低电平,按键用于产生单次脉冲。由于开关和按键为机械部件,每次按下或者释放时,由于簧片的弹性会产生短暂的抖动,然后才能稳定接通或者断开。
抖动现象会导致按键威廉希尔官方网站
的输出产生毛刺,如图1所示,从而可能导致系统产生误动作。
开关和按键的抖动时间一般在20ms以内。为了防止因按键抖动引起的系统误动作,必须对按键威廉希尔官方网站
进行消抖,只在按键闭合或者断开稳定后才允许输出。
图1 按键抖动现象
按键消抖有软件消抖和硬件消抖两类方法。软件消抖是在嵌入式系统中,检测到按键按下时,应用软件延时20ms后再次检测按键的状态,如果两次状态相同,则确认按键已经按下。
这种处理方式虽然简单,但是会浪费CPU资源。
硬件消抖有多种方法。第一种方法是应用施密特威廉希尔官方网站 的回差特性配合积分威廉希尔官方网站 实现按键消抖,应用威廉希尔官方网站 如图2所示。
图2 应用积分威廉希尔官方网站 实现按键消抖
第二种方法是应用锁存器的保持功能实现开关消抖,应用威廉希尔官方网站 如图3所示。
图3 应用锁存器实现开关消抖
除了上述两种按键消抖方法外,在基于FPGA的数字系统设计中,也可以应用状态机设计按键消抖威廉希尔官方网站 ,在FPGA内部实现。
基于状态机设计按键消抖威廉希尔官方网站
时,需要将按键的一次动作分解为:按下前、按下时、稳定期和释放时4个状态,如图1中所示,分别用KEY_IDLE、KEY_PRESSED、KEY_ACTIVE和KEY_RELEASE表示。
设按键输入用key_in表示,低电平有效,设计消抖时间为20ms,则按键消抖状态机的状态转换关系如图4所示。
图4 按键消抖状态机
根据上述状态转换关系,描述按键消抖模块的Verilog HDL代码参考如下:
module KEY_debounce #(parameter DEBOUNCE_TIME = 1000_000 )( // 50MHz时钟时,对应消抖时间为20ms input clk_50, // 50MHz时钟,周期为20ns input rst_n, // 复位信号 input key_in, // 按键输入 output reg key_out // 消抖后输出 ); // 内部状态定义,循环编码方式 localparam KEY_IDLE = 2'b00, // 按下前 KEY_PRESSED = 2'b01, // 按下时 KEY_ACTIVE = 2'b11, // 稳定期 KEY_RELEASE = 2'b10; // 释放时 // 内部变量定义 reg [19:0] debounce_cnt; // 消抖计数变量 reg [1:0] current_state,next_state; // 现态和次态 reg [0:1] keytmp; // 同步寄存器 // 内部线网定义 wire cnt_en,cnt_end; // 计数允许和停止计数标志 wire cnt_flag; // 消抖计数标志 wire release_flag; // 按键释放标志 // 允许消抖计数逻辑:按键按下时或者释放时,cnt_en有效。 assign cnt_en = current_state == KEY_PRESSED || current_state == KEY_RELEASE; // 停止计数标志:cnt_en有效并且debounce_cnt达到最大值,则cnt_end有效。 assign cnt_end = cnt_en && ( debounce_cnt == DEBOUNCE_TIME - 1 ); // 正在计数标志:cnt_en有效并且debounce_cnt未达到最大值,则cnt_flag有效。 assign cnt_flag = cnt_en && ( debounce_cnt < DEBOUNCE_TIME ); // 按键已释放标志: cnt_end有效,并且keytmp[1]为高电平,则release_flag有效。 assign release_flag = cnt_end && keytmp[1]; // 按键输入两级同步寄存过程,以消除亚稳态。 always @(posedge clk_50 or negedge rst_n) if ( !rst_n ) keytmp <= 2'b00; // 清零 else keytmp[0:1] <= {key_in,keytmp[0]}; // 右移 // 时序逻辑过程,描述状态转换 always @(posedge clk_50 or negedge rst_n) if ( !rst_n ) current_state <= KEY_IDLE; else current_state <= next_state; // 组合逻辑过程,定义次态 always @(*) begin case ( current_state ) KEY_IDLE: if ( !keytmp[1] ) // 按键按下时,进入KEY_PRESSED next_state = KEY_PRESSED; else // 否则,保持KEY_IDLE next_state = current_state; KEY_PRESSED: if ( cnt_end && !keytmp[1] ) // 消抖时间到且keytmp[1]为0,确认按下有效 next_state = KEY_ACTIVE; else if ( cnt_flag && keytmp[1] ) // 正在计数,但keytmp[1]为1,则为抖动 next_state = KEY_IDLE; else // 否则状态保持 next_state = current_state; KEY_ACTIVE: if ( keytmp[1] ) // keytmp[1]跳变为1则进入释放状态 next_state = KEY_RELEASE; else next_state = current_state; // 否则状态保持 KEY_RELEASE: if ( release_flag ) // 按键已释放,返回 next_state = KEY_IDLE; else if ( cnt_flag && !keytmp[1] ) // 正在计数,但keytmp[1]为0,则为抖动 next_state = KEY_ACTIVE; else // 否则状态保持 next_state = current_state; default: next_state = KEY_IDLE; endcase end // 时序逻辑过程,消抖计时 always @( posedge clk_50 or negedge rst_n ) if ( !rst_n ) debounce_cnt <= 20'b0; else if ( cnt_en ) // 计数允许信号有效 if ( cnt_end ) // 消抖时间到 debounce_cnt <= 20'b0; else // 消抖时间未到 debounce_cnt <= debounce_cnt + 1'b1; else // 计数允许信号无效 debounce_cnt <= 20'b0; // 时序逻辑过程,按键消抖后输出 always @( posedge clk_50 or negedge rst_n ) if ( !rst_n ) key_out <= 1'b1; else case ( current_state ) KEY_IDLE : key_out <= 1'b1; KEY_PRESSED: key_out <= 1'b1; KEY_ACTIVE : key_out <= 1'b0; KEY_RELEASE: key_out <= 1'b0; default: key_out <= 1'b1; endcase endmodule
对上述代码进行仿真验证时,需要建立testbench文件,应用系统函数$random产生随机数,以模拟不规则的抖动脉冲间隔。
应用系统函数$random产生随机整数的语法格式为
num = $random%b其中b为十进制整数,num为-(b-1) ~ (b-1)范围内的随机整数。 应用系统函数$random产生随机正整数的语法格式为
num ={$random}%b其中b为十进制整数,num为0 ~ (b-1)范围内的随机整数。 测试按键消抖模块功能的testbench代码参考如下:
`timescale 1ns/1ps module KEY_debounce_vlg_tst(); reg clk; reg rst_n; reg key_in; wire key_out; // 模块参数重定义,减少计数容量,以缩短仿真时间 defparam KEY_debounce.DEBOUNCE_TIME = 50000; // 内部变量定义 reg [15:0] rand_num; // 仿真参数定义 parameter RESET_TIME = 2, STEP = 5; // 按键消抖模块例化 KEY_debounce i1 ( .clk (clk), .rst_n (rst_n), .key_in (key_in), .key_out (key_out)); // 设置复位信号波形 initial begin rst_n = 1; #1; rst_n = 0; #(STEP * RESET_TIME); rst_n = 1; end // 设置按键输入 initial begin #1; key_in = 1; //按下前 #(STEP * 10); press_key; // 第1次按键过程 #10_000; press_key; // 第2次按键过程 end // 设置时钟信号 initial clk_50 = 0; always #(STEP/2) clk_50 = ~clk_50; // 监测任务 initial $monitor($time,"clk_50=%b rst_n=%b key_in=%b key_out=%b", clk_50,rst_n,key_in,key_out); // 按键任务定义 task press_key; begin repeat (20) begin // 模拟前沿抖动过程 rand_num = {$random}%5000; #rand_num key_in = ~key_in; end key_in = 0; #300_000; repeat (20) begin // 模拟后沿抖动过程 rand_num = {$random}%5000; #rand_num key_in = ~key_in; end key_in = 1; #300_000; end endtask endmodule
上述代码中使用了defparam语句用于对KEY_debounce模块中的DEBOUNCE_TIME参数进行重定义,在确保功能验证的前提下缩短消抖时间。启动modelsim进行仿真,结果如图5所示。
图5 按键消抖模块仿真波形
从波形图中可以看出,消抖威廉希尔官方网站 对按键按下和释放产生的4次抖动都能实现有效消抖,因此验证基于状态机设计的按键消抖模块功能正确。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !