数字电子技术实验——按键防抖动

本文详细介绍了使用有限状态机对按键信号进行防抖处理的方法,对比了两种不同的实现方式:一种是基于固定等待时间,另一种是基于信号维持时间。通过状态机,消除按键抖动,确保电路稳定工作。并提供了ModelSim仿真的示例。
摘要由CSDN通过智能技术生成

由于金属弹性形变的原因,按键/开关在状态切换过程中总是会有抖动情况,有时这种抖动会导致电路错误运行。比如在进行按键加法时,按一下加 1可能会加2个数甚至更多。因此,在很多时候,输入电路的信号需要经过防抖动处理。

一、实验分析与设计

实验使用有限状态机实现,需要将输入的按键信号经过防抖动处理后用于计数器,同时将计数器的值显示在数码管上。

所以有输入按键信号以及处理后的输出按键信号。

状态机有四种状态:低电平状态、由低到高的抖动状态、高电平状态、由高到低的抖动状态。

        当输入按键信号为低电平时,处于低电平状态,输出按键信号也为低电平;

        当输入按键信号由低到高抖动时,处于抖动状态,输出按键信号保持低电平;

        当输入按键信号为高电平时,处于高电平状态,输出按键信号也为高电平;

        当输入按键信号由高到低抖动时,处于抖动状态,输出按键信号保持高电平;

通过状态机的转换,输出按键信号就消除了抖动部分。

所以,本实验的关键在于如何进行状态间的转换。

方法一:

        在低电平状态时,检测到输入信号有上升沿,就转入由低到高的抖动状态;

        在由低到高的抖动状态,等待对应时间(假定大于抖动时间),转入高电平状态。

        在高电平状态时,检测到输入信号有下降沿,就转入由高到低的抖动状态;

        在由高到低的抖动状态,等待对应时间(假定大于抖动时间),转入低电平状态。

方法二:

        在低电平状态时,检测到输入信号有上升沿,就转入由低到高的抖动状态;

        在低到高的抖动状态,检测到高电平状态维持一定时间(小于高电平总时长),转入高电平状态。

        在高电平状态时,检测到输入信号有下降沿,就转入由高到低的抖动状态;

        在高到低的抖动状态,检测到低电平状态维持一定时间(小于低电平总时长),转入低电平状态。

此时需要引入时间信号,用于记录时间。

二、程序代码

采用层次化设计,包括按键防抖动部分,计数器部分、七段译码器部分。

方法一与方法二的区别只在按键防抖动部分,方法二比方法一多4行代码,在方法二中标出。

顶层文件

module Test(clk,key,key_out,Data,codeout);
	input clk; //50MHZ 计时
	input key; //按键输入
	output key_out;//按键经过处理后的输出
	
	output [3:0]Data;	//计数器输出
	output [6:0]codeout;	//七段译码器
	
	Test_1 t1(clk,key,key_out);	//按键防抖动
	Test_2 t2(key_out,Data);	//计数器
	Test_3 t3(codeout,Data);	//译码器
endmodule

按键防抖动部分

方法一:等待时长取10ms。(假设抖动时长小于10ms,如果大于10ms会出现问题)

module Test_1(clk,key,key_out);	//按键防抖动
	input clk; //50MHZ 计时
	input key; //按键输入
	output reg key_out = 1'b0;//按键经过处理后的输出
	
//状态机四种状态
	parameter Down = 2'b00,Filter1 = 2'b01,Filter2 = 2'b10,Up = 2'b11;
	reg [1:0] state = Down; //实时状态
		
//按键输入的上升下降沿
	reg keyT1=1'b0,keyT2=1'b0;
	always@(posedge clk)
	begin
		keyT1 <= key;	//当前按键输入值
		keyT2 <= keyT1;//前一按键输入值
	end
	wire keyPos,keyNeg;
	assign keyPos = keyT1 & (!keyT2); //上升沿为:前一0 当前1。
	assign keyNeg = (!keyT1) & keyT2; //下降沿为:前一1 当前0。
	
//计时10ms
	reg TimeSwitch = 1'b0;	//计时开关
	reg TimeFull = 1'b0;		//计时10ms是否到
	integer n = 1; 	//计时
	
	always@(posedge clk)		//计时模块10ms
	begin
		if(TimeSwitch) n <= n + 1;
		else n <= 1;

		if(n >= 500000) TimeFull <= 1'b1;
		else	TimeFull <= 1'b0;
	end
	
//有限状态机模块
	always@(posedge clk)		
	begin
		case(state)
			Down:		//低电平状态
				begin
					key_out <= 1'b0;	//输出按键信号为低电平
					if(keyPos)	//按键由0到1的第一个上升沿
						begin
							state <= Filter1;	  //进入抖动状态
							TimeSwitch <= 1'b1; //开始计时
						end
					else state = state;
				end
			Filter1:	//低到高抖动状态
				begin
					if(TimeFull)	//计时10ms结束
						begin
							state <= Up; 		  //进入高电平状态
							TimeSwitch <= 1'b0; //计时结束
						end
					else state <= state;
				end
			Up:		//高电平状态
				begin
					key_out <= 1'b1;	//输出按键信号为高电平
					if(keyNeg)	//由1到0的第一个下降沿
						begin
							state <= Filter2;	  //进入抖动状态
							TimeSwitch <= 1'b1; //开始计时
						end
					else state <= state;
				end
			Filter2:	//高到低抖动状态
				begin
					if(TimeFull)	//计时10ms结束
						begin
							state <= Down; 	  //进入低电平状态
							TimeSwitch <= 1'b0; //计时结束
						end
					else state <= state; 
				end
			default: state <= Down;
		endcase
	end
	
endmodule

方法二:维持时长取10ms。(假设高低电平维持时长大于10ms,如果小于10ms会出现问题)

module gwl_2535_7_1(clk,key,key_out);	//按键防抖动
	input clk; //50MHZ 计时
	input key; //按键输入
	output reg key_out = 1'b0;//按键经过处理后的输出
	
//状态机四种状态
	parameter Down = 2'b00,Filter1 = 2'b01,Filter2 = 2'b10,Up = 2'b11;
	reg [1:0] state = Down; //实时状态
	
//按键输入的上升下降沿
	reg keyT1=1'b0,keyT2=1'b0;
	always@(posedge clk)
	begin
		keyT1 <= key;	//当前按键输入值
		keyT2 <= keyT1;//前一按键输入值
	end
	wire keyPos,keyNeg;
	assign keyPos = keyT1 & (!keyT2); //上升沿为:前一0 当前1。
	assign keyNeg = (!keyT1) & keyT2; //下降沿为:前一1 当前0。
	
//计时10ms,key一直保持不变(即key不抖动10ms)
	reg TimeSwitch = 1'b0;	//计时开关
	reg TimeFull = 1'b0;		//计时是否到
	reg flag = 1'b0;	//反应key的值                                      //增加变量flag
	integer n = 1; 	//计时
	
	always@(posedge clk)		//计时模块10ms
	begin
		if(TimeSwitch && flag) n <= n + 1;	//计时开关打开且flag为1        //判定条件被修改
		else n <= 1;

		if(n >= 500000) TimeFull <= 1'b1;
		else	TimeFull <= 1'b0;
	end
	
//有限状态机模块
	always@(posedge clk)		
	begin
		case(state)
			Down:		//低电平状态
				begin
					key_out <= 1'b0;	//输出按键信号为低电平
					if(keyPos)	//按键由0到1的第一个上升沿
						begin
							state <= Filter1;	  //进入抖动状态
							TimeSwitch <= 1'b1; //开始计时
						end
					else state = state;
				end
			Filter1:	//低到高抖动状态
				begin
					flag <= key;	//key保持在高电平,flag为1        //增加将key赋值给flag
					if(TimeFull)	//计时10ms结束
						begin
							state <= Up; 		  //进入高电平状态
							TimeSwitch <= 1'b0; //计时结束
						end
					else state <= state;
				end
			Up:		//高电平状态
				begin
					key_out <= 1'b1;	//输出按键信号为高电平
					if(keyNeg)	//由1到0的第一个下降沿
						begin
							state <= Filter2;	  //进入抖动状态
							TimeSwitch <= 1'b1; //开始计时
						end
					else state <= state;
				end
			Filter2:	//高到低抖动状态
				begin
					flag <= ~key;	//key保持在低电平,flag为1        //增加将~key赋值给flag
					if(TimeFull)	//计时10ms结束
						begin
							state <= Down; 	  //进入低电平状态
							TimeSwitch <= 1'b0; //计时结束
						end
					else state <= state; 
				end
			default: state <= Down;
		endcase
	end

endmodule

用输出按键信号的计数器

module Test_2(key_out,Data);	//输出计数器
	input key_out;
	output reg[4:0]Data = 4'b0000;
	
	always@(posedge key_out)	//上升沿
	begin
		if(Data < 4'b1001)	//小于9递增
			Data <= Data + 1'b1;
		else
		   Data <= 1'b0;
	end
endmodule

七段译码器 

module Test_3(codeout,Data);	//七段译码器
	input[3:0] Data;//4位输入信号
	output reg[6:0] codeout = 7'b1111110;//7位输出信号
	
	always @(Data)//用always块语句描述逻辑
	begin
		case(Data)//case多分支条件语句,按输入显示对应输出
			4'd0 : codeout = 7'b1111110;
			4'd1 : codeout = 7'b0110000;
			4'd2 : codeout = 7'b1101101;
			4'd3 : codeout = 7'b1111001;
			4'd4 : codeout = 7'b0110011;
			4'd5 : codeout = 7'b1011011;
			4'd6 : codeout = 7'b1011111;
			4'd7 : codeout = 7'b1110000;
			4'd8 : codeout = 7'b1111111;
			4'd9 : codeout = 7'b1111011;
			default : codeout = 7'bx;//其他输入情况
		endcase
	end
endmodule

三、ModelSim仿真

一、Test Bench文件

此处输入波形key,为循环输入:

低电平40ms->抖动10ms(每1ms翻转一次)->高电平40ms->抖动10ms(每1ms翻转一次)->低电平

`timescale 1 ns/ 1 ns
module Test_vlg_tst();

reg clk;
reg key;
                                               
wire [3:0]  Data;
wire [6:0]  codeout;
wire key_out;
                          
Test i1 (   
	.Data(Data),
	.clk(clk),
	.codeout(codeout),
	.key(key),
	.key_out(key_out)
);
initial                                                
begin                                                  
    clk = 1'b0;
    key = 1'b0;     
$display("Running testbench");                       
end
always    //循环输入
begin
    #40000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;

    #40000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
    #1000000
    key = 1'b1;
    #1000000
    key = 1'b0;
end                                            
always #10 clk = ~clk;                                                                                         
endmodule

二、波形图

方法一:

方法二:

注:为了使波形图中显示时间相对准却,可以修改计时部分中的500000为499999(方法一)或499998(方法二)。这是由于非阻塞赋值导致的。

  • 41
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值