基于FPGA的数字钟——(二)按键消抖模块设计

基于FPGA的数字钟——(二)按键消抖模块

1、硬件原理

按键按下时会有随机的抖动,因此需要在抖动期过后再进行按键状态的判断。

2、原理实现

按键引脚检测电平跳变,跳变时进行计数,计数一定时间后再判断电平·高低以判断是抖动还是真实按下。检测按键放开同理。

3、FPGA实现

设计一个状态机,有以下几个状态:IDLE(放开),FILTER0(下降沿滤波),DOWN(按下),FILTER1(上升沿滤波)。
消抖计数器计数周期为50000个时钟周期,即50000*20=1000000ns=1ms

verilog代码

//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
// 
// Create Date: 2019/12/29 15:55:40
// Design Name: key_debounce
// Module Name: key_debounce
// Project Name: Clock
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module key_debounce
#(
	parameter BEBON_CNT = 50000//消抖计数器
)
(
	input clk,
	input rst,
	input key_in,
	output reg key_press,
	output reg key_up
);
	reg[15:0] debounce_cnt;//1ms计数器
	reg cnt_en;
	reg cnt_full;
	reg key_temp0;
	reg key_temp1;
	wire nedge;//按键下降沿
	wire pedge;//按键上升沿
	reg[3:0] state;
	localparam IDLE = 4'b0001;
	localparam FILTER0 = 4'b0010;
	localparam DOWN = 4'b0100;
	localparam FILTER1 = 4'b1000;

	always@(posedge clk or negedge rst)begin
		if(!rst)begin
			key_temp0 <= 1'b1;
			key_temp1 <= 1'b1;
		end
		else begin
			key_temp0 <= key_in;
			key_temp1 <= key_temp0;
		end
	end
	assign nedge = (!key_temp0)&(key_temp1);
	assign pedge = (key_temp0)&(!key_temp1);
	
	always@(posedge clk or negedge rst)begin
		if(!rst)begin
			debounce_cnt <= 16'd1;
			cnt_full <= 1'b0;
		end
		else
			if(cnt_en)
				if(debounce_cnt==BEBON_CNT)begin
					debounce_cnt <= 16'd1;
					cnt_full <= 1'b1;
				end
				else begin
					debounce_cnt <= debounce_cnt + 1'b1;
					cnt_full <= 1'b0;
				end
			else begin
				debounce_cnt <= 16'd1;
				cnt_full <= 1'b0;
			end
	end
	
	
	always@(posedge clk or negedge rst)begin
		if(!rst)begin
			state <= IDLE;
		end
		else
			case(state)
				IDLE:begin
					if(nedge)
						state <= FILTER0;
					else
						state <= IDLE;
				end
				FILTER0:begin
					if(cnt_full)
						if(key_temp0==1'b0)//计时结束按键还按着
							state <= DOWN;
						else
							state <= IDLE;
					else
						state <= FILTER0;
				end
				DOWN:begin
					if(pedge)
						state <= FILTER1;
					else
						state <= DOWN;
				end
				FILTER1:begin
					if(cnt_full)
						if(key_temp0==1'b1)//计数结束后按键还放开
							state <= IDLE;
						else
							state <= DOWN;
					else
						state <= FILTER1;
				end
				default:
					state <= IDLE;
			endcase		
	end
	
	always@(posedge clk or negedge rst)begin
		if(!rst)begin
			key_press <= 1'b0;
			key_up <= 1'b1;
			cnt_en <= 1'b0;
		end
		else
			case(state)
				IDLE:begin
					key_press <= 1'b0;
					key_up <= 1'b1;
					cnt_en <= 1'b0;
				end
				FILTER0:begin
					key_press <= 1'b0;
					key_up <= 1'b1;
					cnt_en <= 1'b1;
				end
				DOWN:begin
					key_press <= 1'b1;
					key_up <= 1'b0;
					cnt_en <= 1'b0;
				end
				FILTER1:begin
					key_press <= 1'b1;
					key_up <= 1'b0;
					cnt_en <= 1'b1;
				end
				default:begin
					key_press <= 1'b0;
					key_up <= 1'b1;
					cnt_en <= 1'b0;
				end
			endcase			
	end

endmodule

状态机转移图

在这里插入图片描述

testbench代码

仿真时缩短了计数器的计数周期为500个clk,只是为了让仿真跑快一点,计数50000个clk仿真要很久。

`timescale 1ns/1ps
`define clock_period 20
module key_debonce_tb;
	reg clk;
	reg rst;
	reg key_in;
	wire key_press;
	wire key_up;


key_debounce
#(
	.BEBON_CNT(500)//消抖计数器
)
key_debounce
(
	.clk(clk),
	.rst(rst),
	.key_in(key_in),
	.key_press(key_press),
	.key_up(key_up)
);
	initial clk = 1;
	always#(`clock_period/2)clk = ~clk;
	
	initial begin
		rst = 0;
		key_in = 1;
		#(`clock_period*20);
		rst = 1;
		key_in = 1;
		#(`clock_period*100);
		key_in = 0;
		#(`clock_period*10);//一次抖动
		key_in = 1;
		#(`clock_period*500)
		
		key_in = 0;
		#(`clock_period*1000);//一次成功按下
		key_in = 1;
		#(`clock_period*1000);//一次成功放开
		$stop;
	end

endmodule

仿真波形图

在这里插入图片描述

发布了5 篇原创文章 · 获赞 10 · 访问量 160

猜你喜欢

转载自blog.csdn.net/qq_43650722/article/details/103808539