FPGA(八)---按键消抖

一、按键状态

FPGA(八)---按键消抖

按键未按下时:空闲态,FPGA的引脚通过上拉电阻置为高电平。
按键按下时:FPGA的引脚接地,变为低电平。
**在按键按下和释放的过程中:**会出现一段时间的抖动,可以通过示波器观察抖动时间,不超过20ms(如果是50M的晶振,就会经过多个时钟周期,前一个时钟是高电平,下一个可能就是低电平,也就是会出现抖动的现象,这是我们不想要的)
FPGA(八)---按键消抖
IDEL:空闲态
DOWN:按下状态

***消抖原理:***当按键按下时,检测到下降沿进入FILTER,设置FILTER时间(信号抖动的最大时间)为20ms,也就是说20ms内检测到的上升沿下降沿的抖动不计入引脚状态,直到抖动的最后一次一定是下降沿(因为按下是低电平),然后等待20ms时间到来,就认为信号稳定了,后面同理。

下图为时序逻辑图:
FPGA(八)---按键消抖
**边沿检测:**前一个时刻低电平,后一个时刻高电平,那么就一定有上升沿。
reg1记录前一个时刻的状态,reg0记录后一个时刻的状态。reg0为‘0’时,经取反之后为‘1’,reg1为‘1’时,与门(两者为1,输出才为1)输出为才为‘1’,检测到上升沿。
FPGA(八)---按键消抖
!是逻辑取反,将不是0的数变为0,0110->0
~是按位取反,每一个bit都取反,0110->1001

二、代码

FPGA(八)---按键消抖
在这里,可以查看状态图。
FPGA(八)---按键消抖

module key_filter(Rst_n,Clk,key_in,key_flag,key_state);
	input Rst_n,Clk,key_in;
	output reg key_flag,key_state;//在always@中进行赋值就要加上reg
	
	localparam  
		IDEL    =4'b0001,
		FILTER0 =4'b0010,
		DOWN    =4'b0100,
		FILTER1 =4'b1000;
		
	reg [3:0]state;   //表示上述的四种状态
	//寄存器类型必须放在最前面
	reg key_tmp0,key_tmp1; //边沿检测(前一个时刻高电平,下一个时刻低电平,就是出现了下降沿)
	reg [19:0]cnt; 
	reg en_cnt; //使能计数器,检测到下降沿,开始计数	
	reg cnt_full; //计数满的标志位
	
	wire pedge,nedge;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		key_tmp0 <= 1'b0;
		key_tmp1 <= 1'b0;
	end
	else begin
		key_tmp0 <= key_in;
		key_tmp1 <= key_tmp0;	
	end
	
	assign nedge = !key_tmp0 & key_tmp1;// !是逻辑取反,将不是0的数变为0,0110->0
	assign pedge = key_tmp0 & (!key_tmp1);// ~是按位取反,每一个bit都取反,0110->1001
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		en_cnt <= 1'b0;
		state <= IDEL;   //默认状态
		key_flag <= 1'b0;
		key_state <= 1'b1;
		end
	else begin
		case(state)
			IDEL :
				if(nedge)begin
					state <= FILTER0;
					en_cnt <= 1'b1;
					end
				else
					state <= IDEL;
			FILTER0:
				if(cnt_full)begin
					state <= DOWN;
					en_cnt <= 1'b0;
					key_flag <= 1'b1;
					key_state <= 1'b0;
					end
				else if(pedge)begin //检测到上升沿
					state <= IDEL;
					//en_cnt <= 1'b0;
					end
				else 
					state <= FILTER0;
			DOWN:
				begin
				key_flag <= 1'b0;
				if(pedge)begin
					state <= FILTER1;
					en_cnt <= 1'b1;
					end
				else
					state <= DOWN;		
				end	
			FILTER1:
				if(cnt_full)begin
					en_cnt <= 1'b0;
					state <= IDEL;
					key_state <= 1'b1;
					end
				else if(nedge)
					state <= DOWN;
					//en_cnt <= 1'b0;
				else 
					state <= FILTER1;	
			default: begin//独热编码只表示4种状态,还有其他状态。
				state <= IDEL;
				en_cnt <= 1'b0;
				key_state <= 1'b1;
				key_flag <= 1'b0;
				end
			endcase
		end
		
	//20ns的晶振
	//20ms / 20ns = 1000000 共需要20bit
	always@(posedge Clk or negedge Rst_n)//计数
	if(!Rst_n)
		cnt <= 20'd0;
	else if(en_cnt)
		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 == 20'd999999)
		cnt_full <= 1'b1;
	else
		cnt_full <= 1'b0;
endmodule
`timescale 1ns/1ps
`define clock_period 20

module key_filter_tb;
	
	reg Rst_n;
	reg Clk;
	reg key_in;
	wire key_flag;
	wire key_state;
	
	key_filter filter1(
	.Rst_n(Rst_n),
	.Clk(Clk),
	.key_in(key_in),
	.key_flag(key_flag),
	.key_state(key_state)
	);
	
	initial Clk = 1'b1;
	always#(`clock_period/2) Clk = ~ Clk;
	
	initial begin 
		Rst_n = 1'b0;
		key_in = 1'b0;
		#(`clock_period*20+1) Rst_n = 1'b1;
		#(`clock_period*20+1) 
		press_key;
		#10000;
		press_key;
		#10000;
		$stop;
	end	
	
	//改进
	reg [15:0]myrand;
	task press_key;
		begin
			repeat(50)begin
				myrand = {$random}%65536;//产生随机数
				#myrand key_in = ~key_in;
			end
			key_in = 0;//按下时,最后状态为0
			#50000000;//20ms内是抖动状态,20-50ms是按下状态
			
			repeat(50)begin
				myrand = {$random}%65536;//产生随机数
				#myrand key_in = ~key_in;
			end
			key_in = 1;//释放时,最后状态为1
			#50000000;
		end
	endtask
	
endmodule

三、结果图

FPGA(八)---按键消抖

四、仿真模型module

FPGA(八)---按键消抖
(1)module类似于一个函数,就是进行了一次封装。
(2)module中不写时钟和复位,时钟和复位写在tb文件中
(3)FPGA(八)---按键消抖
(4)在仿真时,必须把自己写的module和tb文件都加上。
FPGA(八)---按键消抖

`timescale 1ns/1ps
module key_module(key);
	
	output reg key;
	
		initial begin 
		key = 1'b0;   //在module中,不需要时钟和复位
		press_key;
		#10000;
		press_key;
		#10000;
		$stop;
	end	
	
	reg [15:0]myrand;
	task press_key;
		begin
			repeat(50)begin
				myrand = {$random}%65536;//产生随机数
				#myrand key = ~key;
			end
			key = 0;//按下时,最后状态为0
			#50000000;//20ms内是抖动状态,20-50ms是按下状态
			
			repeat(50)begin
				myrand = {$random}%65536;//产生随机数
				#myrand key = ~key;
			end
			key = 1;//释放时,最后状态为1
			#50000000;
		end
	endtask
	
endmodule
`timescale 1ns/1ps
`define clock_period 20

module key_module_tb;
	
	reg Rst_n;
	reg Clk;
	//reg key_in;
	wire key_in; //当key_in进行连接时,用wire类型
	wire key_flag;
	wire key_state;
	
	key_filter filter1(
	.Rst_n(Rst_n),
	.Clk(Clk),
	.key_in(key_in),
	.key_flag(key_flag),
	.key_state(key_state)
	);
	
	key_module mod0(.key(key_in));
	
	initial Clk = 1'b1;
	always#(`clock_period/2) Clk = ~ Clk;
	
	initial begin 
		Rst_n = 1'b0;
		//key_in = 1'b0;
		#(`clock_period*20+1) Rst_n = 1'b1;
		#(`clock_period*20+1); 	
	end	
endmodule
上一篇:FPGA——串口通信——使用三状态的状态机实现任意字节的数据发送


下一篇:VIVADO 按键消抖