FPGA基础之按键消抖

在平常的按键应用中,会涉及到对按键状态进行消抖的设计。按键的抖动源于按键的硬件结构,在按键的组成中,有一个反作用弹簧,因为有这个弹簧,才能使按键在手松开后自动回弹。但是也因为这个,造成了在按下和弹起时有多余的电平抖动,按键的按下的实际情况如下:
在这里插入图片描述

图1 按键按下实际波形

如上图所示,在整个按键过程中,按键从稳定到按下之间,因为反作用弹簧,会有一段时间的抖动现象,但这种抖动现象一般都在1ms以内,在释放过程中的抖动现象也基本能够在1ms内趋于稳定。在这种情况下,则可以将按键的按下过程分为几个状态,通过状态的切换来判断当前按键是否成功按下。

状态切换

如上图的按键按下的实际波形,可将按键分为4个状态,分别为空闲状态(IDLE),抖动滤波状态(FILTER),按下状态(DOWN)和释放状态(FREE),按键的按下依次在这四种状态中切换,状态切换如图所示:
在这里插入图片描述

图2 状态切换

如图所示,在无按下状态时,按键一直为空闲状态,等待按键按下;按键按下之后,状态切换为抖动滤波状态,进行状态滤波,直到时间计数值为1ms之后,按键依旧为低,此时切换为按键按下状态;在按键按下后,采集按键的上升沿,采集到上升沿后进行按键释放滤波,在滤波计数结束后,按键仍旧为高,此时按键释放完毕,状态切换为空闲状态。按键状态再这四个状态中切换,完成按键按下的采样判断。

状态实现

按键边沿获取
在空闲状态下,需要获取按键输入得到异步信号,进行采样后,获取按键的边沿信号进行状态切换的判定,按键的边沿信号获取如下:

    reg s0_key_in,s1_key_in;
	reg tmp0_key_in,tmp1_key_in;
	wire key_rsing,key_falling;
	
	always@(posedge clk50m or negedge rst_n)begin
		if(!rst_n)begin
			s0_key_in <= 1'b0;
			s1_key_in <= 1'b0;
		end
		else begin
			s0_key_in <= key_in;
			s1_key_in <= s0_key_in;
		end
	end
	
	always@(posedge clk50m or negedge rst_n)begin
		if(!rst_n)begin
			tmp0_key_in <= 1'b0;
			tmp1_key_in <= 1'b0;
		end
		else begin
			tmp0_key_in <= s1_key_in;
			tmp1_key_in <= tmp0_key_in;
		end
	end
	
	assign key_rsing = tmp0_key_in & (!tmp1_key_in);
	assign key_falling = !tmp0_key_in & tmp1_key_in;

如上所示,首先进行异步信号key_in的时钟域同步,s1_key_in即为异步信号输入后的同步信号,再通过两个寄存器搭建边沿获取电路获取按键边缘。
去抖计数
在按键按下和释放时,需要进行去抖的时间计数,以此来进行按键是否正确按下的判断。使用的输入时钟为50mhz,每一个时钟周期为20ns,因此,1ms的时间需要计数50次,计数代码如下:

reg [5:0]cnt;
reg cnt_en;
reg cnt_full;
always@(posedge clk50m or negedge rst_n)begin
	if(!rst_n)
		cnt <= 6'd0;
	else if(cnt_en)
		cnt <= cnt + 1'b1;
		else
		cnt <= 6'd0;
end
		
always@(posedge clk50m or negedge rst_n)begin
	if(!rst_n)
		cnt_full <= 1'b0;
	else if(cnt == 6'd50)
		cnt_full <= 1'b1;
	else
		cnt_full <= 1'b0;
end

状态切换
根据图2 所示,按键控制的4个状态,当前设计为按键短按,没有按键的长按判断,因此,在按下后不需要进行按下的时间判定,只需在按下又释放之后,输出一个时钟的高电平进行按键按下状态的输出,状态设计如下:

reg [3:0]key_status;
localparam 
	idle = 4'b0001,		//空闲
	filter = 4'b0010,	//按下消抖
	down = 4'b0100,		//按键按下
	free = 4'b1000;		//释放消抖
	
always@(posedge clk50m or negedge rst_n)begin
	if(!rst_n) begin
		key_state = 1'b0;
		key_status = idle;
			cnt_en <= 1'b0;
	end
	else begin
		case(key_status)
			idle: begin
				key_state <= 1'b0;
				if(key_falling)begin
					
					key_status <= filter;
					cnt_en <= 1'b1;
				end
			end
		
			filter: begin
				if(cnt_full)begin
					cnt_en <= 1'b0;
						key_status <= down;
				end
				else if(key_rsing)begin
					cnt_en <= 1'b0;
					key_status <= idle;
				end
				else
					key_status <= filter;
			end
			down: begin
				if(key_rsing)begin
					cnt_en <= 1'b1;
					key_status <= free;
				end
				else
					key_status <= down;
			end
			free:begin
				if(cnt_full)begin
					cnt_en <= 0;
					key_state <= 1'b1;
					key_status <= idle;
				end
				else if(key_falling)begin
					key_status <= down;
				end
				else begin
					key_status <= free;
					cnt_en <= 1'b1;
				end
				end
				default:begin
				cnt_en <= 1'b0;
				key_state <= 1'b0;
				key_status <= idle;
			end
			endcase
		end
	end

如上的设计中,key_state作为按键状态的输出,在完成一次按键操作后,输出一个时钟的高电平。

仿真测试
在完成应用设计后,创建激励文件进行仿真测试。仿真测试中,采用task语句来模拟按键按下的过程,task语句实现如下:

	task press_key1;
		begin
			repeat(20)begin
				my_rand = {$random}%50;
				#my_rand key_in1 = ~key_in1;
			end
			key_in1 = 0;
			#1100;
			
			repeat(20)begin
				my_rand = {$random}%50;
				#my_rand key_in1 = ~key_in1;
			end
			key_in1 = 1;
			#1100;
		end
	endtask

在设计中,key_in作为按键输入,在空闲状态保持低电平,之后重复20次的随机抖动,抖动完成后保持一段时间的低电平,释放时又重复20次的随机抖动,最后完成释放。激励仿真测试的结果如下:
在这里插入图片描述
如图所示,key_in作为按键输入,在完成一次按下操作后,key_state输出一个时钟的高电平用于按键判断。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值