Verilog实现独立按键消抖(状态机)

本文参考小梅哥的独立按键消抖视频

1,实验原理:

这里是黑金开发板教程中的图,可以看出,按键未按下时的状态是高电平,按下为低电平。下边是小梅哥画的图解。

因为是机械按键,按下时候有一个不稳定的抖动期,这个时间大概在20ms以内。

2,设计思路:利用状态机实现独立按键的消抖

  1. 未按下时空闲状态
  2. 按下抖动滤除状态
  3. 按下稳定状态
  4. 释放抖动滤除状态

在第一个状态时,等待按键按下,一旦有按键按下(按键下降沿到来),便跳转到第二个状态,抖动滤除状态。在第二个状态,有检测到高电平(上升沿),就会被认为是毛刺,进而返回第一个状态继续等待下降沿。等毛刺被滤除后(计数满)则进入按下稳定状态。在按下稳定状态,等待释放(上升沿),同按键按下的状态,再次滤除释放按键的抖动。等抖动滤除后,恢复到第一个状态。

配个图吧。聊胜于无。

这段话可能有些啰嗦,结合代码看会比较清楚。当然这段话并没有牵扯到计数器的关闭与开启,会在后面代码注释中详细说明。

3,仿真图

注意看state的第一个状态,是0001,这个是第一个下降沿等待状态。后面才开始有了状态的跳变。每一个计数满就意味着一次滤波的完成。第一次代表按下抖动释放,第二次代表松开抖动释放。

仿真图很重要!!!可以帮助理解

RTL图可以自己去看,对应代码还是很清楚的。

4,代码

module filter(clk,rst_n,key_in,key_flag,key_state
	);
//端口声明
input clk;        //时钟50MHz
input rst_n;      //复位信号
input key_in;     //按键输入

output key_state;    //按键状态,高电平为未按下,低电平为按下状态
output key_flag;       /*完成滤波信号(消抖后的按键),这里有很有趣的一件事,我们在生活中发现,有些按键是按下时产生效果的,有些是按下松开后起作用的,在这段代码中,依据这个信号来产生*/

//定义
parameter   IDLE 	= 4'b0001,
		 	FILTER0 = 4'b0010,
		 	DOWN 	= 4'b0100,
		 	FILTER1	= 4'b1000;
//内部信号声明
reg [3:0] state;
reg key_flag;
reg key_state;
reg cnt_full;
reg [19:0] cnt;
reg en_counter;
//边沿检测模块,将输入信号寄存一个节拍,分别按键上升沿和下降沿的产生
reg key_tmp0,key_tmp1;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
	begin
		key_tmp0 <= 1'b1;
		key_tmp1 <= 1'b1;
	end
	else 
	begin
		key_tmp0 <= key_in;
		key_tmp1 <= key_tmp0;
	end
end
wire pedge,nedge;
assign nedge = (!key_tmp0) &  key_tmp1;        //下降沿
assign pedge = key_tmp0  & (!key_tmp1);        //上升沿

//状态机模块
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			state <= IDLE;
			en_counter <= 1'b0;
			key_state <= 1'b1;
			key_flag <= 1'b0;
		end
	else 
	begin
		case(state)
			IDLE:
			begin
				key_flag <= 1'b0;
				key_state <= 1'b1;
				en_counter <= 1'b0;
				if(nedge)        //检测到下降沿,进入下一个状态同时打开计数器
					begin
						state <= FILTER0;
						en_counter <= 1'b1;
					end
				else 
					state <= state;    
			end
			FILTER0:
				if(cnt_full)        //计数满,说明达到稳定状态,关闭计数器
					begin
						state <= DOWN;
						en_counter <= 1'b0;
						key_flag <= 1'b1;
						key_state <= 1'b0;
					end
				else if(pedge)        //检测到上升沿(毛刺),跳回idle状态同时关闭计数器
						begin
							en_counter <= 1'b0;
							state <= IDLE;
						end
					 else 
					 	state <= state;
			DOWN:
				begin
					key_flag <= 1'b0;
					if(pedge)
						begin
							state <= FILTER1;
							en_counter <= 1'b1;
						end
					else 
						state <= DOWN;
				end
			FILTER1:
				if(cnt_full)
					begin
						state <= IDLE;
						//key_flag <= 1'b1;
						key_state <= 1'b0;
					end
				else 
					if(nedge)
						begin
					 		en_counter <= 1'b0;
					 		state <= DOWN;					 		
					 	end 
					else 
						state <= state;
			default:
				state <= IDLE;
			endcase
	end
end

//20ms计数器
//这里有一个计数使能信号,只有当计数使能为高电平的时候,计数器才会计数,数数到999_999计数满时间到
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		cnt <= 20'd0;
	else if(en_counter)
			cnt <= cnt + 1'b1;
		 else 
   		 	cnt <= 20'd0;
end
//计数满信号
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		cnt_full <= 1'b0;
	else if(cnt == 20'd999_999)
			cnt_full <= 1'b1;
		 else 
		 	cnt_full <= 1'b0;
end

endmodule 

5,仿真代码

代码就直接贴上来了,理解应该没什么问题,这里的毛刺产生笔者随便模拟产生,没有用视频中讲的随机数产生函数。仿真时间比较长,不要着急。

module tb_filter;

	// Inputs
	reg clk;
	reg rst_n;
	reg key_in;

	// Outputs
	wire key_flag;
	wire key_state;

	// Instantiate the Unit Under Test (UUT)
	filter uut (
		.clk(clk), 
		.rst_n(rst_n), 
		.key_in(key_in), 
		.key_flag(key_flag), 
		.key_state(key_state)
	);
always #10 clk = ~clk;
	initial begin
		// Initialize Inputs
		clk = 1'b0;
		rst_n = 1'b0;
		key_in = 1'b1;

		// Wait 100 ns for global reset to finish
		#200;
        rst_n = 1'b1;
        #(20*10);
        key_in = 1'b0;
        #1000;
        key_in = 1'b1;
        #140;
        key_in = 1'b0;
        #2000;
        key_in = 1'b1;
        #3000;
        key_in = 1'b0;
        #100;
        key_in = 1'b1;
        #20;
        key_in = 1'b0;
        #(20*10000000);
        key_in = 1'b1;
        #1000;
        key_in = 1'b0;
        #140;
        key_in = 1'b1;
        #2000;
        key_in = 1'b0;
        #3000;
        key_in = 1'b1;
        #100;
        key_in = 1'b0;
        #20;
        key_in = 1'b1;
		// Add stimulus here

	end
      
endmodule

6,小总结

这里的按键消抖其实更多的是用来理解运用状态机的,具体个人感觉并不实用。要是多个按键就必须每一个按键分别调用模块,资源多,小点的还好,一旦太多,实例化就很麻烦了。所以真正可以用的代码我将在下一篇博客里分享。其次就是这里key_flag信号很有意思。仿真图很重要!!!可以帮助理解!仿真图很重要!!!可以帮助理解!仿真图很重要!!!可以帮助理解!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值