【FPGA】三段式状态机按键消抖

发布于:2023-01-28 ⋅ 阅读:(576) ⋅ 点赞:(0)


状态机

状态机分类:

Moore型状态机:
状态机的变化只与当前的状态有关

Mearly型状态机:
状态机的变化不仅与当前的状态有关,还与输入有关

状态机编写方法:

一段式:
主要是讲所有的状态变化以及导致的输出变化都写在了一个always块中。

两段式:
将一些复位信号,clk信号单独写在一个always块中,其他的状态变化,输出值得变化写在一个always块中。

三段式:
将一些复位信号,clk信号单独写在一个always块中,其他的状态迁移变化写在一个always块中,对应状态的输出值得变化写在一个always块中


一、题目分析

本次设计需要用到状态机进行按键消抖的编写,同时使用按键控制led灯实现每按下一次按键使led灯以二进制顺序亮起,按键消抖的原理图如下:
在这里插入图片描述

原理图说明:

前沿抖动与后沿抖动持续时间均约为5-10ms,键稳定状态持续时间约为20ms-40ms

二、设计分析

当前沿抖动时将高低电平切换的状态分为高低电平两个状态两个状态来回切换,当按键稳定时进入下一个状态并将标志信号拉高一个时钟周期,当按键长时间保持时标志信号并不一直保持高电平状态,所以在按键松开时重新回到空闲状态。

三、状态图

key == 0
cnt == 0
key == 1
key == 0
IDLE
s0
s1
s2

四、三段式状态机编写步骤

1.确定输入输出

输入:系统时钟复位信号按键信号
输出:标志信号

代码实现:

	`timescale 1ns / 1ps
	module KeyAndLed(
    input 		clk		,
    input 		rst_n	,
    input 		key		,
    output reg  flag	
    );

2.状态编码

编码类型简介:

例:编写八个状态

编码类型代码如下:

二进制编码:

	s0 = 4'b0001;
	s1 = 4'b0010;
	s2 = 4'b0011;
	s3 = 4'b0100;
	s4 = 4'b0101;
	s5 = 4'b0110;
	s6 = 4'b0111;
	s7 = 4'b1000;

格雷码:

	s0 = 5'b00001;
	s1 = 5'b00011;
	s2 = 5'b00010;
	s3 = 5'b00110;
	s4 = 5'b00100;
	s5 = 5'b01100;
	s6 = 5'b01000;
	s7 = 5'b11000;

独热码:

	s0 = 8'b00000001;
	s1 = 8'b00000010;
	s2 = 8'b00000100;
	s3 = 8'b00001000;
	s4 = 8'b00010000;
	s5 = 8'b00100000;
	s6 = 8'b01000000;
	s7 = 8'b10000000;

3.状态机编写

第一段:
状态切换,使用时序逻辑进行实现。

代码如下(示例):

	always@(posedge clk)
    if(!rst_n)
        cur_state <= IDLE;
    else
        cur_state <= next_state;

第二段:
根据当前状态确定下一个状态,使用组合逻辑实现。

代码如下:

	always@(*)
    case(cur_state)
        IDLE  :begin
            if(key == 0)
                next_state = s0;
             else
                next_state = cur_state;
        end
        s0  :begin
            if(key == 1)
                next_state = IDLE;
            else if(cnt == DELAY - 1)
                next_state = s1;
            else
                next_state = cur_state;
        end
        s1  :begin
            next_state = s2;
        end
        s2  :begin
            if(key == 0)
                next_state = s2;
            else
                next_state = IDLE;
        end
        default:begin
            next_state = IDLE;        
        end
    endcase

第三段:
确定输出状态,使用时序逻辑进行实现。

代码如下:

	always@(posedge clk)
    if(!rst_n)begin
        flag2 <= 0;
        cnt <= 0;
    end
    else
       case(cur_state)
           IDLE :begin
                flag <= 0;
                cnt <= 0;
           end
           s0  :begin
                if(cnt == DELAY - 1)
                    cnt <= 32'b0;
                else
                    cnt <= cnt + 32'b1;
           end
           s1  :begin
                flag <= 1;
                cnt <= 0;
           end
           s2  :begin
                flag <= 0;
                cnt <= 0;
           end
           default:begin
                flag <= 0;
                cnt <= 0;
           end
       endcase 

五、顶层模块编写

1.确定输入输出

输入:系统时钟复位信号按键
输出:4个led

2.顶层模块编写

将按键消抖模块例化到顶层模块。

代码实现:

	`timescale 1ns / 1ps
	module TOP(
	    input       	 clk      	 ,
	    input       	 rst_n       ,
	    input       	 key         ,
	    output reg [3:0] led
	    );
	wire                flag;
	Key2 key_1(
	    .clk        	(clk 	)       ,
	    .rst_n      	(rst_n  )       ,
	    .key        	(key    )       ,
	    .flag       	(flag   )
	    );
	always@(posedge clk)
	    if(!rst_n)
	        led <= 0;
	     else if(flag == 1)
	        led <= led + 1;
	     else
	        led <= led;
	endmodule

六、实验结果

1.仿真文件编写

代码实现:

	`timescale 1ns / 1ps
	module test();
	reg         clk;
	reg         rst_n;
	reg         key;
	TOP top(
	    .clk                    (clk   ),
	    .rst_n                  (rst_n ),
	    .key                    (key   ),
	    .led                    (led   )
	    );
	initial
	    begin
	        clk = 1;
	        rst_n = 0;
	        #100 rst_n = 1;
	    end
	always #1 clk = ~clk;
	initial
	    begin
	        key = 1;
	        #200 key = 0;
	        
	    end
	endmodule


2.仿真结果

仿真截图:

在这里插入图片描述


七、总结

1.了解按键消抖原理
2.了解状态机编写方法
3.学会描述并绘画状态图
重点:
状态机编写逻辑