【FPGA学习】按键消抖

        上面的时序图 大致描述了按键消抖的主要思路:由key_r1和key_r2形成两个边沿触发信号上升沿(cnt_pos)和 下降沿信号(cnt_neg),计数器cnt根据cnt_pos和cnt_neg来判断开始计数和终止计数。当计数器记满20ms后产生key_flag信号,并且key_state跳转低电平,整个前抖动滤除动作完成,后抖动同样道理。

边沿检测代码

        以下降沿为例:assign key_neg = ~r1_key & r2_key;就是将r1_key取反这样就会和r2_key‘与’出key_neg ,即检测到下降沿。上升沿同理。

代码

always @(posedge clk or negedge rst_n)
		if(!rst_n)begin
			r1_key <= 1'b0;
			r2_key <= 1'b0;
		end
		else begin 
			r1_key<= key_in;
			r2_key <= r1_key;
		end
		
	assign nedge = !r1_key && r2_key ;
	assign pedge =  r1_key && (!r2_key );

消除亚稳态

         由于按键信号与系统时钟为异步信号 故在使用时需要将其同步。即利用D触发器的延迟性进行延迟两拍以下是相应代码,与边沿检测代码大致相似。

always @(posedge clk or negedge rst_n)
		if(!rst_n)begin
			key_in_s0 <= 1'b0;
			key_in_s1 <= 1'b0;
			end
			else begin
				key_in_s0 <= key_in;
				key_in_s1 <= key_in_s0;
			end

即key_in_s1就是消除亚稳态后的输入信号。

状态机

 代码

always @(posedge clk or negedge rst_n)	
		if(!rst_n) begin
			state <= idle;
			cnt_en <= 1'b0;
			key_state <= 1'b1;
			key_flag <= 1'b0;//稳定标志 通知外部读取key_state
			
		end
		else begin
			case (state)
				idle:
					begin
						key_flag <= 1'b0;
						if(nedge)begin
							state <= filter0;
							cnt_en <= 1'b1;
						end
					
						else 
							state <= idle;
					end
				filter0:
					if(cnt_full)begin
						key_flag <= 1'b1;
						state <= down;
						cnt_en <= 1'b0;
						key_state <= 1'b0;
						
					end
					else if (pedge)begin
						state <= idle;
						cnt_en <= 1'b0;
					end
					else 
						state <= filter0;
				
				down:
					begin
						key_flag <= 1'b0;
						if(pedge)begin
							state <= filter1;
							cnt_en <= 1'b1;
						end
						else 
							state <= down;
					end
					
				filter1:
					
					if(cnt_full)begin
						state <= idle;
						
						key_state <= 1'b1;
						key_flag <= 1'b1;
					end
					else if (nedge)begin
						state <= down;//idle
						cnt_en <= 1'b0;
					end
					else 
						state <= filter1;
					default:
						begin
							state <=idle;
							key_flag <= 1'b0;
							key_state <= 1'b1;
							cnt_en <= 1'b0;
						end
				endcase
			
		end
		
	always @(posedge clk or negedge rst_n)	
		if(!rst_n)
			cnt <= 20'd0;
		else if(cnt_en == 1'b1)
			cnt <= cnt + 1'b1;
		else
			cnt <= 20'd0;
			
	always @(posedge clk or negedge rst_n)	
		if(!rst_n)
			cnt_full <= 1'b0;
		else if(cnt == 999_999)
			cnt_full <= 1'b1;
		else 
			cnt_full <= 1'b0;
			
			
			
endmodule 

testbench


`timescale 1ns/1ns
module key_filter_tb();
	reg clk;
	reg rst_n;
	reg key_in;
		
	wire key_flag;
	wire key_state;
	//wire key_state;
	parameter clock = 20;
	
	key_filter u_key_filter(
	.clk(clk),
	.rst_n(rst_n),
	.key_in(key_in),
	.key_flag(key_flag),
	.key_state(key_state)
	
	);
	key_model key_model(.key(key_in));
	initial clk = 0;
	always #(clock/2) clk = ~clk;
		rst_n =1'b0;
		key_in = 1'b1;
		#(3*clock + 5)
		rst_n = 1'b1;
		press_key;
	$stop();

	end
	integer i;
	task press_key;
		begin
			repeat(15)begin
				i = {$random}%10_0000;
				#(i*clock )
				key_in = ~key_in;
				end
				key_in = 1'b0;
				#(110_0000*clock)
			
			repeat(26)begin
				i = {$random} % 10_0000;
				#(i*clock) 
				key_in = ~key_in;
				end
				key_in = 1'b1;
				#(110_0000*clock);
		end
	endtask
endmodule 

效果

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值