按键消抖模块的设计与验证Verilog

1、设计内容

        按键消抖的原理:

        通常我们所使用的开关为机械弹性开关,当我们按下或松开按键时,由于弹片的物理特性,不能立即闭合或断开,往往会在断开或闭合的短时间内产生机械抖动,消除这种抖动的过程即称为按键消抖。按键消抖可分为硬件消抖和软件消抖。硬件消抖主要使用RS触发器或电容等方法实现消抖,一般在按键较少时使用。软件消抖的原理主要为按键按下或松开后延时5ms—20ms采样,也可以在检测到按键状态稳定后采样,即避开抖动区域后再采样。

         对于按键消抖电路,未按下时,FPGA检测到高电平;按下时,FPGA检测到低电平。

        共分为4个状态,分别是:

                        ①未按下时空闲态——IDEL

                        ②按下抖动滤波状态——FILTER0

                        ③按下稳定状态——DOWN

                        ④释放抖动滤波状态——FILTER1

        状态转移图:

2、知识点

2.1 定义状态值

localparam
    状态1 = 4’b0001,
    状态2 = 4‘b0010,
    状态3 = 4’b0100,
    状态4 = 4‘b1000;

2.2边沿检测

        原理:脉冲边沿两侧发生了变化。

        下降沿:高电平——>低电平

        上升沿:低电平——>高电平

在这里插入图片描述

        如图所示,边沿检测就是清楚边沿0和1的变化顺序,时间一般都是从左往右进行,那么对于下降沿会首先检测一个1随后检测到0说明是下降沿,反之首先检测一个0随后检测到1是上升沿。

        思路:

                ①对输入脉冲信号进行两级寄存器锁存;

                ②对两级寄存器进行逻辑运算(异或),两个信号不相同则发生了边沿;

                ③若{先进reg,后进reg}=2’b10,则是下降沿;若{先进reg,后进reg}=2’b01,则是上升沿。

2.3 $random

        $random函数调用时返回一个32位的随机数,它是一个带符号的整形数。

 reg[23:0] rand;
 rand=$random%60; //产生一个在 -59—59范围的随机数
 reg[23:0] rand;
 rand={$random} %60; //通过位拼接操作{}产生0—59范围的随机数
reg[23:0] rand;
rand = min+{$random}%(max-min+1);//产生一个在min, max之间的随机数

2.4 仿真模型概念

3、代码

module ley_filter(clk,Rst_n,key_in,key_flag,key_state);
	
	input clk;
	input Rst_n;
	input key_in;
	output reg key_flag;
	output reg key_state;
	
	localparam
		IDEL 	 = 4'b0001,
		FILTER0  = 4'b0010,
		DOWN 	 = 4'b0100,
		FILTER1  = 4'b1000;
		
		reg [3:0]state;
		reg [19:0]cnt;
		reg en_cnt;
		reg cnt_full;//计数满标志信号
		
		reg key_tmp0,key_tmp1;
		wire pedge,nedge;

		
		always @(posedge clk or negedge Rst_n)
		begin 
			if(!Rst_n)
			begin 
				key_tmp0 <= 1'b0;
				key_tmp1 <= 1'b0;
			end
			else
			begin 
				key_tmp0 <= key_in;
				key_tmp1 <= key_tmp0;
			end
		end
		assign nedge = (!key_tmp0)&key_tmp1;
		assign pedge = key_tmp0&(!key_tmp1);
		
	always @(posedge clk or negedge Rst_n)
	
		if(!Rst_n)
		begin
			state <= IDEL;
			en_cnt <= 1'b0;
			key_flag <= 1'b0;
			key_state <= 1'b1;
			end
		else begin
			case(state)
				IDEL:
					begin
						key_flag <= 1'b0;
					if(nedge)begin
						state <= FILTER0;
						en_cnt<=1'b1;
						end
					else
						state <= IDEL;
					end
					
				FILTER0:
					if(cnt_full)begin 
						key_flag <= 1'b1;
						key_state <= 1'b0;
						state <= DOWN;
						en_cnt <= 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 
					begin
						state <= DOWN;
					end
					end
					
				FILTER1:
					if(cnt_full)begin 
						key_flag <= 1'b1;
						key_state <= 1'b1;
						state <= IDEL;
					end
					else if(nedge)
						begin
							en_cnt <= 1'b0;
							state <= DOWN;
						end
					else
						begin
							state <= FILTER1;
						end
				default:
				begin 
					state <= IDEL;
					en_cnt <= 1'b0;
					key_flag <= 1'b0;
					key_state <= 1'b1;
					
				end
				endcase

	end
	
	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 == 999_999)
		cnt_full <= 1'b1;
	else
		cnt_full <= 1'b0;
		

endmodule
 

4、RTL电路

        RTL电路

         状态转移图

        状态转移表

 5、testbench代码

5.1 方法一

`timescale 1ns/1ns
`define clk_period 20

module ley_filter_tb;

	reg clk;
	reg Rst_n;
	reg key_in;
	
	wire key_flag;
	wire key_state;


	ley_filter ley_filter0(
		.clk(clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	
	initial clk = 1;
	always #(`clk_period/2) clk = ~clk;
	
	initial begin 
		Rst_n = 1'b0;
		key_in = 1'b1;
		#(`clk_period*10) Rst_n = 1'b1;
		#(`clk_period*10+1);
		key_in = 1'b0;
		#1000;
		key_in = 1'b1;
		#2000
		key_in = 1'b0;
		#1400;
		key_in = 1'b1;
		#2600
		key_in = 1'b0;
		#1300;
		key_in = 1'b1;
		#200
		key_in = 1'b0;
		#20000100;
		#50000100;
		
		key_in = 1'b1;
		#2000
		key_in = 1'b0;
		#1000;
		key_in = 1'b1;
		#2000
		key_in = 1'b0;
		#1400;
		key_in = 1'b1;
		#2600
		key_in = 1'b0;
		#1300;
		key_in = 1'b1;
		#20000100;
		#50000100;
		
		key_in = 1'b0;
		#1000;
		key_in = 1'b1;
		#2000
		key_in = 1'b0;
		#1400;
		key_in = 1'b1;
		#2600
		key_in = 1'b0;
		#1300;
		key_in = 1'b1;
		#200
		key_in = 1'b0;
		#50000100;
		
	   key_in = 1'b0;
		#1000;
		key_in = 1'b1;
		#2000
		key_in = 1'b0;
		#1400;
		key_in = 1'b1;
		#2600
		key_in = 1'b0;
		#1300;
		key_in = 1'b1;
		#200
		key_in = 1'b0;
		#50000100;
		$stop;
	end
	endmodule
	

5.2 方法二

        运用随机数发生函数random。

`timescale 1ns/1ns
`define clk_period 20

module ley_filter_tb;

	reg clk;
	reg Rst_n;
	reg key_in;
	
	wire key_flag;
	wire key_state;


	ley_filter ley_filter0(
		.clk(clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	
	initial clk = 1;
	always #(`clk_period/2) clk = ~clk;
	
	initial begin 
		Rst_n = 1'b0;
		key_in = 1'b1;
		#(`clk_period*10) Rst_n = 1'b1;
		#(`clk_period*10+1);
		press_key;
		#10000;
		press_key;
		#10000;
		press_key;
		$stop;
	end
	
	reg [15:0]myrand;
	
	task press_key;
		begin 
			repeat(50) begin 
				myrand = {$random}%65536; //0-65535
				#myrand key_in = ~key_in;
			end
			key_in = 0;
			#50000000;
			
		  	repeat(50) begin 
				myrand = {$random}%65536; //0-65535
				#myrand key_in = ~key_in;
			end
			key_in = 1;
			#50000000;
		end
		endtask
	
	endmodule
	

5.3 方法三

        运用仿真模块。

`timescale 1ns/1ns

module key_model(key);
	output reg key;
	
	reg [15:0]myrand;
	
	
	initial begin  
		key = 1'b1;
		press_key;
		#10000;
		press_key;
		#10000;
		press_key;
		$stop;
	end
	
	task press_key;
		begin 
			repeat(50) begin 
				myrand = {$random}%65536; //0-65535
				#myrand key = ~key;
			end
			key = 0;
			#50000000;
			
		  	repeat(50) begin 
				myrand = {$random}%65536; //0-65535
				#myrand key = ~key;
			end
			key = 1;
			#50000000;
		end
		endtask
endmodule

        编写testbench文件

`timescale 1ns/1ns
`define clk_period 20

module ley_filter_tb;

	reg clk;
	reg Rst_n;
	wire key_in;
	
	wire key_flag;
	wire key_state;


	ley_filter ley_filter0(
		.clk(clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	key_model key_model0(.key(key_in));
	
	
	initial clk = 1;
	always #(`clk_period/2) clk = ~clk;
	
	initial begin 
		Rst_n = 1'b0;
		#(`clk_period*10) Rst_n = 1'b1;
		#(`clk_period*10+1);
	end
	
	endmodule
	

         仿真

        抖动

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你可以使用 Verilog 语言来进行按键消抖的仿真。按键消抖是为了解决按键在按下或释放时可能引起的抖动或干扰问题。以下是一个简单的 Verilog 代码示例: ```verilog module debounce( input wire btn, // 按键输入 output reg debounced_btn // 消抖后的按键输出 ); parameter debounce_cycles = 10; // 消抖周期数 reg [debounce_cycles-1:0] history; // 历史按键状态记录 always @(posedge btn) begin history <= {history[debounce_cycles-2:0], btn}; // 更新历史记录 debounced_btn <= (history == debounce_cycles'b1); // 判断是否稳定按下 end endmodule ``` 在这个示例中,debounce 模块接收一个按键输入信号 `btn`,并输出一个消抖后的信号 `debounced_btn`。`debounce_cycles` 参数定义了消抖周期数,即需要连续多少个周期保持稳定状态才认为按键被按下。 在 `always @(posedge btn)` 块中,我们使用一个 `history` 寄存器来记录最近的按键状态。每次按键信号 `btn` 上升沿触发时,我们将历史记录左移一位,并将当前的按键状态添加到最低位。然后,通过比较 `history` 是否全为1,来判断按键是否稳定按下。如果是,则将 `debounced_btn` 设置为1;否则,保持为0。 你可以使用任何 Verilog 仿真工具(如 ModelSim、Xilinx ISE、Quartus Prime 等)来仿真这个代码,并观察消抖效果。你可以创建一个测试平台,通过模拟按键输入信号,来验证按键消抖的功能。 希望对你有所帮助!如果有任何问题,请随时问我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发光中请勿扰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值