我是靠谱客的博主 忧虑小天鹅,最近开发中收集的这篇文章主要介绍实验十五 摩尔状态机序列检测器“1101”按键消抖模块debounce_button:并转串模块parallel_serial:摩尔状态机序列检测器seq_det_moore:仿真文件parallel_serial_tb如下:parallel_serial_tb的仿真结果:引脚分配的代码:,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
实验十五 摩尔状态机序列检测器
- 按键消抖模块debounce_button:
- 由于实际的拨码开关和按键开关都是机械式的设备,开关动作来回抖动多次后才能稳定下来,这个过程就会使得信号产生==抖动==。因此,如果用按键来产生时钟信号,为了==一次按键得到一次上升沿(或下降沿)==,那么需要对按键输入先进行==消抖处理==。
- 并转串模块parallel_serial:
- 运用并转串输出模块,将==八个拨码开关==作为外部二进制码流的输入。用一个按键作为一个启动检测信号,另用一个按键来模拟 clk 信号,检测开关序列中是否存在“1101”序列,按下启动检测信号后,每按一次模拟 clk 的按键便==传入一个开关的值==,这个值时传入到==摩尔状态机序列检测器seq_det_moore==中的。
- 摩尔状态机序列检测器seq_det_moore:
- 这里实现的摩尔状态机序列是没有重叠的检测方式,即“1101101”中只有一个“1101”序列;但如果是有重叠的序列检测方式,那么就有两个“1101”序列。代码实现方式只需要修改最后一个状态s4,在din=1时,变成指向s2即可(这里是指向s1的)。
- 仿真文件parallel_serial_tb如下:
- 对并转串模块的仿真
- parallel_serial_tb的仿真结果:
- 每次一次时钟上升沿,便检测一次data_o的值。当检测到“1101”时,dout输出1,并在下个一时钟上升沿来的时候,dout的值将会变化。
- 引脚分配的代码:
- 注意系统时钟clk的引脚要分配到fgpa固定的那个时钟引脚上。手动输入的key[0],并转串模板的“时钟”也是“选择信号”,按键消抖中的“被消抖的按键”,绑定在想要的成为输入按键的地方就好。我这里==key[0]绑定的是T17==,==reset绑定的是U18==,==en绑定的是R2==(要赋为1才能进行检测操作,否则上板子不会亮的),输入==data_i右下角八个键==(reset我没有进行消抖,因为不消抖也没什么影响)
- 以上就是==摩尔状态机序列检测器==啦~
按键消抖模块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的值将会变化。
引脚分配的代码:
注意系统时钟clk的引脚要分配到fgpa固定的那个时钟引脚上。手动输入的key[0],并转串模板的“时钟”也是“选择信号”,按键消抖中的“被消抖的按键”,绑定在想要的成为输入按键的地方就好。我这里key[0]绑定的是T17,reset绑定的是U18,en绑定的是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才能进行检测)
(=.=点击这里查看上板实验操作演示的视频哦=.=)
最后
以上就是忧虑小天鹅为你收集整理的实验十五 摩尔状态机序列检测器“1101”按键消抖模块debounce_button:并转串模块parallel_serial:摩尔状态机序列检测器seq_det_moore:仿真文件parallel_serial_tb如下:parallel_serial_tb的仿真结果:引脚分配的代码:的全部内容,希望文章能够帮你解决实验十五 摩尔状态机序列检测器“1101”按键消抖模块debounce_button:并转串模块parallel_serial:摩尔状态机序列检测器seq_det_moore:仿真文件parallel_serial_tb如下:parallel_serial_tb的仿真结果:引脚分配的代码:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复