资讯详情

Verilog实现按键消抖

目录

  • 1、实验平台
  • 2、实验目的
    • 2.1、实验要求
  • 3、实验流程
    • 3.1、实验原理
    • 3.2、系统架构
    • 3.3、功能模块划分
      • 3.3.按钮抖动模块
          • 模块框图
          • 信号定义
          • 时序信号图
          • 设计文件
          • 仿真文件
          • 仿真图
      • 3.3.2、LED驱动模块
          • 模块框图
          • 信号定义
          • 信号时序图
          • 设计文件
          • 仿真文件
          • 仿真图
      • 3.3.3、顶层文件
    • 3.4、上板验证
  • 4、总结

1、实验平台

软件:PC、Quartus Prime 18.1、Modelsim 10.5b 硬件:Altera FPGA开发板(EP4CE6E22F17C8) 

2、实验目的

  • 1.按钮通过延迟抖动
  • 2、状态机demo熟悉
  • 3、状态机实现按键消抖(多位宽)

2.1、实验要求

消除抖动后使用按键信号控制LED呈现两种不同的状态  a) 流水灯  b) 闪烁 

3、实验流程

3.1、实验原理

根据开发板的原理图,可以获得以下信息 

在这里插入图片描述

由于弹簧片的存在,这种机械按钮默认处于高电平。按下和释放时会有一定的抖动时间。抖动时,其电平状态为不定值,因此需要在稳定时读取信号,通常抖动时间为5~10ms,故设置一个20ms用于计时低电平时长的计数器。 

根据硬件原理图,8根发光二极管连接3根阳极.3V正电压,也就是高电平,所以如果我们想要的话 如果发光二极管导通,需要在阴极接通低电平,才能让LED亮起来。 

3.2、系统架构

根据系统要求,可获得以下框架分布 

3.3、功能模块划分

以下模块可根据系统构建获得 

3.3.按钮抖动模块

模块框图

信号定义
信号名 端口类型 数据位宽 信号说明
Clk i 1 输入时钟信号,50MHz
Rst_n i 1 输入复位信号,低电平有效
key i 1 输入按键信号,低电平有效
flag o 1 输出按钮抖动信号,低电平有效
时序信号图

设计文件
/*================================================*\ Filename ﹕key_filter.v Author ﹕Adolph Description ﹕计数器实现按钮抖动 Called by ﹕key_top.v Revision History ﹕ 2022-5-9 Revision 1.0 Email﹕ adolph1354238998@gamil.com \*================================================*/ module key_filter(  input clk  ,//system clock ,50MHz  input rst_n ,//system reset ,low valid  input key_in ,//key input,high valid    output key_flag //filter out,high valid );  parameter CNT_20MS = 20'd100_0000;//20ms counter value  //20ms counter  reg [19:0] cnt_delay;    always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt_delay <= 20'd0; else if(!key_in)begin if(cnt_delay >= CNT_20MS) cnt_delay <= cnt_delay;//just valid once else cnt_delay <= cnt_delay + 20'd1; end else cnt_delay <= 20'd0; end assign key_flag = (cnt_delay == CNT_20MS - 20'd1); endmodule 
仿真文件
/*================================================*\ Filename ﹕tb_key_filter.v Author ﹕Adolph Description ﹕按键消抖测试文件 Called by ﹕No file Revision History ﹕ 2022-5-9 Revision 1.0 Email﹕ adolph1354238998@gamil.com \*================================================*/

`timescale 1ns/1ns 

module tb_key_filter();

	reg		tb_clk;
	reg		tb_rst_n;
	reg		tb_key;
	
	wire	tb_flag;
	
	parameter clk_period = 20;
	defparam U_key_filter.CNT_20MS = 1400; // 

	key_filter		U_key_filter(
		/*input */.clk		(tb_clk		),//system clock ,50MHz
		/*input */.rst_n	(tb_rst_n	),//system reset ,low valid
		/*input */.key_in	(tb_key		),//key input,high valid
		
		/*output*/.key_flag	(tb_flag	) //filter out,high valid
	);
	
	initial tb_clk = 1'b0;
	always #(clk_period / 2) tb_clk = ~tb_clk;

	integer i; //整形信号,32-bit
	
	initial begin
		tb_rst_n = 1'b0;
		tb_key = 1'b1;
		#(clk_period * 20 + 3);
		tb_rst_n = 1'b1;

		press_key; //调用任务
		
		#(clk_period * 4000);
		
		press_key;

		$stop;
	end

	task press_key;
		begin
			tb_key = 1'b1;
			repeat(48)begin //模拟前抖动
				i = { 
        $random} % 300;
				#(i * clk_period); tb_key = ~tb_key;
			end
			tb_key = 1'b0;
			#(1500 * clk_period);
			repeat(55)begin //模拟后抖动
				i = { 
        $random} % 300;
				#(i * clk_period); tb_key = ~tb_key;
			end
			tb_key = 1'b1;
			#(3000 * clk_period);
		end
	endtask 
	
endmodule 	
仿真图

3.3.2、LED驱动模块

模块框图

信号定义
信号名 端口类型 数据位宽 信号说明
Clk i 1 输入时钟信号,50MHz
Rst_n i 1 输入复位信号,低电平有效
key1 i 1 输入按键消抖信号,高电平有效
key2 i 1 输入按键消抖信号,高电平有效
led_o o 8 输出按键消抖信号,低电平有效
信号时序图

设计文件
/*================================================*\ Filename ﹕led_driver.v Author ﹕Adolph Description ﹕按键消抖——LED驱动部分 Called by ﹕key_top.v Revision History ﹕ 2022-5-10 Revision 1.0 Email﹕ adolph1354238998@gmail.com \*================================================*/
module 	led_driver(
	input  wire			Clk		, //system clock 50MHz
	input  wire 		Rst_n	, //reset ,low valid
		   
	input  wire 		key_in1	, //消抖后的按键信号输入
	input  wire 		key_in2	, //消抖后的按键信号输入
	
	output reg  [07:00]	led_o	  //LED信号输出
);
//Parameter Declarations
	parameter	CNT_MAX	= 24'd1000_0000; //50MHz主频下200ms计数值

//Interrnal wire/reg declarations
	reg		[23:00]	cnt		; //200ms Counter 
	wire			add_cnt ; //Counter Enable
	wire			end_cnt ; //Counter Reset 
	
	reg				flag_1	; //key_in1 ctrl blink led
	reg		[07:00]	r1_led	; //key_in1 ctrl blink led
	reg				flag_2	; //key_in2 ctrl water led 
	reg		[07:00]	r2_led	; //key_in2 ctrl water led
	
//Logic Description
	
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			cnt <= 24'd0; 
		end  
		else if(add_cnt)begin  
			if(end_cnt)begin  
				cnt <= 24'd0; 
			end  
			else begin  
				cnt <= cnt + 1'b1; 
			end  
		end  
		else begin  
			cnt <= 24'd0;  
		end  
	end 
	
	assign add_cnt = flag_1 || flag_2; 
	assign end_cnt = add_cnt && cnt >= CNT_MAX - 24'd1; 
	
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			flag_1 <= 1'b0;
			flag_2 <= 1'b0;
		end  
		else begin  
			case({ 
        key_in1,key_in2})
				2'b10:begin flag_1 <= 1'b1; flag_2 <= 1'b0; end
				2'b01:begin flag_1 <= 1'b0; flag_2 <= 1'b1; end
				default: ;//当两个按键同时按下,则维持不变
			endcase
		end  
	end //always end
	
	//闪烁
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			r1_led <= 8'd0;
		end  
		else if(key_in1)begin
			r1_led <= 8'd255;
		end 
		else if(end_cnt)begin  
			r1_led <= ~r1_led;
		end  
		else begin  
			r1_led <= r1_led;
		end  
	end //always end
	
	//流水
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			r2_led <= 8'd1;
		end  
		else if(key_in2)begin  
			r2_led <= 8'd1;
		end  
		else if(end_cnt)begin
			r2_led <= { 
        r2_led[6:0],r2_led[7]};
		end 
		else begin  
			r2_led <= r2_led;
		end  
	end //always end
	
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			led_o <= 8'd0;
		end  
		else if(flag_1)begin  
			led_o <= r1_led;
		end  
		else if(flag_2)begin
			led_o <= r2_led;
		end 
		else begin  
			led_o <= led_o;
		end  
	end //always end
	
endmodule 

仿真文件
/*================================================*\ Filename ﹕tb_led_driver.v Author ﹕Adolph Description ﹕按键消抖——LED驱动部分 测试文件 Called by ﹕No file Revision History ﹕ 2022-5-10 Revision 1.0 Email﹕ adolph1354238998@gmail.com \*================================================*/

`timescale 1ns/1ns 		//仿真系统时间尺度定义

`define clk_period 20  	//时钟周期参数宏定义 

module tb_led_driver(); 

//参数重定义
	defparam U_led_driver.CNT_MAX = 200;

//激励信号定义 
	reg				Clk		; 
	reg				Rst_n	; 
	reg 			key_in1	;
	reg 			key_in2 ;
	
//响应信号定义 
	wire	[07:00]	led_o	; // 
	
//实例化
	led_driver	U_led_driver(
		/*input wire */.Clk		(Clk	), //system clock 50MHz
		/*input wire */.Rst_n	(Rst_n	), //reset ,low valid
		
		/*input wire */.key_in1	(key_in1), //消抖后的按键信号输入
		/*input wire */.key_in2	(key_in2), //消抖后的按键信号输入
		
		/*output reg [07:00]*/.led_o	(led_o	)  //LED信号输出
	);

//产生时钟 
	initial Clk = 1'b0;		       		 
	always #(`clk_period / 2) Clk = ~Clk;  		 

//产生激励 
	initial  begin	 
		Rst_n = 1'b0;	 
		key_in1 = 1'b0;
		key_in2 = 1'b0;
		#(`clk_period * 10 + 3);	 
		Rst_n = 1'b1;	 
		#(`clk_period * 10); 
		
		key_in1 = 1'b1; //闪烁灯
		#`clk_period;
		key_in1 = 1'b0;
		#(10 * `clk_period * U_led_driver.CNT_MAX);
		
		key_in2 = 1'b1; //流水灯
		#`clk_period;
		key_in2 = 1'b0;
		#(10 * `clk_period * U_led_driver.CNT_MAX);
		
		key_in1 = 1'b1; //闪烁灯
		#`clk_period;
		key_in1 = 1'b0;
		#(10 * `clk_period * U_led_driver.CNT_MAX);
		
		key_in2 = 1'b1; //流水灯
		#`clk_period;
		key_in2 = 1'b0;
		#(10 * `clk_period * U_led_driver.CNT_MAX);
		
		$stop(2); 
	end	 

endmodule 
仿真图

3.3.3、顶层文件

顶层文件在此不作讲解,根据下列RTL视图,相信读者可以很轻易的完成相应代码设计

3.4、上板验证

基于前面的步骤的结束,我们开始上板验证
在quartus的Pin planner 中进行引脚绑定

然后进行全编译,待到全编译通过后,连接好开发板,电源线和下载都要连接好,然后打开电源

下载编程文件

如果是第一次使用开发板的童鞋,参看这里更新驱动,切记,前提条件是开发板正确和PC连接,并且已经通电!!! 驱动更新成功后,点击“Start”进行编程,右上角的Progress为下载进度,成功后会有“100% Successful”提示字样,然后在开发板上可以看到相应的效果——流水灯。

4、总结

到这里基本上就结束了,给大家提几点在学习过程中可能会出现的错误
	1、我们的文件名和Module后面的模块名要保持一致,不然在仿真的时候会找不到文件的
	2、reg和wire信号的使用规则一定要分清楚
	3、任何信号在使用之前一定要先声明
	...
然后大家可以在提供的基础代码上进行创新,比如:
	1、使用状态机实现按键消抖
	2、实现一个文件同时进行多位按键消抖
	3、修改LED驱动部分显示效果
	...

标签: 贴片二极管丝印f17

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台