【FPGA学习笔记03】按键消抖

本文详细介绍了FPGA开发中如何通过硬件和软件方法实现按键消抖,包括使用RS触发器、电容等硬件手段以及编程逻辑来确保按键状态的准确性。同时,展示了如何利用FPGA控制蜂鸣器的示例代码,以及仿真测试的过程和结果。
摘要由CSDN通过智能技术生成

具体见正点原子《达芬奇pro之FPGA开发指南v2.1》第十八章

1.为什么要消抖

因为我们通常使用的开关为机械弹性开关,当我们按下或松开按键时,由于弹片物理特性,无法立即闭合或断开,会在该时刻短时间内产生机械抖动,使我们采集到错误的按键状态。
所以要消除这种抖动,而消抖的过程就被称为按键消抖。
①硬件消抖: 主要用RS触发器、电容等方法在硬件电路上实现,一般适用于按键较少。
②软件消抖: 按键按下或松开后,由处理器偃师5-20ms,再对按键状态进行采样判断。简单说就是在规定时间内读取两次按键状态,判断两次状态是否一致。
其原理图如下所示:
在这里插入图片描述

2.软件部分:代码

2.1 按键消抖

module key_debounce(
  input			sys_clk,
  input			sys_rst_n,
  
  input 		key,   //外部输入的按键信号
  output reg	key_fliter
);

//parameter define
parameter	CNT_MAX = 20'd100_0000; //消抖时间20ms
//parameter CNT_MAX = 20'd10;

//reg define
reg [19:0]  cnt;
reg			key_d0;
reg			key_d1;

//***********************************
//**	main code
//***********************************

//记录两次键值,key_d0记录外部输入的键值,key_d1记录前一时钟周期的键值
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		key_d0 <= 1'b1;
		key_d1 <= 1'b1;
	end
	else begin
		key_d0 <= key;
		key_d1 <= key_d0;
	end
end

//按键消抖
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt <= 20'd0;
	else begin
		if(key_d1 != key_d0) //按键值发生变化,则该键值不作数,滤去,计数器重置
			cnt <= CNT_MAX;
		else begin  //按键值一致,判断是否20ms后还一致,这里可以改成5-20ms
			if(cnt > 20'd0)  
				cnt <= cnt - 1'b1;
			else
				cnt <= 20'd0;
		end
	end
end

//确认最终键值
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		key_fliter <= 1'b1;
	else if(cnt == 20'd1) //递减到1,已确认按键最终状态
		key_fliter <= key_d1;
	else
		key_fliter <= key_fliter;
end

endmodule

①记录的键值会在时钟上升沿发生变化。
②总体可分为:读取键值-延时-二次读取键值两部分。

2.2 按键控制蜂鸣器

module key_beep(
  input			sys_clk,
  input 		sys_rst_n,
  
  input 		key_fliter,
  output reg	beep
);

//reg define
reg 	key_fliter_d0;

//wire define
wire 	neg_key_fliter;

//***********************************
//**	main code
//***********************************

/*
key_fliter 有两种状态,按下-0,未按下-1
*/

assign neg_key_fliter = (~key_fliter) & key_fliter_d0;

always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		key_fliter_d0 <= 1'b1;
	else
		key_fliter_d0 <= key_fliter;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		beep <= 1'b0; //上电为关闭状态
	else if(neg_key_fliter)
		beep <= ~beep;
	else
		beep <= beep;
end

endmodule

①对于语句assign neg_key_fliter = (~key_fliter) & key_fliter_d0;主要是为了让按键的状态得到记录。因为开发板上的按键需要压力才会有响应,导致需要按住按键才能满足需求。
在这里插入图片描述
由仿真效果可知,按键松开时,传递给处理器的按键状态并没有改变。

2.3 顶层代码

module top_key_beep(
  input 		sys_clk,
  input			sys_rst_n,
  
  input			key,
  output		beep
);

//parameter define
parameter CNT_MAX = 20'd100_0000;
//parameter CNT_MAX = 20'd10;

//wire define
wire key_fliter;

//********************************
//**	main code
//********************************

key_debounce #(
	.CNT_MAX	(CNT_MAX)
)u_key_debounce(
	.sys_clk	(sys_clk),
	.sys_rst_n	(sys_rst_n),
	.key		(key),
	.key_fliter	(key_fliter)
);

key_beep u_key_beep(
	.sys_clk	(sys_clk),
	.sys_rst_n	(sys_rst_n),
	.key_fliter	(key_fliter),
	.beep		(beep)
);

endmodule

3.测试部分:仿真

仿真代码

`timescale 1ns/1ns

module tb_top_key_keep();

//parameter define
parameter CLK_PERIOD = 20;
parameter CNT_MAX = 20'd10; //消抖时间20 * 10 = 200 ns

//reg define
reg 		sys_clk;
reg			sys_rst_n;
reg 		key;

//wire define
wire		beep;

//信号初始化
initial begin
	sys_clk <= 1'b0;
	sys_rst_n <= 1'b0;
	key <= 1'b1;
	#200
	sys_rst_n <= 1'b1;
//key信号变化,只有延迟超过300ns,其按键效果才有效。(仿真是200ns的延迟)
	#20
	key <= 1'b0;
	#20
	key <= 1'b1;
	
	#50
	key <= 1'b0;
	#40
	key <= 1'b1;
	
	#20
	key <= 1'b0;
	#300
	key <= 1'b1;
	
	#50
	key <= 1'b0;
	#40
	key <= 1'b1;
	#300
	key <= 1'b0;

end

//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;

//特例化待测设计
top_key_beep #(
	.CNT_MAX	(CNT_MAX)
)u_top_key_beep(
	.sys_clk	(sys_clk),
	.sys_rst_n	(sys_rst_n),
	.key		(key),
	.beep		(beep)
);

endmodule

仿真结果
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值