实验十五 摩尔状态机序列检测器“1101”

实验十五 摩尔状态机序列检测器

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

按键消抖模块debounce_button:

由于实际的拨码开关和按键开关都是机械式的设备,开关动作来回抖动多次后才能稳定下来,这个过程就会使得信号产生抖动。因此,如果用按键来产生时钟信号,为了一次按键得到一次上升沿(或下降沿),那么需要对按键输入先进行消抖处理
`timescale 1ns / 1ps

// Module Function:按键消抖
 
module debounce_button (clk,rst,key,key_pulse,en, data_i, data_o,dout);
 
        parameter       N  =  1;                      //要消除的按键的数量
 
	input             clk;
        input             rst;
        input 	[N-1:0]   key;                        //输入的按键					
	output  [N-1:0]   key_pulse;                  //按键动作产生的脉冲	
	
	
	input en;
    input [7:0] data_i;
    output  data_o,dout;
	
 
        reg     [N-1:0]   key_rst_pre;                //定义一个寄存器型变量存储上一个触发时的按键值
        reg     [N-1:0]   key_rst;                    //定义一个寄存器变量储存储当前时刻触发的按键值
 
        wire    [N-1:0]   key_edge;                   //检测到按键由高到低变化是产生一个高脉冲
 
        //利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
        always @(posedge clk  or  negedge rst)
          begin
             if (rst) begin
                 key_rst <= {N{1'b1}};                //初始化时给key_rst赋值全为1,{}中表示N个1
                 key_rst_pre <= {N{1'b1}};
             end
             else begin
                 key_rst <= key;                     //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
                 key_rst_pre <= key_rst;             //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
             end    
           end
 
        assign  key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
 
        reg	[17:0]	  cnt;                       //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器     
 
        //产生20ms延时,当检测到key_edge有效是计数器清零开始计数
        always @(posedge clk or negedge rst)
           begin
             if(rst)
                cnt <= 18'h0;
             else if(key_edge)
                cnt <= 18'h0;
             else
                cnt <= cnt + 1'h1;
             end  
 
        reg     [N-1:0]   key_sec_pre;                //延时后检测电平寄存器变量
        reg     [N-1:0]   key_sec;                    
 
 
        //延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
        always @(posedge clk  or  negedge rst)
          begin
             if (rst) 
                 key_sec <= {N{1'b1}};                
             else if (cnt==18'h3ffff)
                 key_sec <= key;  
          end
       always @(posedge clk  or  negedge rst)
          begin
             if (rst)
                 key_sec_pre <= {N{1'b1}};
             else                   
                 key_sec_pre <= key_sec;             
         end      
       assign  key_pulse = key_sec_pre & (~key_sec);     //在产生一个时钟的高脉冲后,传入parallel_serial的clk中,计数,往后读取并行的输入数据
       
       
       
       parallel_serial u1(
.clk(key_pulse), .reset(rst), .en(en), .data_i(data_i), .data_o(data_o),.dout(dout)/*,.clk_real()*/
    );
       
 
endmodule

并转串模块parallel_serial:

运用并转串输出模块,将八个拨码开关作为外部二进制码流的输入。用一个按键作为一个启动检测信号,另用一个按键来模拟 clk 信号,检测开关序列中是否存在“1101”序列,按下启动检测信号后,每按一次模拟 clk 的按键便传入一个开关的值,这个值时传入到摩尔状态机序列检测器seq_det_moore中的。
`timescale 1ns / 1ps

module parallel_serial(
clk, reset, en, data_i, data_o,dout/*,clk_real*/
    );
input clk, reset,en;
input [7:0] data_i;
output  data_o,dout;
//input clk_real;

reg [7:0]  data_buf;

	localparam N = 3; //使用低16位对50Mhz的时钟进行分频(50MHZ/2^16)
	reg [N-1:0] regN; //高两位作为控制信号,低16位为计数器,对时钟进行分频
	
	
	/*reg flag;
    always @ (posedge clk_real) 
        begin
            flag <=clk;
        end 
*/
	
	
	
	
    always@(posedge clk, posedge reset)
	begin
		if(reset)                    //FPGA板上配有 5 个按键,当按键按下时,表示 FPGA 的相应输入脚为高电平。所以平时的状态为低电平,即按键是未按下的  “弹起”  状态。
			regN <= 0;
		else
			regN <= regN + 1;
	end
	
always@(posedge clk or negedge reset)
if(reset)
data_buf <= 8'b0;
else if(en)                    
/*当en=0时,regN只能计数,不能执行以下传输代码,即不能改变data_buf的值了,不能再往seq_det_moore中传入下一个data_o了,
但是seq_det_moore还是会随着clk的进行,而继续检索1101序列,此时data_o的值是之前传入的值,不变。
只有在en=1后的下一次posedge clk时,才可以继续传输值*/
begin
case(regN[N-1:N-3])
3'b0: data_buf <= data_i[7:0];
3'b1: data_buf <= {data_i[6:0],data_i[7]};
3'd2: data_buf <= {data_i[5:0],data_i[7:6]};
3'd3: data_buf <= {data_i[4:0],data_i[7:5]};
3'd4: data_buf <= {data_i[3:0],data_i[7:4]};
3'd5: data_buf <= {data_i[2:0],data_i[7:3]};
3'd6: data_buf <= {data_i[1:0],data_i[7:2]};
3'd7: data_buf <= {data_i[0],data_i[7:1]};
default: data_buf <= data_i[7:0];
endcase
end


assign data_o = data_buf[7];

seq_det_moore u1(
    .clk(clk),
    .reset(reset),
    .din(data_o),//每按一次模拟 clk 的按键便传入一个开关的值
    .dout(dout)
    );

endmodule

摩尔状态机序列检测器seq_det_moore:

这里实现的摩尔状态机序列是没有重叠的检测方式,即“1101101”中只有一个“1101”序列;但如果是有重叠的序列检测方式,那么就有两个“1101”序列。代码实现方式只需要修改最后一个状态s4,在din=1时,变成指向s2即可(这里是指向s1的)。

摩尔状态机

`timescale 1ns / 1ps
 
 
module seq_det_moore(
    input clk,
    input reset,
    input din,
    output reg dout
    );
    //状态声明
    localparam [2:0]
    s0 = 3'b000,
    s1 = 3'b001,
    s2 = 3'b010,
    s3 = 3'b011,
    s4 = 3'b100;
    
    reg [2:0] current_state,next_state;
    
    always @(posedge clk, posedge reset)
    begin
        if(reset)            //FPGA板上配有 5 个按键,当按键按下时,表示 FPGA 的相应输入脚为高电平。所以平时的状态为低电平,即按键是未按下的  "弹起"  状态。
            current_state <= s0;
        else
            current_state <= next_state;
    end
    
    always @ *
    begin
        case(current_state)
        s0:
            if(din == 1'b1) next_state = s1;
            else next_state = s0;
        s1:
            if(din == 1'b1) next_state = s2;
            else next_state = s0;
        s2:
            if(din == 1'b0) next_state = s3;
            else next_state = s2;
        s3:
            if(din == 1'b1) next_state = s4;
            else next_state = s0;
        s4:
            if(din == 1'b1) next_state = s1;
            else next_state = s0;
        default: next_state = s0;
        
        endcase
    
    end
    
    always @*
    begin
        if(next_state == s4) dout = 1;
        else dout = 0;
    end
    
    
endmodule

仿真文件parallel_serial_tb如下:

对并转串模块的仿真
`timescale 1ns / 1ps

module parallel_serial_tb;
	reg clk,reset,en;
	wire data_o;
	wire dout;
	reg [7:0] data_i;

 
   // Note: CLK must be defined as a reg when using this method
   
   parameter PERIOD = 10;
 
   always begin
           #(PERIOD/2)  clk = ~clk;
   end  
				
	initial begin
    clk   = 0;
	reset = 1'b1;


	 @(posedge clk)   reset = 1'b0;
     en=1'b1;
     data_i = 8'b11011101;
	end
 
  parallel_serial u1(
.clk(clk), .reset(reset), .en(en), .data_i(data_i), .data_o(data_o) , .dout(dout)
    );
 
 
endmodule

parallel_serial_tb的仿真结果:

每次一次时钟上升沿,便检测一次data_o的值。当检测到“1101”时,dout输出1,并在下个一时钟上升沿来的时候,dout的值将会变化。

parallel_serial_tb的仿真结果

引脚分配的代码:

注意系统时钟clk的引脚要分配到fgpa固定的那个时钟引脚上。手动输入的key[0],并转串模板的“时钟”也是“选择信号”,按键消抖中的“被消抖的按键”,绑定在想要的成为输入按键的地方就好。我这里key[0]绑定的是T17reset绑定的是U18en绑定的是R2(要赋为1才能进行检测操作,否则上板子不会亮的),输入data_i右下角八个键(reset我没有进行消抖,因为不消抖也没什么影响)
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_i[0]}]
set_property PACKAGE_PIN W13 [get_ports {data_i[7]}]
set_property PACKAGE_PIN W14 [get_ports {data_i[6]}]
set_property PACKAGE_PIN V15 [get_ports {data_i[5]}]
set_property PACKAGE_PIN W15 [get_ports {data_i[4]}]
set_property PACKAGE_PIN W17 [get_ports {data_i[3]}]
set_property PACKAGE_PIN W16 [get_ports {data_i[2]}]
set_property PACKAGE_PIN V16 [get_ports {data_i[1]}]
set_property PACKAGE_PIN V17 [get_ports {data_i[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports data_o]
set_property IOSTANDARD LVCMOS33 [get_ports dout]
set_property IOSTANDARD LVCMOS33 [get_ports en]

set_property PACKAGE_PIN R2 [get_ports en]

set_property PACKAGE_PIN E19 [get_ports data_o]
set_property PACKAGE_PIN U16 [get_ports dout]


set_property IOSTANDARD LVCMOS33 [get_ports {key[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key_pulse[0]}]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst]
set_property PACKAGE_PIN U18 [get_ports rst]
set_property PACKAGE_PIN T17 [get_ports {key[0]}]
set_property PACKAGE_PIN L1 [get_ports {key_pulse[0]}]

以上就是摩尔状态机序列检测器啦~

我用的板子是XILINX的BASYS 3
上板子的实验输入按键。(注意最左边的是en按键,要赋为1才能进行检测)
(=.=点击这里查看上板实验操作演示的视频哦=.=)
具体按键
能给我你的👍吗?

  • 28
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗娜mei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值