通过两段式状态机实现FPGA按键消抖

在电子产品中我们会经常用到按键,比如电脑的键盘,手机的按键等等,按键就是人机交互的一种工具。

1. 按键的硬件电路

在这里插入图片描述

2. 按键抖动

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,电压信号小型如下图。由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,如下图。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。
在这里插入图片描述
我们需要对都抖动的按键信号处理后才可以再输入FPGA中,经过滤波后的信号应该如图下所示:
在这里插入图片描述

3. 按键消抖的状态转换图

在这里插入图片描述
按键没有按下时处于空闲状态:IDEL
按下抖动时处于滤除状态:FILTER0
按下稳定状态:DOWN
释放抖动处于滤除状态:FILTER1
nedge:为下降沿信号、pedge为上升沿信号
en_cnt:计数器的使能信号
cnt_full_flag计时器记到20ms的标记信号。

4.代码实现

module key(
	input	wire	sclk,
	input	wire	rst_n,
	input	wire	key,
	output	reg		key_flag,
	output	reg		key_value
);

parameter				IDEL		= 4'b0001;
parameter				FILTER0		= 4'b0010;
parameter				DOWN		= 4'b0100;
parameter				FILTER1 	= 4'b1000;

reg 	key_in_sa,key_in_sb;
reg 	key_tmpa,key_tmpb;
wire 	pedge,nedge;//跳变沿信号
reg  	en_cnt;//使能计数器
reg	 	cnt_full_flag;//计数满标志
reg		[3:0]	state;//状态
reg		[19:0]	cnt;//计数器

always @(posedge sclk or negedge  rst_n)//将输入的异步信号进行同步处理
	if(~rst_n)begin 
	key_in_sa<=1'b0;
	key_in_sb<=1'b0;
	end 
	else begin 
	key_in_sa<=key;
	key_in_sb<=key_in_sa;
	end
	
	always @(posedge sclk or negedge  rst_n)//使用D触发器存储两个相邻时钟上升沿时外部输入信号的电平
	if(~rst_n)	
	begin 
	key_tmpa<=1'b0;
	key_tmpb<=1'b0;
	end 
	else begin 
	key_tmpa<=key_in_sb;
	key_tmpb<=key_tmpa;
	end
	
assign  nedge=(~key_tmpa) & key_tmpb;
assign	pedge=key_tmpa & (~key_tmpb);//产生跳变沿信号



always @(posedge sclk or negedge  rst_n)//状态迁移
	if(~rst_n)
		begin
		state<=IDEL;
		en_cnt<=1'b0;
		end
	else begin
		case(state)
		IDEL:if(nedge==1'b1) begin
			  state<=FILTER0;
			  en_cnt<=1'b1;
			  end
			  else  begin
			  state<=IDEL;
			  en_cnt<=1'b0;
			  end
			  
		FILTER0: if(cnt_full_flag==1'b1) 
			  begin
			  state<=DOWN;
			  en_cnt<=1'b0;
			  end
			  else  if(pedge) begin
			  state<=IDEL;
			  en_cnt<=1'b0;
			  end
			  else begin
			  state<=FILTER0;
			  en_cnt<=1'b1;
			  end
			  
		DOWN: if(pedge==1'b1) begin
			  state<=FILTER1;
			  en_cnt<=1'b1;
			  end
			  else	begin
			  state<=DOWN;
			  en_cnt<=1'b0;
			  end
			  
		FILTER1: if(cnt_full_flag==1'b1) 
				begin
				state<=IDEL;
				en_cnt<=1'b0;
				end
			  else  if(nedge) 
					begin
					state<=DOWN;
					en_cnt<=1'b0;
					end
						else 
						begin
						state<=FILTER1;
						en_cnt<=1'b01;
						end
		default: begin
		         state<=IDEL;
				 en_cnt<=1'b0;
				 end
		endcase 
	end 
always @(posedge sclk or negedge  rst_n)//key_flag的输出状态
	if(~rst_n) 
		key_flag=1'b0;
	else
		if(cnt_full_flag==1&& state==FILTER0)
		key_flag<=1'b1;
	else
		if(cnt_full_flag==1&& state==FILTER1)
		key_flag<=1'b1;
	else
		key_flag<=1'b0;
	
always @(posedge sclk or negedge  rst_n)//key_value的输出状态
	if(~rst_n) 
		key_value=1'b1;
	else
		if(cnt_full_flag==1&& state==FILTER0)
		key_value <=1'b0;
		else
		if(state==DOWN)
		key_value <=1'b0;
		else
		if(state==DOWN && pedge)
		key_value <=1'b0;
		else
		if(state==FILTER1 && nedge)
		key_value <=1'b0;
		else
		if(state==FILTER1 && cnt_full_flag==0)
		key_value<=1'b0;
		else
		key_value=1'b1;
		
always @(posedge sclk or negedge  rst_n)//计数器
	if(~rst_n)
		cnt<='d0;
	else
		if(en_cnt)
		cnt<=cnt+1'b1;
		else
		cnt<='d0;

always @(posedge sclk or negedge  rst_n)//计数20ms满标志
	if(~rst_n)
		cnt_full_flag<=1'b0;
	else
		if(cnt==20'd999_999)
		cnt_full_flag<=1'b1;
		else
		cnt_full_flag<=1'b0;

endmodule 

5testbench代码

`timescale 1ns/1ps
`define		clk_period    20
module  key_tb1;

reg		sclk;
reg		rst_n;
reg		key;
wire	key_value;
wire	key_flag;
reg 	[15:0]  myrand;	

key  key2(
	.sclk		(sclk),
	.rst_n		(rst_n),
	.key		(key),
	.key_flag	(key_flag),
	.key_value	(key_value)
);

initial sclk=1;
always	#(`clk_period/2) sclk=~sclk;

initial
begin
	rst_n=0;
	key=1;
	#(`clk_period*10);
	rst_n=1;
	#(`clk_period*10+1);
	press_key;
	#10000;
	$stop;
	
end

task press_key;
	begin
		repeat(50) begin
			myrand={$random}%65536;
			#myrand;
			key=~key;
		end
			key=0;
			#50_000_000;
			
		repeat(50) begin
			myrand={$random}%65536;
			#myrand;
			key=~key;
		end
			key=1;
			#50_000_000;
	end
endtask

endmodule 

6仿真结果

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值