/********************************Copyright************************************** **----------------------------File information--------------------------** File name :key_shake.v ** CreateDate :2015.03** Funtions : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,之后输出按键输出.** Operate on :M5C06N3L114C7** Copyright :All rights reserved[F]. ** Version :V1.0**---------------------------Modify the file information----------------** Modified by :** Modified data : ** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。*******************************************************************************/ module key_shake ( clk_100M, rst_n, key_in, key_out ); input clk_100M; //100Mhz input rst_n; input key_in; output key_out; //-------------------------------------- //在复位之后的100us内,不响应按键的操作 localparam t_100us = 14'd9999; localparam t1ms = 17'd99999; //定时1ms localparam t_20ms = 5'd20; reg [13:0] cnt; reg key_en; //复位之后允许按键输入标志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt <= 0; key_en <=0; end else begin if(cnt == t_100us) begin key_en <= 1; end else begin key_en <= 0; cnt <= cnt + 1; end end end //-------------------------------------------------- wire HtoL_flag; //下降沿标志 wire LtoH_flag; //上升沿标志 reg [2:0] key_reg; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_reg <= 3'b111; //默认没按下状态为高,按下之后为低.反之则为3'b000; end else begin key_reg <= {key_reg[1:0],key_in}; end end assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0; //下降沿检测,一个时钟的高电平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0; //上升沿检测,一个时钟的高电平 //--------------------------------------------- reg cnt_en; //计数使能标志 reg [16:0] cnt2; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt2 <= 17'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin cnt2 <= 17'd0; end else if(cnt_en) begin cnt2 <= cnt2 + 17'd1; end else cnt2 <= 17'd0; end reg [4:0] cnt3; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt3 <= 5'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin if(cnt3 == t_20ms ) cnt3 <= t_20ms; else cnt3 <= cnt3 + 1; end else if(!cnt_en) cnt3 <= 5'd0; end //----------------------------------//按键状态机 reg [2:0] i; reg key_down; //按键按下标志 reg key_up; //按键释放标志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_down <= 0; key_up <= 0; i <= 0; cnt_en <= 0; end else begin case(i) 'd0: begin key_down <= 0; key_up <= 0; if(HtoL_flag) i <= 'd1; //检测到按下 else if(LtoH_flag) i <= 'd2; //检测到释放按键 else i <= 'd0; end 'd1: begin if(cnt3 == t_20ms ) begin if(!key_in) //检测到按键依然被按下 begin key_down <= 1; //按键按下成功 i <= 'd3; cnt_en <= 0; end else begin key_down <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd2: begin if(cnt3 == t_20ms ) begin if(key_in) //检测到按键被释放 begin key_up <= 1; //按键释放成功 i <= 'd3; cnt_en <= 0; end else begin key_up <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd3: begin key_up <= 0; key_down <= 0; i <= 'd0; end default:i <= 'd0; endcase end end assign key_out = key_down; //当按键被按下有效时// assign key_out = key_up; //当按键被释放后才有效时endmodule
测试代码如下:
/********************************Copyright************************************** **----------------------------File information--------------------------** File name :key_testbench.v ** CreateDate :2015.03** Funtions :按键消抖的测试文件** Operate on :M5C06N3L114C7** Copyright :All rights reserved. ** Version :V1.0**---------------------------Modify the file information----------------** Modified by :** Modified data : ** Modify Content:*******************************************************************************/ module key_testbench; reg clk; reg rst_n; reg key_in; wire key_out; key_shake key_shake_1( .clk, .rst_n, .key_in, .key_out ); localparam tck = 24;localparam t = 1000/tck;always #(t/2) clk = ~clk;task key_in_down; begin #(3*t) key_in = 1; #(3*t) key_in = 0; #(8*t) key_in = 1; #(8*t) key_in = 0; #(13*t) key_in = 1; #(13*t) key_in = 0; end endtask task key_in_up; begin #(3*t) key_in = 0; #(3*t) key_in = 1; #(8*t) key_in = 0; #(8*t) key_in = 1; #(13*t) key_in = 0; #(13*t) key_in = 1; endendtask initial begin clk = 0; rst_n = 0; key_in = 1; #(100*t) rst_n = 1; #(100*t); #(10*t) key_in_down; #(100*t); #(10*t) key_in_up; #(8000*t); #(10*t) repeat(2) key_in_down; //按下时抖动 #(640000*t); //按下时间 #(10*t) repeat(2) key_in_up; //释放时抖动 end endmodule
仿真结果:
1、在复位之后100us之前按下按键时,不响应。
2、抖动(按下后20ms之内释放)。
如果将按键按下有效时刻、按键释放有效时刻和按键所处状态全部表现出来,则代码稍作修改即可:
/********************************Copyright************************************** **----------------------------File information--------------------------** File name :key_shake.v ** CreateDate :2015.03** Funtions : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,检测,然后松开时也有20ms的检测,之后输出按键输出.** Operate on :M5C06N3L114C7** Copyright :All rights reserved[F]. ** Version :V1.0**---------------------------Modify the file information----------------** Modified by :** Modified data : ** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。*******************************************************************************/ module key_shake ( clk, rst_n, key_in, key_down_out, key_up_out, key_in_out ); input clk; //24Mhz input rst_n; input key_in; output key_down_out; //按下输出 output key_up_out; //释放输出 output key_in_out; //跟随输入输出 //-------------------------------------- //在复位之后的100us内,不响应按键的操作 parameter t_20ms = 5'd20; `define CLK_20M// `define CLK_24M// `define CLK_50M `ifdef CLK_20M parameter t_100us = 12'd1999; parameter t1ms = 16'd19999; //定时1ms `endif `ifdef CLK_20M parameter t_100us = 12'd2399; parameter t1ms = 16'd23999; //定时1ms `endif `ifdef CLK_20M parameter t_100us = 13'd4999; parameter t1ms = 16'd49999; //定时1ms `endif reg [12:0] cnt; reg key_en; //复位之后允许按键输入标志 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 0; key_en <=0; end else begin if(cnt == t_100us) begin key_en <= 1; end else begin key_en <= 0; cnt <= cnt + 1; end end end //-------------------------------------------------- wire HtoL_flag; //下降沿标志 wire LtoH_flag; //上升沿标志 reg [2:0] key_reg; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_reg <= 3'b111; //默认没按下状态为高,按下之后为低.反之则为3'b000; end else begin key_reg <= {key_reg[1:0],key_in}; end end assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0; //下降沿检测,一个时钟的高电平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0; //上升沿检测,一个时钟的高电平 //--------------------------------------------- reg cnt_en; //计数使能标志 reg [15:0] cnt2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt2 <= 16'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin cnt2 <= 16'd0; end else if(cnt_en) begin cnt2 <= cnt2 + 16'd1; end else cnt2 <= 16'd0; end reg [4:0] cnt3; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt3 <= 5'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin if(cnt3 == t_20ms ) cnt3 <= t_20ms; else cnt3 <= cnt3 + 1; end else if(!cnt_en) cnt3 <= 5'd0; end //----------------------------------//按键状态机 reg [2:0] i; reg key_down; //按键按下标志 reg key_up; //按键释放标志 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_down <= 0; key_up <= 0; i <= 0; cnt_en <= 0; end else begin case(i) 'd0: begin key_down <= 0; key_up <= 0; if(HtoL_flag) i <= 'd1; //检测到按下 else if(LtoH_flag) i <= 'd2; //检测到释放按键 else i <= 'd0; end 'd1: begin if(cnt3 == t_20ms ) begin if(!key_in) //检测到按键依然被按下 begin key_down <= 1; //按键按下成功 i <= 'd3; cnt_en <= 0; end else begin key_down <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd2: begin if(cnt3 == t_20ms ) begin if(key_in) //检测到按键被释放 begin key_up <= 1; //按键释放成功 i <= 'd3; cnt_en <= 0; end else begin key_up <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd3: begin key_up <= 0; key_down <= 0; i <= 'd0; end default:i <= 'd0; endcase end end//--------------------------- reg key_out; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_out <= 1; end else begin if(key_down) key_out <= 0; //按下为低 else if(key_up) key_out <= 1; //释放为高 else key_out <= key_out; //否则保持 end end assign key_down_out = key_down; //当按键被按下有效时 assign key_up_out = key_up; //当按键被释放后才有效时 assign key_in_out = key_out;endmodule
仿真:
联系客服