FPGA状态机实现花式流水灯

花式流水灯是指流水灯不再是按照固定的时间间隔,固定的顺序循环,而是根据需要的顺序点亮、熄灭、闪烁

而要实现花式流水灯,就需要用到状态机,有关状态机的介绍大家可以看我上一篇blog

FPGA状态机详解_居安士的博客-CSDN博客


首先我们需要设计一个花式流水灯的状态转移图

FPGA状态机实现花式流水灯_第1张图片

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也加进来了

FPGA状态机实现花式流水灯_第2张图片

可以看到,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就要再晚一个时钟周期)

看一下总体的仿真结果图

 和一段式没有区别,可以看出来和我们设计的花式流水灯效果是符合的 


完整的工程已经上传至资源,包含仿真文件,求赞赞~~

你可能感兴趣的:(fpga开发)