Augus

14.菜鸟初入FPGA之按键消抖(二段式状态机)

0
阅读(2868)

            按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开

            消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响

结合上节笔记的边沿检测,我们看一下按键消抖例程

代码:

module Key_filter(
						clk,
						rst_n,
						key_in,
						key_flag,
						key_state
						);
						
	input clk,rst_n;
	input key_in;
	
	output reg key_state;//按键状态
	output reg key_flag;//按键标志输出
		
//	localparam 
//			idel    = 4'b0001,  //空闲态
//			filter0 = 4'b0010,  //抖动滤波
//			down    = 4'b0100,  //按下稳定态
//			filter1 = 4'b1000;
			
////////////////处理外部输入信号////////////////////////	
	reg key_in1,key_in2;//
	//外部输入的异步信号同步化 消除亚稳态
	always @(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_in1 <= 1'b0;
		key_in2 <= 1'b0;
	end
	else begin
		key_in1 <= key_in;
		key_in2 <= key_in1;
	end
	
	reg key_tmp1,key_tmp2;
	//使用两级寄存器,边沿检测
	always @(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_tmp1 <= 1'b0;
		key_tmp2 <= 1'b0;
	end
	else begin
		key_tmp1 <= key_in2;
		key_tmp2 <= key_tmp1;
	end
	
	wire p_edge,n_edge;
	//产生跳变沿信号
	assign n_edge = !key_tmp1 & key_tmp2;
	assign p_edge = key_tmp1 & (!key_tmp2);
	
/////////////////计数信号输出//////////////////////	
	reg [3:0]cnt;
	reg en_cnt;//使能计数
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		cnt <= 4'd0;
	else if(en_cnt)
		cnt <= cnt + 1'b1;
	else if(!en_cnt)
		cnt <= 4'd0;

	//计数满标志
	always @(posedge clk or negedge rst_n)
	if(!rst_n)
		key_flag <= 1'd0;
	else if(cnt == 4'd9)
		key_flag <= 1'b1;
	else
		key_flag <= 1'd0;

////////////////状态机模块///////////////////////	

	reg [3:0]state,NS;//两段式状态机
	always @(posedge clk or negedge rst_n)
	if(!rst_n) begin
		state <= 4'b1;
	end
	else begin
		state <= NS;
	end
	
	always @(state or n_edge or p_edge or key_flag)
	begin
		//en_cnt <= 1'b0;
		key_state <= 1'b1;
		case(state)
				4'd1	:	begin
									if(n_edge)begin
										en_cnt <= 1'b1;
										NS <= 4'd2;
									end
									else
										NS <= 4'd1;
								end
						
				4'd2	:	begin
								if(key_flag)begin
									NS <= 4'd3;
									en_cnt <= 1'b0;
								end
								else
									NS <= 4'd2;
							end
							
				4'd3	:	begin
									key_state <= 1'b0;
									if(p_edge)begin
										en_cnt <= 1'b1;
										NS <= 4'd4;
									end
									else
										NS <= 4'd3;
								end
							
				4'd4	:	begin
									if(key_flag)begin
										en_cnt <= 1'b0;
										key_state <= 1'b1;
										NS <= 4'd1;
									end
									else
										NS <= 4'd4;
								end
			default	:	begin
								NS <= 4'd1;
								en_cnt <= 1'b0;
								key_state <= 1'b1;
							end
		endcase
			
	end
endmodule


`timescale 1ns/1ns

`define clk_period 20

module Key_tb;

	reg clk;
	reg rst_n;
	reg key_in;

	wire key_flag;
	wire key_state;
	
	Key_filter key_filter0(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	initial clk = 1;
	always #(`clk_period/2)clk = ~clk;
	
//	always @(posedge clk)//随机函数使用
//	begin
//		#(`clk_period*100); 
//		key_in ={$random}%2;
//	end
	initial begin
		rst_n = 0;
		key_in = 1;
		#(`clk_period*5);
		rst_n = 1;
		#(`clk_period + 1);
		press_key;
		#(`clk_period*200);
		press_key;
		#(`clk_period*200);
		press_key;
		#(`clk_period*500);
		$stop;
	end
	
 	task press_key;//任务函数的使用
 		begin
 			#500 key_in = 0;
 			#500 key_in = 1;
 			#500 key_in = 0;
 			#500 key_in = 1;
 			#500 key_in = 0;
 			#500 key_in = 1;
 			#500 key_in = 0;
 			#500 key_in = 1;
 			#500 key_in = 0;
 			#500 key_in = 1;
 			#500 key_in = 0;
 			#500 key_in = 1;	
 		end
 	endtask
endmodule

仿真波形:

2016-04-10_171400.jpg

关于仿真模型

此时可以使用仿真模型,Augus会使用...但是讲不明白==,很伤情的

   需要了解的请查看

    发烧友小梅哥专版 http://bbs.elecfans.com/zhuti_fpga_1.html

    视频讲的很容易懂