状态机实现N位按键消抖

状态机实现N位按键消抖

1、原理

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

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

状态机简介:

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

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

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

verilog 复制代码
 always @*
    begin
        if(current_state == s4) dout = 1;
        else dout = 0;
    end
 

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

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

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

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

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

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

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

可参考:

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

2、代码

verilog 复制代码
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、仿真代码

verilog 复制代码
`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、仿真结果

5、总结

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

相关推荐
DS小龙哥6 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师16 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战17 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩1 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发