FPGA入门五按键消抖

按键消抖实验

在这里插入图片描述
按键消抖四种状态:
1.高脉冲稳定状态 2.按下消抖状态 3.低电平稳定状态 4.键起消抖状态
按键状态
边沿检测电路:两个寄存器
在这里插入图片描述

编写主逻辑文件

module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
   input Clk;
	input Rst_n;
	input Key_in;
	output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
	output reg Key_data;
	
   reg [3:0]state; 
   reg  [19:0]count; //寄存器计数	
	reg   count_start;//时能计时
	reg  count_full;//计满脉冲信号
	reg reg0,reg1;
	wire rise,fall;
	
	localparam 
	High_pulse      =   4'b0001, //高电平稳定状态
	Low_eliminate   = 4'b0010, //键下降沿稳定状态
	Low_pulse       = 4'b0100, //低电平稳定状态
	High_eliminate  = 4'b1000; //键上升沿稳定状态
	
	always @(posedge Clk or negedge Rst_n)    //脉冲边沿检测   两个寄存器
	 if(!Rst_n) begin
       reg0 <= 1'b0;
		 reg1 <=1'b0;
		 end
      else   begin 
		 reg0 <= Key_in;
	    reg1 <= reg0; 	
	end
	
	assign fall = !reg0 & reg1;  //检测到下降沿
	assign rise = reg0 & !reg1;  //检测到上升沿  
	
	 always @(posedge Clk  or negedge Rst_n)    //20ms计数器
       if(!Rst_n)
          count <= 20'b0;            	
	      else  if(count_start)
			  count <= count + 20'b1;
             else  count <= 20'b0;		  	
				 
	 always @(posedge Clk  or negedge Rst_n)     //20ms计数器计数满标志脉冲信号
       if(!Rst_n)
          count_full <= 1'b0;            	
	      else  if(count == 99_9999)
			  count_full <= 1'b1;
             else  count_full <= 1'b0;	
				 
	always @(posedge Clk or negedge Rst_n)
	 if(!Rst_n) begin
	  Key_data <= 1'b1;
	  state <= High_pulse;
	  Key_flag <= 1'b0;
	  count_start <= 1'b0;
         end		
	  else  case(state) 
	          High_pulse : begin  Key_flag <= 0;  
				                     Key_data <= 1'b1;
				   if(fall) begin
					    state <= Low_eliminate;
						 count_start <= 1'b1;   //使能开始20ms计数
	              end
					  else   state <= High_pulse;
					      end
				 Low_eliminate :  
   				if(count_full) begin
					     state <= Low_pulse;
						  Key_flag <= 1'b1;
				        Key_data <= 1'b0;
						  count_start <= 1'b0;   //关闭使能20ms计数
						 end
					 else begin  
					     if(rise) begin
						 state <= High_pulse;
						 count_start <= 1'b0;   //关闭使能20ms计数
						               end
					      else 
							 state <= Low_eliminate;
						         end
              
              Low_pulse:  begin Key_flag <= 1'b0;
                 if(rise) begin
					    state <= High_eliminate;
						 count_start <= 1'b1; //开始计时
						 end
					  else  state <= Low_pulse;
				            end
			     High_eliminate : 
				     if(count_full) begin
					   Key_data <= 1'b1;
						Key_flag <= 1'b1;
						state <= High_pulse;
					  end
					   else begin
						  if(fall) begin
						   state <= Low_pulse;
							count_start <= 1'b0;
							   end else 
								 state <= High_eliminate;
						            end		
		
		default : begin 
		               state <= High_pulse; //默认状态
							count_start <= 1'b0;
							Key_data <= 1'b1;
					    	Key_flag <= 1'b0;
		     end
	  endcase 

endmodule 

主逻辑文件仿真

`timescale 1ns/1ns
`define period_clock 20
module Key_shake_tb;
   
	reg Clk;
	reg Rst_n;
	reg Key_in;  //高电平为按键默认状态
	wire Key_flag; 
	wire Key_data;
   reg  [15:0]random_time; //寄存器存储不超过20ms的随机时间
Key_shake  Key_shake0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in),
  .Key_flag(Key_flag),
  .Key_data(Key_data)
  );

initial Clk = 1;
always #(`period_clock/2) Clk = ~Clk;
initial begin
    Rst_n = 0;
	 Key_in = 1;
	 #(`period_clock*10);
	 Rst_n = 1;
    #(`period_clock*10);
	 press_key;
	 Key_in = 0;
	 #(`period_clock*1000010);
     press_key;
	  Key_in = 1;
	 #(`period_clock*1010010); 
	 $stop;
	 
	 end

task press_key;  //任务
begin
   repeat(50) begin
  	 random_time = {$random}%65535;   //无{}表示取值范围-65535~65535
 	 #random_time  Key_in = ~ Key_in; 
	end
end 
 endtask

endmodule 

仿真结果

在这里插入图片描述
在消抖稳定后20m产生Key_flag信号和Key_state信号。

仿真模型概念

在这里插入图片描述
Key_shake_tb0模块应为Key_shake0模块,写错。

Key_shake_tb仿真模型模块

`timescale 1ns/1ns
`define period_clock 20
module Key_shake_tb;
   
	reg Clk;
	reg Rst_n;
	wire Key_in;  //高电平为按键默认状态 两个寄存器连接wire型
	wire Key_flag; 
	wire Key_data;
   reg  [15:0]random_time; //寄存器存储不超过20ms的随机时间
Key_shake  Key_shake0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in),
  .Key_flag(Key_flag),
  .Key_data(Key_data)
  );
Key_model  Key_model(
  .Key(Key_in)
      );
initial Clk = 1;
always #(`period_clock/2) Clk = ~Clk;
initial begin
    Rst_n = 0;
	 #(`period_clock*10);
	 Rst_n = 1;
    #(`period_clock*10+1); 
	 end

endmodule 

Key_model模块

模拟产生按键抖动信号

`timescale 1ns/1ns
`define period_clock 20
module Key_model(Key); //按键模型
output  reg  Key;
reg  [15:0]random_time; //寄存器存储不超过20ms的随机时间
initial begin
       Key = 1'b1;
	   press_key;
	 #(`period_clock*1000);
     press_key;
	 #(`period_clock*1010); 
	 $stop; 
	 end

task press_key;  //任务
begin
   repeat(50) begin
  	 random_time = {$random}%65535;   //无{}表示取值范围-65535~65535
 	 #random_time  Key = ~ Key; 
	end
	 Key = 0;
	 #(`period_clock*1000010);
	
	repeat(50) begin
  	 random_time = {$random}%65535;   //无{}表示取值范围-65535~65535
 	 #random_time  Key = ~ Key; 
	end
	Key = 1;
	#(`period_clock*2000010);
end 
 endtask
 
endmodule

仿真结果

在这里插入图片描述

异步信号处理

在输入信号之前添加两级寄存器。减少D触发器震荡。按键事件对响应时间要求不高,可以使用多级触发器。

module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
   input Clk;
	input Rst_n;
	input Key_in;
	output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
	output reg Key_data;
	
   reg [3:0]state; 
   reg  [19:0]count; //寄存器计数	
	reg   count_start;//时能计时
	reg  count_full;//计满脉冲信号
	reg reg0,reg1;
	reg key_in_now0,key_in_now1;
	wire rise,fall;
	
	localparam 
	High_pulse      =   4'b0001, //高电平稳定状态
	Low_eliminate   = 4'b0010, //键下降沿稳定状态
	Low_pulse       = 4'b0100, //低电平稳定状态
	High_eliminate  = 4'b1000; //键上升沿稳定状态
	
		always @(posedge Clk or negedge Rst_n)    //对输入按键信号做同步处理,消除异步信号亚稳态的影响。
	 if(!Rst_n) begin
       key_in_now0 <= 1'b0;
		 key_in_now1 <=1'b0;
		 end
      else   begin 
		 key_in_now0 <= Key_in;
	    key_in_now1 <= key_in_now0; 	
	end
	
	
	always @(posedge Clk or negedge Rst_n)    //脉冲边沿检测   两个寄存器
	 if(!Rst_n) begin
       reg0 <= 1'b0;
		 reg1 <=1'b0;
		 end
      else   begin 
		 reg0 <= key_in_now1;
	    reg1 <= reg0; 	
	end
	
	assign fall = !reg0 & reg1;  //检测到下降沿
	assign rise = reg0 & !reg1;  //检测到上升沿  
	
	 always @(posedge Clk  or negedge Rst_n)    //20ms计数器
       if(!Rst_n)
          count <= 20'b0;            	
	      else  if(count_start)
			  count <= count + 20'b1;
             else  count <= 20'b0;		  	
				 
	 always @(posedge Clk  or negedge Rst_n)     //20ms计数器计数满标志脉冲信号
       if(!Rst_n)
          count_full <= 1'b0;            	
	      else  if(count == 99_9999)
			  count_full <= 1'b1;
             else  count_full <= 1'b0;	
				 
   状态机(同上)

endmodule 

在这里插入图片描述
将异步信号传入到D触发器中可能产生触发器震荡,导致D触发器输出未知状态,所以将异步信号输入到两级D触发器减少D触发器震荡影响(消除亚稳态).

顶层文件Key_led_top

module Key_led_top(Clk,Rst_n,Key_in0,Key_in1,led);
  input Clk;
  input Rst_n;
  input Key_in0,Key_in1;
  output [3:0]led;
  wire Key_flag0,Key_flag1;
  wire Key_data0,Key_data1;
  
   Key_shake   Key_shake0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in0),
  .Key_flag(Key_flag0),
  .Key_data(Key_data0)
  );
     
   Key_shake   Key_shake1(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in1),
  .Key_flag(Key_flag1),
  .Key_data(Key_data1)
  );
  
  Led_control Led_control0(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_flag0(Key_flag0),
  .Key_state0(Key_data0),
  .Key_flag1(Key_flag1),
  .Key_state1(Key_data1),
  .Led(led)
  );

endmodule 

LED驱动文件Led_control

module Led_control(Clk,Rst_n,Key_flag0,Key_state0,Key_flag1,Key_state1,Led);
  input Clk,Rst_n;
  input Key_flag0,Key_flag1;
  input Key_state0,Key_state1;
  output   [3:0]Led;
  reg [3:0]Led_un;
  
  always @(posedge Clk  or negedge Rst_n)
   if(!Rst_n)
	  Led_un <= 4'b0000;
	  else  if(Key_flag0 && Key_state0)
       Led_un <= Led_un + 4'b1;
        else  if(Key_flag1 && Key_state1)
         Led_un <= Led_un - 4'b1;
	 else 
	   Led_un <= Led_un;
  
  assign Led = ~Led_un;
  
endmodule 

顶层仿真文件

`timescale 1ns/1ns
`define period_clock 20
module Led_top_tb;
  
  reg Clock;
  reg Rst_n;
  wire Key_in0;
  wire Key_in1;
  reg press0,press1;
  wire [3:0]led;
 

Key_led_top Key_led_top0(
  .Clk(Clock),
  .Rst_n(Rst_n),
  .Key_in0(Key_in0),
  .Key_in1(Key_in1),
  .led(led)
    );
Key_model  Key_model0( 
         .Press(press0),
			.Key(Key_in0)
			  ); 
Key_model  Key_model1( 
         .Press(press1),
			.Key(Key_in1)
			  ); 
initial Clock = 1;
always #(`period_clock/2) Clock = ~Clock;

initial begin
  Rst_n = 0;
  press0 = 0;
  press1 = 0;
  #(`period_clock*4);
   Rst_n = 1;
 #(`period_clock*10+1);
   press0 = 1;     //自加
 #(`period_clock*2);
   press0 = 0;
 #(`period_clock*4_000_000);
	press0 = 1;
 #(`period_clock*2);
   press0 = 0;
 #(`period_clock*4_000_000);
 
 
   press1 = 1;     //自加
 #(`period_clock*2);
   press1 = 0;
 #(`period_clock*4_000_000);
	press1 = 1;
 #(`period_clock*2);
   press1 = 0;
 #(`period_clock*4_000_000);
   $stop;
end
			  
			  
endmodule 

仿真结果

在这里插入图片描述
Key_flag0信号使LED自增,Key_flag1信号使LED自减。led取反。

状态机

格雷码编码方式:
0000; 0010; 0011;0111; (一次只转变一位数字,运行时钟频率比较高)

独热码编码方式:
0001;0010;0100;1000;(译码逻辑简单,频率高)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值