【FPGA】序列检测101

一、功能介绍

该项目由state_dete、seg、seg_j、key_filter、top五个模块组成
复位按键:复位
按键1:模拟序列1输入
按键2:模拟序列0输入
LED:检测到数值101翻转
数码管:显示输入值和101出现次数(十六进制显示)

二、时序图和状态转换图

时序图
在这里插入图片描述

状态转换图

在这里插入图片描述

三、程序设计

1.顶层模块

// A code block
var foo = 'bar';
// An highlighted block
//模块顶层
module top( 
    input				clk		,
    input				rst_n	,
    input		[2:0]	key_in	,
    output		[3:0]   led	    ,
    output      [5:0]   seg_sel ,
    output      [7:0]   segment	
);							 

//状态机消抖模块
wire            [2:0]       key_down;
 key_filter #( .KEY_W (3),.TIME_20MS ( 5)) u_key_filter(
    /*input 			         */     .clk	(clk),
    /*input 			         */     .rst_n	(rst_n),
    /*input 		[KEY_W - 1:0]*/	    .key_in	(key_in),
    /*output 		[KEY_W - 1:0]*/	    .key_out(key_down)	
);

//状态机序列检测
wire                [19:0]          value;  
 state_dete u_state_dete( 
    /*input				*/.clk		(clk     ),
    /*input				*/.rst_n	(rst_n   ),
    /*input		[1:0]	*/.key_down (key_down),
                          .value    (value),
 /*output	reg	  [3:0] */.led	    (led)
);

//数码管数字显示
wire             [23:0]             din;
seg_j u_seg_j( 
    /*input				*/  .clk	   (clk)     ,
    /*input				*/  .rst_n	   (rst_n)   ,
    /*input		[20:0]	*/  .value	   (value)   ,
    /*input       [1:0] */  .key_down  (key_down),
    /*output      [23:0]*/  .din       (din)
);								 	      

//数码管驱动
  seg u_seg(
    /*input             */ .clk      (clk)      ,
    /*input             */ .rst_n    (rst_n)    ,
    /*input       [23:0]*/ .din      (din)      ,
    /*input       [5:0] */ .point    (6'b111110),
    /*output  reg [5:0] */ .seg_sel  (seg_sel)  ,
    /*output  reg [7:0] */ .segment  (segment)
);                   
endmodule


2.按键消抖模块

// A code block
var foo = 'bar';
// An highlighted block
module key_filter # (parameter KEY_W = 3,TIME_20MS = 5)(
    input 			            clk		,
    input 			            rst_n	,
    input 		[KEY_W - 1:0]	key_in	,
    output 		[KEY_W - 1:0]	key_out	         
);
    // 参数定义
    localparam IDLE  = 4'b0001;     //初始状态 
    localparam DOWN  = 4'b0010;     //按键按下抖动
    localparam HOLD  = 4'b0100;     //按键按下后稳定
    localparam UP    = 4'b1000;     //按键上升抖动
    // 信号定义
    reg     [3:0]           state_c         ;   //现态
    reg     [3:0]           state_n         ;   //次态

    // 状态转移条件定义
    wire                    idle2down       ;
    wire                    down2idle       ;
    wire                    down2hold       ;
    wire                    hold2up         ;
    wire                    up2idle         ;

    reg     [KEY_W - 1:0]   key_r0          ;   //同步
    reg     [KEY_W - 1:0]   key_r1          ;   //打拍
    wire    [KEY_W - 1:0]   nedge           ;   //下降沿
    wire    [KEY_W - 1:0]   pedge           ;   //上升沿

    // 20ms计数器
    reg     [19:0]          cnt_20ms        ;
    wire                    add_cnt_20ms    ;
    wire                    end_cnt_20ms    ;

    reg [KEY_W - 1:0] key_out_r;    // 输出寄存

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end


    assign idle2down = (state_c == IDLE) && nedge;                      // 检测到下降沿
    assign down2idle = (state_c == DOWN) && (pedge && ~end_cnt_20ms);   // 计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
    assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);   // 计时到20ms时没有出现上升沿标志按键按下后保持稳定
    assign hold2up   = (state_c == HOLD) && (pedge);                    // 检测到上升沿跳转到上升态
    assign up2idle   = (state_c == UP)   && end_cnt_20ms;               // 计数器计数到20ms跳转到初始态

    always@(*)begin
        case(state_c)
            IDLE: begin
                if(idle2down)begin
                    state_n = DOWN;
                end
                else begin
                    state_n = state_c;
                end
            end
            DOWN: begin
                if(down2idle)begin
                    state_n = IDLE;
                end
                else if(down2hold)begin
                    state_n = HOLD;
                end
                else begin
                    state_n = state_c;
                end
            end
            HOLD: begin
                if(hold2up)begin
                    state_n = UP;
                end
                else begin
                    state_n = state_c;
                end
            end
            UP: begin
                if(up2idle)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
            default:state_n = state_c;
        endcase
    end


    // 20ms计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_20ms <= 0;
        end
        else if(add_cnt_20ms)begin
            if(pedge || end_cnt_20ms)begin
                cnt_20ms <= 0;
            end
            else begin
                cnt_20ms <= cnt_20ms + 1'b1;
            end
        end
    end
    assign add_cnt_20ms = state_c == DOWN || state_c == UP;             // 当按键按下或上弹时开始计数
    assign end_cnt_20ms = add_cnt_20ms && (cnt_20ms == TIME_20MS - 1);  // 当计数到最大值或检测到上升沿计数器清零


    // 同步打拍
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            key_r0 <= {KEY_W{1'b1}};
            key_r1 <= {KEY_W{1'b1}};
        end
        else begin
            key_r0 <= key_in;
            key_r1 <= key_r0;
        end
    end

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


    // 按键赋值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            key_out_r <= {KEY_W{1'b0}};
        end
        else if(hold2up) begin
            key_out_r <= ~key_r1;
        end
        else begin
            key_out_r <= {KEY_W{1'b0}};
        end
    end

    assign key_out = key_out_r;

endmodule


3.序列状态转换模块

// A code block
var foo = 'bar';
// An highlighted block
/*******************************************
-------------------------------------------
Component name:state_dete
Author        :lcy 
time          :2021/12/4 
Description   :三段式状态机序列检测将序列出现
            101的次数传输出去出现一次led翻转 
src           :
--------------------------------------------
********************************************/

module state_dete( 
    input				        				clk		,
    input				        				rst_n   ,
    input		  		        [2:0]	        key_down,
    output	    reg	            [3:0]         	led     ,	
    output      reg             [19:0]          value
);								 
    //参数定义			 
    localparam  //工程文件doc状态图
                A = 4'b0001,
                B = 4'b0010,
                C = 4'b0100,
                D = 4'b1000;                   
    //中间信号定义		 
    reg			[3:0]      current_state  ;
    reg		    [3:0]	   next_state     ;

    wire        AB  ;
    wire        BC  ;    
    wire        CA  ;
    wire        CD  ;
    wire        DB  ;
    wire        DC  ;

//状态机三段式   当前状态
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            current_state <= A;
        end 
        else begin 
           current_state <= next_state;
        end 
    end

//次状态
    always @(*)begin 
        case (current_state)
        A : begin if (AB) begin
                next_state = B;
            end
        else begin
            next_state = current_state;
            end   
        end
        B : begin if (BC) begin
                    next_state = C;
            end
        else begin
                next_state = current_state;
            end   
        end
        C : begin if (CD) begin
                next_state = D;
            end
        else if (CA) begin
                next_state = A;
            end
        else begin
                next_state = current_state;
            end
            
        end
        D : begin if (DB) begin
            next_state = B;
        end
        else if (DC) begin
                next_state = C;
            end
        else begin
                next_state = current_state;
            end    
        end
             default: begin
               next_state = current_state;
            end
        endcase
    end

//状态转换条件
 assign  AB = current_state == A && key_down[0] == 1;
 assign  BC = current_state == B && key_down[1] == 1;
 assign  CA = current_state == C && key_down[1] == 1;
 assign  CD = current_state == C && key_down[0] == 1;
 assign  DB = current_state == D && key_down[0] == 1;
 assign  DC = current_state == D && key_down[1] == 1;

//状态机检测到101  led翻转,并将出现的次数输出给seg_j
 always @(posedge clk or negedge rst_n)begin 
     if(!rst_n)begin
         value <= 0;
        led <= 4'b0000;
     end 
     else if(current_state == C && key_down[0] == 1)begin 
         led <= ~led;
         value <= value+1;
     end 

 end






                        
endmodule

4.数码管显示

// A code block
var foo = 'bar';
// An highlighted block
/*******************************************
-------------------------------------------
Component name:seg_j
Author        :lcy 
time          :2021/12/4 
Description   :将state_dete模块101出现的次数
            和按键产生的序列传输给seg驱动模块
src           :
--------------------------------------------
********************************************/
module seg_j( 
    input				clk		,
    input				rst_n	,
    input		[20:0]	value	,
    input       [1:0]   key_down,
    output      [23:0]  din


);								 	                   
    //中间信号定义		 
    
    reg        [3:0]    value_0;

    //按键按下显示1,0值
    always @(posedge clk or negedge rst_n)begin     
        if(!rst_n)begin
            value_0 <= 0;
        end 
        else if(key_down[0])begin 
            value_0 <= 1;
        end 
        else if (key_down[1]) begin
            value_0 <= 0;
        end
    end


assign din = {value_0,value[3:0],value[7:4],value[11:8],value[15:12],value[19:16]};       //数码管显示值

                        
endmodule

5.数码管驱动

// A code block
var foo = 'bar';
// An highlighted block
/*******************************************
-------------------------------------------
Component name:seg
Author        : 
time          :2021/12/4 
Description   :seg数码管驱动
src           :
--------------------------------------------
********************************************/

module  seg (
    input               clk      ,
    input               rst_n    ,
    input       [23:0]  din      ,
    input       [5:0]   point    ,
    output  reg [5:0]   seg_sel  ,
    output  reg [7:0]   segment
);

parameter DELAY = 1_000_00;
localparam  ZER = 7'b100_0000,
            ONE = 7'b111_1001,
            TWO = 7'b010_0100,
            THR = 7'b011_0000,
            FOU = 7'b001_1001,
            FIV = 7'b001_0010,
            SIX = 7'b000_0010,
            SEV = 7'b111_1000,
            EIG = 7'b000_0000,
            NIN = 7'b001_0000,
            A   = 7'b000_1000, 
            B   = 7'b000_0011,
            C   = 7'b100_0110,
            D   = 7'b010_0001,
            E   = 7'b000_0110,
            F   = 7'b000_1110;

reg [3:0] num;
reg       seg_point;

reg [19:0] cnt;

wire add_cnt;
wire end_cnt;

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt <= 0;
    end 
    else if(add_cnt)begin 
            if(end_cnt)begin 
                cnt <= 0;
            end
            else begin 
                cnt <= cnt + 1;
            end 
    end
   else  begin
       cnt <= cnt;
    end
end 

assign add_cnt = 1'b1;
assign end_cnt = add_cnt && cnt == DELAY - 1;

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        seg_sel <= 6'b111110;
    end 
    else if(end_cnt)begin
        seg_sel <= {seg_sel[4:0],seg_sel[5]};
        end
    else begin 
        seg_sel <= seg_sel;
    end 
end

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        num <= 4'b0;
        seg_point <= 1'b1;
    end 
    else begin
        case (seg_sel)
            6'b111110 : begin num <= din[3:0];seg_point <= point [0];end
            6'b111101 : begin num <= din[7:4];seg_point <= point [1];end
            6'b111011 : begin num <= din[11:8];seg_point <= point [2];end
            6'b110111 : begin num <= din[15:12];seg_point <= point [3];end
            6'b101111 : begin num <= din[19:16];seg_point <= point [4];end
            6'b011111 : begin num <= din[23:20];seg_point <= point [5];end
        default: begin num <= 4'b0;seg_point <= 1'b1;end
        endcase 
        
    end 
end

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        segment <= 8'b1111_1111;
    end 
    else begin 
        case (num)
            4'd0:  segment <= {seg_point,ZER};
            4'd1:  segment <= {seg_point,ONE}; 
            4'd2:  segment <= {seg_point,TWO}; 
            4'd3:  segment <= {seg_point,THR};     
            4'd4:  segment <= {seg_point,FOU};     
            4'd5:  segment <= {seg_point,FIV};     
            4'd6:  segment <= {seg_point,SIX};         
            4'd7:  segment <= {seg_point,SEV};     
            4'd8:  segment <= {seg_point,EIG};     
            4'd9:  segment <= {seg_point,NIN};
            4'd10: segment <= {seg_point,A};            
            4'd11: segment <= {seg_point,B};
            4'd12: segment <= {seg_point,C};
            4'd13: segment <= {seg_point,D};
            4'd14: segment <= {seg_point,E};
            4'd15: segment <= {seg_point,F};
            default: segment <= 8'b11111111;
        endcase
    end 
end

endmodule


四、tb仿真

1.tb程序

// A code block
var foo = 'bar';
// An highlighted block

`timescale 1ns/1ns
                
module top_tb();
//激励信号定义 
reg				    tb_clk  	;
reg				    tb_rst_n	;
reg         [2:0]   tb_key_in   ;
wire 		[3:0]   tb_led	    ;
wire        [5:0]   tb_seg_sel  ;
wire        [7:0]   tb_segment	;	

//输出信号定义	 
                                          
//时钟周期参数定义					        
    parameter		CLOCK_CYCLE = 20;    
 top u_top( 
    /*input				*/ .clk		(tb_clk)    ,
    /*input				*/ .rst_n	(tb_rst_n)  ,
    /*input		[2:0]	*/ .key_in	(tb_key_in) ,
    /*output	[3:0]   */ .led	    (tb_led)    ,
    /*output      [5:0] */ .seg_sel (tb_seg_sel),
    /*output      [7:0] */ .segment	(tb_segment)
);							                                                         			         		      		      
//产生时钟							       		
initial 		tb_clk = 1'b0;		       		
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;  		
integer i,j;                                                   
//产生激励							       				       																		            							            					                                                                     

initial begin
    tb_rst_n = 1'b1;
    #(CLOCK_CYCLE*4);
    tb_rst_n = 1'b0;
    #(CLOCK_CYCLE*4);
    tb_rst_n = 1'b1;


     repeat(10)begin //重复执行10次  ,顺序执行
            for(i=0;i<10000;i=i+1)begin   //按键0检测
            #2

            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b001;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b010;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b001;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b010;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b001;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b010;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b001;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b010;
            #(10*CLOCK_CYCLE);
            tb_key_in = 3'b011;
            #(10*CLOCK_CYCLE);

            end
            tb_key_in = 2'b11;

            //#(50*CLOCK_CYCLE);
            //for(i=0;i<20;i=i+1)begin  //按键1检测
            //j= {$random}%30;//j的取值范围就是0-30;
            //#2
            //tb_key_in[1] = {$random};//0 或者 1,模拟按键抖动过程
            //#(j*CLOCK_CYCLE);
            //end
//
            //tb_key_in = 2'b11;       
end

#(CLOCK_CYCLE);
end


endmodule 	
								       	

2.Modelsim仿真图
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值