状态机实现N位按键消抖

状态机实现N位按键消抖

1、原理

利用状态机实现按键的消抖,具体的原理可参考

(50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客

状态机简介:

状态机分类可以主要分为两类:moore和mealy

根据三段式状态机最后一段的组合逻辑,根据状态机的输出是否与输出条件有关可以用来区分moore状态机和mealy状态机

若输出只与当前状态机有关,则为moore状态机

 always @*
    begin
        if(current_state == s4) dout = 1;
        else dout = 0;
    end
 

Moore状态机仅仅和当前状态有关

状态机实现N位按键消抖_第1张图片

Mealy状态机:输出不仅取决于当前状态,还和输入有关;

同样是三段式描述,最后的输出为:

always @(*)
	begin
		if(reset) dout = 1'b0;
		else if( (current_state == s3)&&(din == 1'b1) ) dout = 1'b1;
		else dout = 1'b0;
	end
    

状态机实现N位按键消抖_第2张图片

可见,输出不仅和当前状态和输入都有关系。

最后,Moore状态机和Mealy状态机可以相互转换。上述两个状态转移图实际上实现的是同一个功能,就是检测序列1101.

状态机按照段式分类,可分为:一段式、二段式、三段式

可参考:

(50条消息) 状态机详解(一段式、二段式、三段式)_状态机一段式二段式三段式_CuteBaBaKiller的博客-CSDN博客

状态机实现N位按键消抖_第3张图片

2、代码

module fsm_key_n#(parameter N = 4,parameter TIME_20MS = 1000_000)(
    input wire clk,
    input wire rst_n,
    input wire[N-1:0] key_in,

    output wire[N-1:0] key_out
);
reg[3:0] key_out_r;//中间信号
reg[24:0] cnt_20ms;//20ms计数器
//状态空间
parameter IDLE = 4'b0001,
			FILTER_DOWN = 4'b0010,
			DOWN = 4'b0100,
			FILTER_UP = 4'b1000;

reg[3:0] cstate;//现态
reg[3:0] nstate;//次态
reg[N-1:0] key_r0,key_r1,key_r2;//按键延时
reg flag;//检测下降沿和上升沿,寄存
//****************************************************************
//--状态转移条件定义
//****************************************************************
wire idle2filter_down;
wire filter_down2down;
wire down2filter_up;
wire filter_up2idle;
//****************************************************************
//--"计时开始结束条件
//****************************************************************
wire add_cnt_20ms;
wire end_cnt_20ms;

//****************************************************************
//--下降沿上升沿检测
//****************************************************************
assign nedge = |(~key_r1&key_r2);
assign podge = |(key_r1&key_r2);
//****************************************************************
//--状态转移条件约束
//****************************************************************
assign idle2filter_down = nedge && cstate == IDLE;
assign filter_down2down = end_cnt_20ms && cstate == FILTER_DOWN;
assign down2filter_up = podge && cstate == DOWN;
assign filter_up2idle = end_cnt_20ms && cstate == FILTER_UP;

//****************************************************************
//--"信号延时
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      	key_r0 <= {N{1'b1}};
      	key_r1 <= {N{1'b1}};
      	key_r2 <= {N{1'b1}};
    end
    else begin
        key_r0<=key_in;
        key_r1<=key_r0;
        key_r2<=key_r1;
    end
end

//****************************************************************
//--"flag信号约束
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      	flag<=1'b0;
    end
    else if(nedge || podge)begin
        flag<=1;
    end
    else if(end_cnt_20ms)begin
    	flag<=0;
    end
    else begin
    	flag<=flag;
    end
end
//****************************************************************
//--"20ms计数
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
       cnt_20ms<='d0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <='d0;
        end
        else if(nedge)begin
            cnt_20ms <= 0;
        end
        else begin
        	cnt_20ms <= cnt_20ms + 1'b1;
        end
    end
    else begin
       cnt_20ms<=cnt_20ms;
    end
end
//****************************************************************
//--"20ms计数条件约束
//****************************************************************
assign add_cnt_20ms = flag;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;

//****************************************************************
//--"三段式状态机,第一段,时序逻辑
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cstate<=IDLE;//初始当前状态为空闲
    end
    else begin
        cstate<=nstate;//次态赋值给现态
    end
end


//****************************************************************
//--"三段式状态机,第二段,组合逻辑,状态转移
//****************************************************************
always @(*) begin
    case(cstate)
        IDLE:begin
        	if(idle2filter_down)begin
        		nstate = FILTER_DOWN;
        	end
        	else begin
        		nstate = cstate;
        	end
        end
        FILTER_DOWN:
        begin
        	if(filter_down2down)begin
        	    nstate = DOWN;		
        	end
        	else begin
        	    nstate = cstate;	
        	end    
        end
        		
        DOWN:begin
        	if(down2filter_up)begin
        	    nstate = FILTER_UP;	
        	end
        	else begin
        	    nstate = cstate;		
        	end
        end

        FILTER_UP:begin
        	if(filter_up2idle)begin
        	    nstate = IDLE;		
        	end
        	else begin
        	    nstate = cstate;		
        	end
        end
        default:
            nstate = cstate;
    endcase
end

//****************************************************************
//--"有限状态机,第三段,时序逻辑
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
       	key_out_r<={N{1'b0}};
    end
    else if(filter_down2down)begin
        key_out_r<=~key_r1;
    end
    else begin
        key_out_r<={N{1'b0}};
    end
end
assign key_out = key_out_r;

endmodule

3、仿真代码

`timescale 1ns/1ns
module fsm_key_tb();

reg clk;
reg rst_n;
reg[3:0] key;
reg[3:0] delay;

wire[3:0] key_r;
parameter SYS_CLK = 20;
parameter TIME_20MS = 10;
parameter N = 4;
always #(SYS_CLK/2) clk = ~clk;

task task_init;
	begin
		clk=1'b0;
		rst_n=1'b0;
		#(2*SYS_CLK);
		rst_n=1'b1;
		key = 4'b1111;
		#(2*SYS_CLK);
	end
endtask

task task_key;
	input[3:0] key_in;
	output[3:0] key_out;
	begin
		key_out[0] = ~key_in[0];
		key_out[2] = ~key_in[2];
		key_out[3] = key_in[3];
		key_out[1] = key_in[1];
	end
endtask

initial begin
	task_init();

	repeat(10)begin
		repeat (20) begin
			task_key(key,key);
	   		// key[0] = ~key[0];
	   		// key[2] = ~key[2];
	   		delay = {$random()}%4;
	   		#(SYS_CLK*delay);
		end
		task_key(key,key);
		// key[0] = ~key[0];
		// key[2] = ~key[2];
		//wait(inst_fsm_key.end_cnt_20ms);
		#(30*SYS_CLK);
	end
	$stop;
end

fsm_key_n #(
		.N(N),
		.TIME_20MS(TIME_20MS)
	) inst_fsm_key (
		.clk     (clk),
		.rst_n   (rst_n),
		.key_in  (key),
		.key_out (key_r)
	);

endmodule

4、仿真结果

状态机实现N位按键消抖_第4张图片

5、总结

使用状态机进行按键消抖,可以经消抖分为四个部分,空闲状态、下降沿状态、按下状态、上升沿状态,这几个状态使用状态机进行按键消抖,可以更好的理解消抖的原理和过程。状态机的规范编写也是提升自己理解时序,理解逻辑的好的方式

你可能感兴趣的:(fpga开发)