FPGA状态机进行独立按键消抖

状态机设计

(看了小梅哥的教学视频后,学习的第九课)

按键按下抬起过程中,有等待按下空闲状态(IDEL),按下抖动滤除状态(FILTER0),按下稳定状态(DOWN),抬起抖动滤除状态(FILTER1)。

按键在按下和抬起过程中,大致会有20ms的波形抖动,不稳定状态。

接下来,用状态机实现该过程(verilog):
// An highlighted block
var foo = 'bar';
module key_filter(Clk,Rst_n,key_in,key_state,key_flag);//filter:滤波
	input Clk;
	input Rst_n;
	input key_in;
	
	output reg key_state;//按键稳定状态
	output reg key_flag;//按键检测成功标志信号

	localparam//定义状态
		IDLE 	=	4'b0001,//空闲状态
		FILTER0 = 4'b0010,//按下滤波状态
		DOWN 	=	4'b0100,//按下稳定状态
		FILTER1 =	4'b1000;//释放滤波状态
		//在quartus中,寄存器放在哪里都行,但在modelsim中不行	
	reg[3:0]state;//状态寄存器
	reg key_temp0,key_temp1;//定义两个寄存器 
	wire pedge,negdge;//定义一个上升沿和一个下降沿
	
	reg cnt_full;//定义计数满寄存器(计数器满标志信号)
	reg [19:0]cnt;//定义计数器20位宽,需要计数20ms
					 //给出使能计数信号后,才开始计数,其余时候为清零状态
	reg en_cnt;//使能计数寄存器
	
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)begin
			key_temp0 <= 1'b0;
			key_temp1 <= 1'b0;
		end
		else begin
			key_temp0 <= key_in;//寄存两次状态
			key_temp1 <= key_temp0;
		end
//检测上升沿与下降沿
		assign negdge = !key_temp0 & key_temp1;//组合逻辑输出值为1,则检测到下降沿
		assign pedge = key_temp0 & (!key_temp1);//!为取非 ~为按位取反
		//eg:0110~1001   !(0110)=0   !(0000)=1
//状态机主程序
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin//所有赋值的值,再复位的时候都要赋个初值
			key_flag <= 1'b0;
			key_state <= 1'b1;
			state <= IDLE;
			en_cnt <= 1'b0;
		end 
		else begin
			case(state)
				IDLE:
					begin 
					key_flag <= 1'b0;//因为在FILTER1里拉高了,在这里要清零
					if(negdge)begin
						state <= FILTER0;
						en_cnt <= 1'b1;
					end 
					else 
						state <= IDLE;
					end 
				FILTER0:
					if(cnt_full)begin 
						key_flag <= 1'b1;
						key_state <= 1'b0;
						en_cnt <= 1'b0;//计数满了之后清零
						state <= DOWN;
					end 
					else if(pedge)begin//如果还没有计数完,检测到上升沿
						state <= IDLE;
						en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
					end
					else
						state <= FILTER0;
				DOWN:
					begin
					key_flag <= 1'b0;//该语句与之后的if语句并行的
					if(pedge)begin
						state <= FILTER1;
						en_cnt <= 1'b1;
					end 
					else 
						state <= DOWN;
					end 
				FILTER1:
					if(cnt_full)begin 
						key_flag <= 1'b1;//可忽略,也可表示再次检测到按键,按键释放
						key_state <= 1'b1;
						//en_cnt <= 1'b0;//计数满了之后清零
						state <= IDLE;
					end 
					else if(negdge)begin//如果还没有计数完,检测到上升沿
						state <= DOWN;
						en_cnt <= 1'b0;//表示这只是抖动,不用计数,等待下降沿的到来才开始计数
					end
					else
						state <= FILTER1;
				default:
					begin
					en_cnt <= 1'b0;//给一个回到正常状态的值
					key_flag <= 1'b0;//默认为没有按键的时候的一个状态
					key_state <= 1'b1;
					state <= IDLE;
					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 == 999999)//1000000-1
			cnt_full <=  1'b1;
		else 				
			cnt_full <= 1'b0;	
	endmodule 
这次用了仿真模型,key_model,仿真模型,不可综合模块,所以得修改脚本。
//按键模型(和仿真有一定的相似之处)在按键里,不存在时钟概念
//仿真模型,不可综合模块,所以得修改脚本
`timescale 1ns/1ns
//`define clk_period 20系统时间周期可不写
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;//按键抖动函数(按下抖动模拟)“$random”为随即发生函数
   	begin 
   		repeat (50)begin					//若没有“{}”取值范围为(-65536~65536)
   			myrand = {$random}%65536;//限定了myrand的取值范围(0~65536)
   			#myrand key = ~key;//延时了myrand ns的时间后,发生翻转									
   		end 
   		key = 0;
   		#50000000;		
//释放抖动过程
   		repeat (50)begin					
   			myrand = {$random}%65536;
   			#myrand key = ~key;								
   		end 
   		key = 1;
   		#50000000;
   	end
   endtask


endmodule 

仿真主程序(test_bench文件):
//使用仿真模型进行仿真
`timescale 1ns/1ns
`define clk_period 20
module key_filter_tb;
	reg Clk;
	reg Rst_n;
	wire key_in;//因为是两个模块之间进行连接,所以用wire型
	
	wire key_flag;
	wire key_state;
	
	key_filter key_filter0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_state(key_state),
		.key_flag(key_flag)
	);
	//模块调用
	key_model key_model(.key(key_in));//连接到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 

欢迎指正(:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值