五、状态机消抖单个按键与状态机实现流水灯

1、fsm_key_filter.v

module fsm_key_filter(
    input   wire        clk   ,
    input   wire        rst_n ,
    input   wire        key   ,

    output  reg         key_flag
);

//定义状态变量独热码编码
localparam IDLE         = 4'b0001,//表示空闲状态
           FILETER_DOWN = 4'b0010,//按键按下前抖动状态
           HOLD_DOWN    = 4'b0100,//按键稳定时的状态
           FILETER_UP   = 4'b1000;//按键松手后,后抖动状态

//定义状态
reg [3:0] state_c ;
reg [3:0] state_n ;

//定义状态转移条件
wire [3:0]  idle_2_F_D;
wire [3:0]  F_D_2_H_D ;
wire [3:0]  F_D_2_idle;
wire [3:0]  H_D_2_F_U ;
wire [3:0]  F_U_2_idle;
wire [3:0]  F_U_2_H_D ;

//计数器模块定义
parameter TIME_20MS = 20'd999_999;
reg [19:0] cnt_20ms ;
wire       add_cnt  ;
wire       end_cnt  ;

//寄存按键信号同步打拍,检测下降沿与上升沿
reg        key_r0   ;//同步信号寄存
reg        key_r1   ;//打拍信号
wire       nedge    ;//下降沿信号
wire       pedge    ;//上升沿信号

//状态机设计三段式时序逻辑描述状态转移
//第一段
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state_c <= IDLE ;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段,组合逻辑,更据状态转移图描述状态转移条件
always@(*)begin
    case(state_c)
        IDLE : begin
            if(idle_2_F_D)begin//条件成立状态转移
                state_n = FILETER_DOWN;
            end
            else begin
                state_n = IDLE ;
            end
        end
        FILETER_DOWN :begin
            if(F_D_2_idle)begin
                state_n = IDLE;
            end
            else if(F_D_2_H_D)begin
                state_n = HOLD_DOWN;
            end
            else begin
                state_n = FILETER_DOWN;
            end
        end
        HOLD_DOWN : begin
            if(H_D_2_F_U)begin
                state_n = FILETER_UP ;
            end
            else begin
                state_n = HOLD_DOWN ;
            end
        end
        FILETER_UP : begin
             if(F_U_2_H_D)begin
                state_n = HOLD_DOWN ;
            end
            else if(F_U_2_idle)begin
                state_n = IDLE ;
            end
            else begin
                state_n = FILETER_UP ;
            end
        end
        default : state_n = IDLE ;
    endcase
end

assign idle_2_F_D = (state_c == IDLE        ) && nedge  ;
assign F_D_2_idle = (state_c == FILETER_DOWN) && pedge  ;
assign F_D_2_H_D  = (state_c == FILETER_DOWN) && end_cnt;
assign H_D_2_F_U  = (state_c == HOLD_DOWN   ) && pedge  ;
assign F_U_2_idle = (state_c == FILETER_UP  ) && end_cnt;
assign F_U_2_H_D  = (state_c == FILETER_UP  ) && nedge  ;   

//第三段,描述输出
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
     key_flag <= 1'b0;    
   end 
   else if(F_D_2_H_D)begin
     key_flag <= ~key;
   end
   else 
     key_flag <= 1'b0;
end

//20ms计数器模块
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 20'd0;
    end
    else if(add_cnt)begin
        if(end_cnt)begin
            cnt_20ms <= 20'd0;
        end
        else begin
            cnt_20ms <= cnt_20ms +1'd1;
        end
    end
    else begin
        cnt_20ms <= 20'd0;
    end
end

assign add_cnt = (state_c == FILETER_DOWN) || (state_c == FILETER_UP);
assign end_cnt = (add_cnt && cnt_20ms == TIME_20MS );

//延时打拍按键信号
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= 1'b1;
        key_r1 <= 1'b1;
    end
    else begin
        key_r0 <= key ;//同步
        key_r1 <= key_r0;//打拍
    end
end

//nedge pedge
assign nedge = key_r1 & (~key_r0);//检测下降沿
assign pedge = (~key_r1) & key_r0;//检测上升沿

endmodule

2、fsm_led.v

//状态机实现流水灯
module fsm_led (
    input   wire        clk     ,
    input   wire        rst_n   , 

    output  reg  [3:0]  led          
);

//对应的状态进行独热码编码定义
//一般情况下对状态进行编码定义一般采用局部变量定义
localparam IDLE = 4'b0001 ,//初始状态,led[0]亮
           S1   = 4'b0010,
           S2   = 4'b0100,
           S3   = 4'b1000;

//当前状态和次态的位宽由状态机有几个状态决定的
reg [3:0]       state_c;//当前状态
reg [3:0]       state_n;//次态

//定义状态跳转条件
wire    idle_2_S1;
wire    S1_2_S2  ;
wire    S2_2_S3  ;
wire    S3_2_idle;

//定义计数器参数
parameter TIME_500MS = 25'd24_999_999;
reg [24:0]  cnt_500ms ;
wire        add_cnt_500ms;
wire        end_cnt_500ms;

//三段式状态机
//第一段,时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE ;//c-Current,初始状态为IDLE
    end
    else begin
        state_c <=  state_n; //时钟上升沿将次态给现态
    end
end

//第二段组合逻辑,描述状态转移条件
always @(*)begin
    case(state_c) //以现态作为判断条件
        IDLE : begin
            if(idle_2_S1)begin
                state_n = S1 ;
            end
            else begin
                state_n = state_c ; //条件未成立,保持原来状态不变
            end
        end
        S1  : begin
            if(S1_2_S2)begin
                state_n = S2 ;
            end
            else begin
                state_n = S1 ;
            end
        end
        S2  : begin
            if(S2_2_S3)begin
                state_n = S3 ;
            end
            else begin
                state_n = S2 ;
            end
        end
        S3 : begin
            if(S3_2_idle)begin
                state_n = IDLE ;
            end
            else begin
                state_n = S3;
            end
        end
        default : state_n = IDLE ;
    endcase
end

//描述状态转移跳转的条件
assign idle_S1  = state_c == IDLE && end_cnt_500ms;
assign S1_S2    = state_c == S1   && end_cnt_500ms;
assign S2_S3    = state_c == S2   && end_cnt_500ms;
assign S3_idle  = state_c == S3   && end_cnt_500ms;

//第三段,可时序也可组合,描述输出状态
always @(*)begin
    case(state_c)
        IDLE : led = 4'b0001;
        S1   : led = 4'b0010;
        S2   : led = 4'b0100;
        S3   : led = 4'b1000;
        default : led = led;
    endcase
end

//计数器模块
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_500ms <= 25'd0;
    end
    else if(add_cnt_500ms)begin
        if(end_cnt_500ms)begin
            cnt_500ms <= 25'd0;
        end
        else begin
            cnt_500ms <= cnt_500ms + 1'd1;
        end
    end
    else begin
        cnt_500ms <= cnt_500ms;
    end
end

assign add_cnt_500ms = 1'b1;
assign end_cnt_500ms = add_cnt_500ms && cnt_500ms == TIME_500MS;

endmodule

3、fsm_key_filter.v

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

reg     clk   ;
reg     rst_n ;
reg     key   ;

wire    key_flag;

parameter CYCLE = 20;
defparam u_fsm_key_filter.TIME_20MS = 100;

//模拟复位信号与时钟信号初始化
initial begin
    clk    = 1'b1;
    rst_n <= 1'b0;
    #15
    rst_n <= 1'b1;
end

//模拟时钟信号
always #(CYCLE/2) clk = ~clk;

//模拟按键按下抖动过程
initial begin
    key = 1'b1;
    #(1000*CYCLE)
//按下时的抖动
    key = 1'b0;
    #(15*CYCLE)
    key = 1'b1;
    #(20*CYCLE)
    key = 1'b0;
    #(9*CYCLE)
    key = 1'b1;
    #(6*CYCLE)
    key = 1'b0;
    #(1000*CYCLE)//稳定状态

//按键松手时的后抖动
    key = 1'b1;
    #(10*CYCLE)
    key = 1'b0;
    #(8*CYCLE)
    key = 1'b1;
    #(9*CYCLE)
    key = 1'b0;
    #(15*CYCLE)
    key = 1'b1;
    #(10000*CYCLE)
    $stop;
end

fsm_key_filter u_fsm_key_filter(
    . clk      (clk),
    . rst_n    (rst_n),
    . key      (key),
 
    . key_flag (key_flag)
);


endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值