花式流水灯是指流水灯不再是按照固定的时间间隔,固定的顺序循环,而是根据需要的顺序点亮、熄灭、闪烁
而要实现花式流水灯,就需要用到状态机,有关状态机的介绍大家可以看我上一篇blog
FPGA状态机详解_居安士的博客-CSDN博客
首先我们需要设计一个花式流水灯的状态转移图
用parameter语法定义一下11个状态,在这里我用的是二进制码
reg [4:0]state;
parameter IDLE=5'd0;
parameter S1=5'd1;
parameter S2=5'd2;
parameter S3=5'd3;
parameter S4=5'd4;
parameter S5=5'd5;
parameter S6=5'd6;
parameter S7=5'd7;
parameter S8=5'd8;
parameter S9=5'd9;
parameter S10=5'd10;
Verilog 中通过使用 parameter 可以在调用模块时修改模块里面的常数参数, 提高模块的复用性,类似C语言中函数的形参,在模块调用时将参数传入模块。 用 parameter 可以让程序变得可维护性,所以在 verlog 中,尽量在有数据可能发生变动的地方,设置为 parameter,以免以后又从头开始更改。
定时时间有1s和2s,因此需要分别写两个定时器输出,输出1s定时time1_en和2s定时time2_en
这里有一个需要注点的地方是把两个定时器输出写在一个if-else模块里的时候,应该把跳出循环的条件写在最前面,比如在这里就把跳出if循环的time_cnt==31'd49写在time_cnt==31'd24前面
reg [30:0] time_cnt;
always @(posedge clk_25m)begin
if(reset)begin
time_cnt<=31'd0;
time1_en<=1'd0;
time2_en<=1'd0;
end
else if(time_cnt==31'd49)begin
time_cnt<=31'd0;
time2_en<=1'd1;
end
else if(time_cnt==31'd24)begin
time_cnt<=time_cnt+31'd1;
time1_en<=1'd1;
end
else begin
time_cnt<=time_cnt+31'd1;
time1_en<=1'd0;
time2_en<=1'd0;
end
end
为了方便仿真,上面代码我把1s、2s变成了1us、2us,仿真验证之后再把定时器个数修改成1s、2s即可
接下来编写状态机程序,用到了case( )——endcase语句
( )里面写的是状态,下面是定义的状态编号
这里展示一段式状态机代码
always@(posedge clk_25m)begin
if(reset)begin
led_out<=4'b0000;
state<=IDLE;
end
else begin
case(state)
IDLE:begin
if(time2_en)begin
led_out<=4'b0110;
state<=S1;
end
else begin
led_out<=4'b0000;
state<=IDLE;
end
end
S1 :begin
if(time1_en)begin
led_out<=4'b1001;
state<=S2;
end
else begin
led_out<=4'b0110;
state<=S1;
end
end
S2 :begin
if(time2_en)begin
led_out<=4'b1111;
state<=S3;
end
else begin
led_out<=4'b1001;
state<=S2;
end
end
S3 :begin
if(time2_en)begin
led_out<=4'b0111;
state<=S4;
end
else begin
led_out<=4'b1111;
state<=S3;
end
end
S4 :begin
if(time1_en)begin
led_out<=4'b1011;
state<=S5;
end
else begin
led_out<=4'b0111;
state<=S4;
end
end
S5 :begin
if(time1_en)begin
led_out<=4'b1101;
state<=S6;
end
else begin
led_out<=4'b1011;
state<=S5;
end
end
S6 :begin
if(time1_en)begin
led_out<=4'b1110;
state<=S7;
end
else begin
led_out<=4'b1101;
state<=S6;
end
end
S7 :begin
if(time1_en)begin
led_out<=4'b1101;
state<=S8;
end
else begin
led_out<=4'b1110;
state<=S7;
end
end
S8 :begin
if(time1_en)begin
led_out<=4'b1011;
state<=S9;
end
else begin
led_out<=4'b1101;
state<=S8;
end
end
S9 :begin
if(time1_en)begin
led_out<=4'b0111;
state<=S10;
end
else begin
led_out<=4'b1011;
state<=S9;
end
end
S10 :begin
if(time2_en)begin
led_out<=4'b000;
state<=S10;
end
else begin
led_out<=4'b0111;
state<=IDLE;
end
end
default:begin
led_out<=4'b0000;
state<=IDLE;
end
endcase
end
end
仿真看一下结果
可以看出来和我们设计的花式流水灯效果是符合的
我们还可以写一下三段式状态机,大家可以看一下他们的区别,代码如下
reg[4:0] current_state;
reg[4:0] next_state;
//第一段
always@(posedge clk_25m)begin
current_state<=next_state;
end
//第二段
always @(*)begin
if(reset)begin
next_state=IDLE;
end
else begin
case(current_state)
IDLE:begin
if(time2_en)begin
next_state=S1;
end
else begin
next_state=IDLE;
end
end
S1 :begin
if(time1_en)begin
next_state=S2;
end
else begin
next_state=S1;
end
end
S2 :begin
if(time2_en)begin
next_state=S3;
end
else begin
next_state=S2;
end
end
S3 :begin
if(time2_en)begin
next_state=S4;
end
else begin
next_state=S3;
end
end
S4 :begin
if(time1_en)begin
next_state=S5;
end
else begin
next_state=S4;
end
end
S5 :begin
if(time1_en)begin
next_state=S6;
end
else begin
next_state=S5;
end
end
S6 :begin
if(time1_en)begin
next_state=S7;
end
else begin
next_state=S6;
end
end
S7 :begin
if(time1_en)begin
next_state=S8;
end
else begin
next_state=S7;
end
end
S8 :begin
if(time1_en)begin
next_state=S9;
end
else begin
next_state=S8;
end
end
S9 :begin
if(time1_en)begin
next_state=S10;
end
else begin
next_state=S9;
end
end
S10 :begin
if(time2_en)begin
next_state=S10;
end
else begin
next_state=IDLE;
end
end
default:begin
next_state=IDLE;
end
endcase
end
end
//第三段
always@(posedge clk_25m)begin
if(reset)begin
led_out<=4'b0000;
end
else begin
case(current_state)
IDLE:begin
if(time2_en)begin
led_out<=4'b0110;
end
else begin
led_out<=4'b0000;
end
end
S1 :begin
if(time1_en)begin
led_out<=4'b1001;
end
else begin
led_out<=4'b0110;
end
end
S2 :begin
if(time2_en)begin
led_out<=4'b1111;
end
else begin
led_out<=4'b1001;
end
end
S3 :begin
if(time2_en)begin
led_out<=4'b0111;
end
else begin
led_out<=4'b1111;
end
end
S4 :begin
if(time1_en)begin
led_out<=4'b1011;
end
else begin
led_out<=4'b0111;
end
end
S5 :begin
if(time1_en)begin
led_out<=4'b1101;
end
else begin
led_out<=4'b1011;
end
end
S6 :begin
if(time1_en)begin
led_out<=4'b1110;
end
else begin
led_out<=4'b1101;
end
end
S7 :begin
if(time1_en)begin
led_out<=4'b1101;
end
else begin
led_out<=4'b1110;
end
end
S8 :begin
if(time1_en)begin
led_out<=4'b1011;
end
else begin
led_out<=4'b1101;
end
end
S9 :begin
if(time1_en)begin
led_out<=4'b0111;
end
else begin
led_out<=4'b1011;
end
end
S10 :begin
if(time2_en)begin
led_out<=4'b000;
end
else begin
led_out<=4'b0111;
end
end
default:begin
led_out<=4'b0000;
end
endcase
end
end
对三段式代码进行仿真,这里我把current_state 和next_state也加进来了
可以看到,next_state是比current_state晚一个时钟,这是因为在always块下进行非阻塞赋值,将current_state<=next_state的原因
所以time2_en为高电平时,next_state在time2_en上升沿就变化,而current_state在time2_en上升沿之后的下一个clk_25m上升沿才变化
这也就是为什么三段式的第二段next_state跳转要用阻塞赋值(如果用非阻塞赋值,next_state在time2_en上升沿之后的下一个clk_25m上升沿才变化,current_state就要再晚一个时钟周期)
看一下总体的仿真结果图
和一段式没有区别,可以看出来和我们设计的花式流水灯效果是符合的
完整的工程已经上传至资源,包含仿真文件,求赞赞~~