FPGA花式点灯————基于按键三段式状态机消抖

一、状态机分类

1、Moore型状态机:

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

2、Mearly型状态机:

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

二、状态机写法分类

1、一段式

把状态跳转逻辑,状态机状态和状态机结果都放到一个always块里。

在信号少的情况下容易理解,信号多容易造成出错,且复杂。

2、二段式

把状态跳转逻辑用always组合逻辑编写,状态机状态用always时许逻辑编写,状态机结果用assign组合逻辑输出。

结果输出用组合逻辑容易出现毛刺现象。

3、三段式

把状态跳转逻辑用always组合逻辑编写,状态机状态用always时序逻辑编写,状态机结果用always组合逻辑输出。

描述方式使得FSM做到了同步寄存器输出,消除了组合逻辑输出不稳定与毛刺隐患,更有利于时序稳定

三、状态编码方式

1、二进制编码:

 parameter IDLE              =  3'b001;
 parameter FILTER_DOWN       =  3'b010;
 parameter HOLD              =  3'b011;
 parameter FILTER_UP         =  3'b100;

2、格雷码:

 parameter IDLE              =  3'b001;
 parameter FILTER_DOWN       =  3'b011;
 parameter HOLD              =  3'b010;
 parameter FILTER_UP         =  3'b110;

3、独热码:(可提高电路速度和可靠性)

    parameter IDLE              =  4'b0001;
    parameter FILTER_DOWN       =  4'b0010;
    parameter HOLD              =  4'b0100;
    parameter FILTER_UP         =  4'b1000;

四、三段式状态机设计方法

1、状态空间定义

2、状态跳转(时序逻辑)

3、下一个状态判断(组合逻辑)

4、各个状态下的判断(组合逻辑)

五、三段式状态机编写

1、题目分析

本次设计使用三段式状态机进行编写,实现按键消抖同时控制led亮灭,消抖原理如下:

                 

前沿抖动以及后沿抖动持续时间约为5-10ms,稳定状态持续时间位20-40ms

2、设计分析

当前沿抖动时,定时器开始计数,待20ms之后读取按键状态,进行状态跳转。当后沿抖动时,定时器开始计数,待20ms之后读取按键状态,进行状态跳转。

3、状态图

        

4、三段式状态机编写

(1)确认输入输出

输入:系统时钟,复位信号,按键信号

输出:led标志信号

`timescale 1ns / 1ps
//


module key_shake(
input sys_clk,
input sys_rst_n,
input key,
output reg led
    );

(2)状态编码

本次采用独热码

    parameter IDLE              =  4'b0001;                  //led = 1,key_flag = 1
    parameter FILTER_DOWN       =  4'b0010;                  //key_flag = 0
    parameter HOLD              =  4'b0100;                  //led = 0,key_flag = 1
    parameter FILTER_UP         =  4'b1000;                  //key_flag = 0

(3)其他关键信号以及标准定义

 parameter cntmax            =  15'b100_1110_0001_1111;   //20ms
 reg [3:0]  current_state;
 reg [3:0]  next_state;
 reg [14:0] cnt;
 reg        key_flag;
 reg        cnt_flag;

(4)状态机编写

第一段:

状态切换,使用时序逻辑实现

///
//状态转换    
    always @ (posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            current_state  <=  IDLE;
        end
        else begin
            current_state <= next_state;
        end
    end
第二段:

根据当前状态确定下一状态,使用组合逻辑实现

/
 //转移条件
    always @ ( * )begin
        case(current_state)
            IDLE:begin
                if( !key && key_flag)begin   //kry_flag = 1
                    next_state =  FILTER_DOWN;
                end
                else begin
                    next_state = IDLE;
                end
            end
            FILTER_DOWN:begin
                if( cnt_flag )begin   //key_flag = 0   cnt_flag = 1
                    if(!key )begin//
                        next_state = HOLD;
                    end//
                    else begin//
                        next_state = IDLE;//
                    end//
                end
                else begin
                    next_state = FILTER_DOWN;
                end
            end
            HOLD:begin
                if(!key && key_flag)begin  //key_flag = 1 
                    next_state = FILTER_UP;
                end
                else begin
                    next_state = HOLD;
                end
            end
            FILTER_UP:begin
                if(cnt_flag )begin   //key_flag = 
                    if(!key )begin //
                        next_state = IDLE;  
                    end//
                    else begin//
                        next_state = HOLD;//
                    end//
                end
                else begin
                    next_state = FILTER_UP;
                end
            end
            default:begin
                next_state = IDLE;
            end
        endcase
    end
第三段:

确认输出状态,使用组合逻辑实现


 //状态输出
    always @ ( posedge sys_clk )begin
        case(current_state)
            IDLE:begin
                led =  1'b1;
//                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b1;
                end
            end
            FILTER_DOWN:begin
                if( cnt == cntmax )begin
                    cnt_flag = 1'b1;
                    cnt = 15'b0;
                end
                else begin
                    cnt_flag = 1'b0;
                    cnt = cnt + 15'b1;
                end
                if(!key)begin
                    key_flag = 1'b0;
                end
            end
            HOLD:begin
                led =  1'b0;
//                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b1;
                end    
            end
            FILTER_UP:begin
            if( cnt == cntmax )begin
                cnt_flag = 1'b1;
                cnt = 15'b0;
                end
                else begin
                    cnt_flag = 1'b0;
                    cnt = cnt + 15'b1;
                end
                if(!key)begin
                    key_flag = 1'b0;
                end
            end
            default:begin
                led =  1'b1;
                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b0;
                end
            end
            endcase
    end 
endmodule   

六、ila文件编写

///
ila_0 your_instance_name (
	.clk(sys_clk), // input wire clk


	.probe0(sys_rst_n), // input wire [0:0]  probe0  
	.probe1(key), // input wire [0:0]  probe1 
	.probe2(led), // input wire [0:0]  probe2 
	.probe3(key_flag), // input wire [0:0]  probe3 
	.probe4(cnt_flag), // input wire [0:0]  probe4 
	.probe5(current_state), // input wire [3:0]  probe5 
	.probe6(next_state), // input wire [3:0]  probe6 
	.probe7(cnt) // input wire [14:0]  probe7 
);

七、综合代码(可直接复制)

`timescale 1ns / 1ps
//


module key_shake(
input sys_clk,
input sys_rst_n,
input key,
output reg led
    );

    parameter IDLE              =  4'b0001;                  //led = 1,key_flag = 1
    parameter FILTER_DOWN       =  4'b0010;                  //key_flag = 0
    parameter HOLD              =  4'b0100;                  //led = 0,key_flag = 1
    parameter FILTER_UP         =  4'b1000;                  //key_flag = 0
    parameter cntmax            =  15'b100_1110_0001_1111;   //20ms
    
    reg [3:0]  current_state;
    reg [3:0]  next_state;
    reg [14:0] cnt;
    reg        key_flag;
    reg        cnt_flag;
///
ila_0 your_instance_name (
	.clk(sys_clk), // input wire clk


	.probe0(sys_rst_n), // input wire [0:0]  probe0  
	.probe1(key), // input wire [0:0]  probe1 
	.probe2(led), // input wire [0:0]  probe2 
	.probe3(key_flag), // input wire [0:0]  probe3 
	.probe4(cnt_flag), // input wire [0:0]  probe4 
	.probe5(current_state), // input wire [3:0]  probe5 
	.probe6(next_state), // input wire [3:0]  probe6 
	.probe7(cnt) // input wire [14:0]  probe7 
);
///
//状态转换    
    always @ (posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            current_state  <=  IDLE;
        end
        else begin
            current_state <= next_state;
        end
    end
    
 /
 //转移条件
    always @ ( * )begin
        case(current_state)
            IDLE:begin
                if( !key && key_flag)begin   //kry_flag = 1
                    next_state =  FILTER_DOWN;
                end
                else begin
                    next_state = IDLE;
                end
            end
            FILTER_DOWN:begin
                if( cnt_flag )begin   //key_flag = 0   cnt_flag = 1
                    if(!key )begin//
                        next_state = HOLD;
                    end//
                    else begin//
                        next_state = IDLE;//
                    end//
                end
                else begin
                    next_state = FILTER_DOWN;
                end
            end
            HOLD:begin
                if(!key && key_flag)begin  //key_flag = 1 
                    next_state = FILTER_UP;
                end
                else begin
                    next_state = HOLD;
                end
            end
            FILTER_UP:begin
                if(cnt_flag )begin   //key_flag = 
                    if(!key )begin //
                        next_state = IDLE;  
                    end//
                    else begin//
                        next_state = HOLD;//
                    end//
                end
                else begin
                    next_state = FILTER_UP;
                end
            end
            default:begin
                next_state = IDLE;
            end
        endcase
    end
 
 
 //状态输出
    always @ ( posedge sys_clk )begin
        case(current_state)
            IDLE:begin
                led =  1'b1;
//                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b1;
                end
            end
            FILTER_DOWN:begin
                if( cnt == cntmax )begin
                    cnt_flag = 1'b1;
                    cnt = 15'b0;
                end
                else begin
                    cnt_flag = 1'b0;
                    cnt = cnt + 15'b1;
                end
                if(!key)begin
                    key_flag = 1'b0;
                end
            end
            HOLD:begin
                led =  1'b0;
//                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b1;
                end    
            end
            FILTER_UP:begin
            if( cnt == cntmax )begin
                cnt_flag = 1'b1;
                cnt = 15'b0;
                end
                else begin
                    cnt_flag = 1'b0;
                    cnt = cnt + 15'b1;
                end
                if(!key)begin
                    key_flag = 1'b0;
                end
            end
            default:begin
                led =  1'b1;
                cnt = 15'b0;
                if(key)begin
                    key_flag = 1'b0;
                end
            end
            endcase
    end    
endmodule

八、实验结果

1、ila截图

(1)按键下降沿

(2)按键上升沿

九、总结

1、了解消抖原理

2、了解状态机编写方法

3、学习描绘状态转换图

4、学习状态机编写逻辑

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值