一、项目要求
(1)要求用状态机实现4位流水灯设计,流水间隔1s。
(2)触发流水灯的条件是按下按键且按键消抖后。
二、设计思路
整体思路:状态机流水灯的模块设计调用按键按下的代码,即FMS.v调用例化的key.v文件
(1)信号需求:
a.输入信号:输入系统时钟;
输入系统复位;
按下按键后有一个key信号;
按键消抖后有一个key_flag信号;
b.输出信号:4个led灯,即led[4:0];
(2)计数器需求:
a.状态机的计数器:流水灯变换间隔是1s,由于zynq7000是50MHz,因此一个周期是20ns,一秒内一共有50000000个20ns,因此状态机计数器需要计数50000000次;
b.按键消抖的计数器:按下按键后抖动时间大约为20ms,同理按键消抖计数器需要计数500000次;
(3)存储:
a.两个计数器分别需要存储;
b.状态机的现态和次态需要存储。
(4)管脚绑定
根据原理图,电平标准3.3V,led分别绑定T12/U12/V12/W13管脚
根据原理图绑定时钟信号和按键信号,复位信号随便绑一个可以用的IO口。
三、key.v代码
`timescale 1ns / 1ps
module key(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位
input key, //输入信号key,即按键按下
output key_flag //输出信号key_flag,即按键消消抖成功
);
parameter delay=50_000_0; //计数器最大值为500000次(20ms)
reg [18:0] cnt ; //计数器需要的存储空间换算成二进制需要19位
always@(posedge sys_clk)
if(!sys_rst_n) //复位信号有效的话计数器清零
cnt<=0;
else if(key==0)begin //当按键按下时(key是低电平)
if(cnt==delay-1) //计数器计到最大值的时候
cnt<=cnt; //保持
else
cnt<=cnt+1; //计数器不断增加
end
else
cnt<=0;
assign key_flag=(cnt==delay-2)?1:0; //当cnt计数到最大值-2时,key_flag输出高电平
endmodule
四、FMS.v代码
`timescale 1ns / 1ps
module FMS_0(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位
input key , //输入信号key
output reg [3:0] led // 输出信号led,,位宽为4,一共4个灯
);
wire key_flag ; //key_flag作为中间信号可以不用输出
parameter IDLE =5'b0000_1 , //空闲状态下的状态机
S0 =5'b0001_0 , //第1个led亮时状态机的值
S1 =5'b0010_0 , //第2个led亮时状态机的值
S2 =5'b0100_0 , //第3个led亮时状态机的值
S3 =5'b1000_0 ; //第4个led亮时状态机的值
parameter delay=50_000_000 ; //一秒钟计数器计数的次数
reg [25:0] count ; //给计数次数一个存储空间
reg [4:0] cur_state ; //当前状态(共5种)
reg [4:0] next_state ; //下一个状态(共5种)
//控制状态机计数器计数
always@(posedge sys_clk)
if(!sys_rst_n)
count<=0;
else if(count==delay-1)
count<=0;
else
count<=count+1;
//描述状态机现态和次态的关系
always@(posedge sys_clk)
if(!sys_rst_n)
cur_state<=IDLE;
else
cur_state<=next_state;
//描述状态转移 用组合逻辑
always@(*)
if(!sys_rst_n)
next_state=IDLE;
else
case(cur_state)
IDLE : begin
if(key_flag) //当按键消抖结束时,进入S0状态
next_state=S0;
else
next_state=IDLE;
end
S0 : begin
if(count==delay-1)
next_state=S1;
else
next_state=cur_state;
end
S1 : begin
if(count==delay-1)
next_state=S2;
else
next_state=cur_state;
end
S2 : begin
if(count==delay-1)
next_state=S3;
else
next_state=cur_state;
end
S3 : begin
if(count==delay-1)
next_state=S0;
else
next_state=cur_state;
end
default next_state=IDLE;
endcase
//用时序逻辑去赋值
//将状态机和led联系起来
always@(posedge sys_clk)
if(!sys_rst_n)
led<=4'b1111 ; //复位状态给让灯全亮
else
case(cur_state)
IDLE : led<=4'b1111 ;
S0 : led<=4'b0001 ;
S1 : led<=4'b0010 ;
S2 : led<=4'b0100 ;
S3 : led<=4'b1000 ;
default: led<=4'b0000 ;
endcase
//例化后,可调用key.v中的程序
key u1(
. sys_clk (sys_clk ) ,
. sys_rst_n (sys_rst_n) ,
. key (key ) ,
. key_flag (key_flag )
);
endmodule
五、仿真波形
`timescale 1ns / 1ps
module FMS_0_tb();
reg sys_clk ;
reg sys_rst_n ;
reg key ;
wire [3:0] led ;
initial begin
sys_clk=0; //给时钟初值为低电平
sys_rst_n=0; //给复位信号初值为低电平
key=1; //key信号给高电平
#20
sys_rst_n=1; //过20ns将复位信号拉高
#500000
key=0; //过500000ns拉低key信号,模拟人按按键
#50000000 //过1s后松开按键
key=1;
end
always #10 sys_clk=~sys_clk; //每10ns时钟信号电平翻转一次,即20ns/50MHz为一个周期
//例化
FMS_0 u2(
. sys_clk (sys_clk ) ,
. sys_rst_n (sys_rst_n) ,
. key ( key ) ,
. led (led )
);
endmodule
调整了下状态机的变换时间,不然波形不太容易看,以下是仿真结果