FPGA刷题——交通灯+售卖机

本文介绍了如何使用状态机设计交通灯控制系统,包括正常情况下的周期循环及行人按钮按下时的特殊情况处理。此外,还展示了自动售卖机的状态机实现,包括选择饮料、支付、找零和出饮料的流程。代码示例详细展示了状态转移逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

交通灯

自动售卖机


交通灯和售卖机是常常作为状态机考察的题目,这种题目并不难,需要理清楚状态转移的过程,下面是2道例题

交通灯

要求实现一个交通红绿灯,具有红黄绿三个小指示灯和一个行人按钮,正常情况下,机动车道指示灯按照60时钟周期绿灯,5个时钟周期黄灯,10个时钟周期红灯循环。当行人按钮按下,如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。

注:机动车道的指示灯和人行道指示灯应该是配对的,当机动车道的灯为绿或者黄时,人行道的灯为红;当机动车道的灯为红时,人行道的灯为绿,为简便起见,只考虑机动车道的指示灯。

模块的信号接口图如下:

输入描述:

clk:系统时钟信号

rst_n:复位信号,低电平有效

pass_request:行人按钮信号,当该信号为1,表示按钮按下,如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。

输出描述:

clock:交通灯倒计时读数

red:该信号为1,表示红灯亮,为0表示红灯不亮

yellow:该信号为1,表示黄灯亮,为0表示黄灯不亮

green:该信号为1,表示黄灯亮,为0表示黄灯不亮

题解思路

首先考虑实现正常情况下的功能:机动车道指示灯按照60时钟周期绿灯,5个时钟周期黄灯,10个时钟周期红灯循环

首先设置一个倒计时的计数器,每个时钟计数器输出减一,当计数器计数到0时,切换切换显示灯颜色,同时重置计数器为相应颜色的持续时间。

三个颜色的指示灯之间的转化可以使用一个简单的状态机实现。在红灯状态下,红灯亮,其余两个灯灭。而且当cnt==0,即倒计时结束时切换到黄灯,否则停留在红灯状态。在黄灯灯状态下,黄灯亮,其余两个灯灭。而且当cnt==0,即倒计时结束时切换到绿灯,否则停留在黄灯状态。在绿灯状态下,绿灯亮,其余两个灯灭。而且当cnt==0,即倒计时结束时切换到红灯,否则停留在绿灯状态。

再考虑实现当行人按钮按下的情况:如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。

因此首先判断当前是否是绿灯,绿灯倒计时是否大于10,行人按钮是否被按下,如果这三个条件同时满足,则把倒计时置为10。

分析完成之后,可以画出状态转移图如下:

 代码如下:

module traffic(
input clk,
input reset,
input pass_request,//行人按钮信号
output wire [7:0]clock,//交通灯倒计时读数
output reg red_out    ,//该信号为1,表示红灯亮,为0表示红灯不亮
output reg yellow_out ,//该信号为1,表示黄灯亮,为0表示黄灯不亮
output reg green_out   //该信号为1,表示黄灯亮,为0表示黄灯不亮
    );
    
    
    reg [7:0] cnt;//计数秒数
    
    reg [2:0] current_state;
    reg [2:0] next_state;
    
    parameter IDLE  =3'd0;
    parameter GREEN =3'd1;
    parameter YELLOW=3'd2;
    parameter RED   =3'd3;
    parameter PASS  =3'd4;
    
    always@(posedge clk)begin
    	if(reset)begin
    		current_state<=3'd0;
    	end
    	else begin
    		current_state<=next_state;
    	end
    end
    
    always@(*)begin
    	if(reset)begin
    		next_state=IDLE;
    	end
    	else begin
    		case(current_state)
    			IDLE  :begin
    				next_state=GREEN;
    			end
          GREEN :begin
    				if(cnt>9&&pass_request==1)begin
    					next_state=PASS;
    				end
    				else if (cnt==0)begin
    					next_state=YELLOW;
    				end
    			end
          
          YELLOW:begin
    				if (cnt==0)begin
    					next_state=RED;
    				end
    			end
          RED  :begin
    				if (cnt==0)begin
    					next_state=GREEN;
    				end
    			end 
          PASS :begin
    				if (cnt==0)begin
    					next_state=YELLOW;
    				end
    			end 

    		endcase
    	end
    end
    
    always@(posedge clk)begin
    	if(reset)begin
    		cnt<=8'd0;
    		red_out   <=1'd0;
        yellow_out<=1'd0;
        green_out <=1'd0;

    	end
    	else begin
    		case(current_state)
    			IDLE:begin
    				cnt<=8'd60;
    			end
    			GREEN :begin
    				green_out <=1'd1;//绿灯亮
    				if(cnt==8'd0)begin//跳转黄灯
    					green_out <=1'd0;
    					yellow_out<=1'd1;
    					cnt <=8'd5;
    				end
    				else if(cnt>8'd9&&pass_request==1) begin
    					cnt<=8'd9;
    				end
    				else begin
    					cnt<=cnt-8'd1;
    				end
    			end
          YELLOW:begin
    				if(cnt==8'd0)begin//跳转红灯
    					green_out <=1'd0;
    					yellow_out<=1'd0;
    					red_out   <=1'd1;
    					cnt <=8'd10;
    				end
    				else begin
    					cnt<=cnt-8'd1;
    				end
    			end
          RED :begin
    				if(cnt==8'd0)begin//跳转绿灯
    					green_out <=1'd1;
    					yellow_out<=1'd0;
    					red_out   <=1'd0;
    					cnt <=8'd60;
    				end
    				else begin
    					cnt<=cnt-8'd1;
    				end
    			end  
          PASS:begin
    				if(cnt==8'd0)begin//跳转黄灯
    					green_out <=1'd0;
    					yellow_out<=1'd1;
    					red_out   <=1'd0;
    					cnt <=8'd5;
    				end
    				else begin
    					cnt<=cnt-8'd1;
    				end
    			end  

    		endcase
    	end
    end
    
    assign clock=cnt;
    
endmodule

没有行人按下时: 有行人按下时:


自动售卖机

描述

请设计状态机电路,实现自动售卖机功能,A饮料5元钱,B饮料10元钱,售卖机可接收投币5元钱和10元钱,每次投币只可买一种饮料,考虑找零的情况。

电路的接口如下图所示。sel信号会先于din信号有效,且在购买一种饮料时值不变。

sel为选择信号,用来选择购买饮料的种类,sel=0,表示购买A饮料,sel=1,表示购买B饮料;

din表示投币输入,din=0表示未投币,din=1表示投币5元,din=2表示投币10元,不会出现din=3的情况;

drinks_out表示饮料输出,drinks_out=0表示没有饮料输出,drinks_out=1表示输出A饮料,drinks_out=2表示输出B饮料,不出现drinks_out =3的情况,输出有效仅保持一个时钟周期;

change_out表示找零输出,change_out=0表示没有找零,change_out=1表示找零5元,输出有效仅保持一个时钟周期。

接口电路图如下:

输入描述:

   input                clk   ,
   input                rst_n ,
   input                sel   ,//sel=0,5$dranks,sel=1,10&=$drinks
   input          [1:0] din   ,//din=1,input 5$,din=2,input 10$

输出描述:

   output   reg  [1:0] drinks_out,
   output    reg        change_out  

按照题目的描述,自动售卖机的运行流程是:选择饮料——给钱——找零——出饮料——初始界面

我们也可以按照这个流程来写状态机:

 状态机的难点在于找零操作上,解决方法是:在select(选择饮料)状态根据sel信号赋值给cnt0;在pay状态根据din信号赋值给cnt1,如果cnt1大于cnt0,意味着需要找钱,零钱=cnt1-cnt0

代码如下:

module sale(
	input                clk   ,
  input                rst ,
  input                sel   ,//sel=0,5$dranks,sel=1,10&=$drinks
  input          [1:0] din   ,//din=1,input 5$,din=2,input 10$
 
  output   reg [2:0] drinks_out,//drinks_out=1,output 5$ drinks,drinks_out=2,output 10$ drinks
  output   reg [3:0] change_out   
    );
    
    
    reg[3:0]cnt0;//饮料价格
    reg[3:0]cnt1;//给的钱
    
    reg [2:0] state;
    
    parameter idle  =3'd0;
    parameter select=3'd1;
    parameter pay   =3'd2;
    parameter money =3'd3;
    parameter drink =3'd4;
    parameter calculate=3'd5;
    
    always@(posedge clk)begin
    	if(rst)begin
    		cnt0      <=4'd0;
        cnt1      <=4'd0;
				drinks_out<=1'd0;
				change_out<=3'd0;
				state<=idle;
    	end
    	else begin
    		case(state)
    			idle :begin
    				cnt0      <=4'd0;
            cnt1      <=4'd0;
				    drinks_out<=1'd0;
				    change_out<=3'd0;
    				state<=select;
    			end 
          select:begin
    				if(sel==1'd0)begin//选择A饮料
    					cnt0      <=4'd10;
    				end
    				else if(sel==1'd1) begin//选择B饮料
    					cnt0      <=4'd5;
    				end
    				state<=pay;
    			end 
          pay:begin
    				if(din==2'd2)begin//投币10元
    					cnt1      <=4'd10;
    				end
    				else if(din==2'd1) begin//投币5元
    					cnt1      <=4'd5;
    				end
    				else if(din==2'd0)begin//投币0元
    					cnt1      <=4'd0;
    				end 
    				state<=calculate;    		    
    			end    
    			calculate:begin
    				if(cnt1>cnt0)begin
    					state<=money;
    				end
    				else begin
    					state<=drink;
    				end
    			end
          money:begin
    				change_out<=cnt1-cnt0;
    				state<=drink;
    			end  
          drink:begin
    				if(sel==1'd0)begin//输出A饮料
    					drinks_out<=3'd1;
    				end
    				else if(sel==1'd1) begin//输出B饮料
    					drinks_out<=3'd2;
    				end
    				state<=idle;
    			end  

    		endcase
    	end
    end
    
endmodule

买B饮料,给10元的情况

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值