Verilog实现自动售货机控制系统

设计思路:

首先,依次分析题目题干,整理verilog代码思路。

  1. 该系统能完成自身的复位。对货物信息的存储、进程控制,硬币处理、余额计算、显示等功能。
    在代码中,可以将该控制系统设计成一个有限状态机,将各个进程定义为相应的状态,实现状态之间的条件跳转。
    显示功能可以用out定义,后续定义引脚,在电路板上进行相应的显示。
  2. 机器只接受1元硬币和5角硬币。
    定义两个变量,分别为一元硬币和五角硬币的数量。再定义一个变量,存储总钱数。因为verilog语言中float(小数)会有数据的丢失,且复杂。我们这里就暂用5角钱为一单位进行计算。所有输入和输出都统一为5角钱的倍数。
    投币模块应设计成上升沿或下降沿触发,每次投币变量发生变化时,投币数量即加一。
  3. 机器共提供4种货物,价格分别为2元、2元、1.5元、1.5元(可通过调整代码改变每种货物的单价)。
    可以在代码开始时用parameter宏定义货物的单价,后续改变时也可以直接在代码初始阶段改变代码。
  4. 顾客先选择需要的一种商品,在确认所选货物后,进入投币状态。当顾客选择的货物售完时,在顾客确定货物之时,提示顾客货物卖光,并返回初始状态(顾客确认货物时,提供确认选择和重新选择两个选项)。
    选择货物是一个状态。可以通过置确认变量为1进入判断是否售完的状态。也可以通过置重新选择变量为1进入初始化状态。
  5. 若等待30s,顾客不投币,则返回初始状态;顾客投币后,系统自动计算所投钱币。若钱币够,则出货找零。若自客户确认货物之后30s,投币不够,则退币并返回初状态。也可在投币完成时按投币完成按钮快速进入计算钱币状态以及后续状态。
    进入投币状态后,开始计时,使用时钟分频模块,将48MHz的时钟计数至30s,若时间到达30s或置确认为1,即进入下一个状态:判断钱数够不够。若置返回的变量为1,则下一个状态是初始状态。
  6. 所要求的设计在某电路板上完成,该电路板能够提供48MHz标准时钟信号。
    因为硬件条件有限,我们仅限于软件层面上,不下板。但是时钟信号仍采用题中描述的48MHz,以此作为我们的标准信号。

状态转换图:

根据设计思路,我们设计状态如下:
S0:初始化
S1:选择货物
S2:货物是否卖光
S3:投币
S4:钱数够不够
S5:找零
S6:出货

状态转换图如图所示:
在这里插入图片描述

Verilog代码编写:

根据状态转换图及相关逻辑
我们编写的完整的verilog代码如下:

module machine(clk,money_in1,money_in2,select,item_confirm,money_confirm,cancel,sold_out,money_out,item_out,sta);
	input clk;
  
  //为方便计算钱数,以五角钱为一单位
	input money_in1; //投入五角硬币
	input money_in2; //投入一元硬币
	  
	input [1:4] select; //选择货物,只有一位有效
	input item_confirm,money_confirm,cancel;
   input [1:4] sold_out; //四种货物是否售空的状态,为1表示售空
   output reg [2:0] money_out; //退款或找零的钱数
   output reg [1:4] item_out; //出货物,只有一位有效
	  
   reg [1:4] select_sta;
   output reg [2:0] sta; //状态机的状态

	reg timeing; //计时状态
   reg [31:0] time_cnt_s;
   reg [8:0] time_cnt_l;

   reg [2:0] money_sum;
	reg [2:0] money_sum1;
	reg [2:0] money_sum2;
	  
   parameter S_init=4'b000,S_select=4'b001,S_soldout=4'b010,S_30sec=4'b011,
	S_enough=4'b100,S_change=4'b101,S_out=4'b110;
	
	parameter item1=3'd4,item2=3'd4,item3=3'd3,item4=3'd3; //货物单价
	
	initial
	begin
		sta=S_init;
		money_sum1=0;
		money_sum2=0;
	end
	  
	always@(posedge clk) //分频模块,波形测试时需减小分频,方便看波形
	begin
		if(sta==S_init)
		begin
			time_cnt_s=0;
			time_cnt_l=0;
		end
		
		if(timeing)
			time_cnt_s=time_cnt_s+1;
		if(time_cnt_s==32'd5_000_000)
			time_cnt_l=time_cnt_l+1;
	end
	  
	always@(posedge money_in1) //投币模块,上升沿为投币
	begin		
		if(timeing)
		begin	
			money_sum1=money_sum1+1;
		end
		
		if(sta!=S_30sec)
			money_sum1=0;
	end
	
	always@(posedge money_in2) //投币模块,上升沿为投币
	begin
		if(timeing)
		begin	
			money_sum2=money_sum2+2;
		end
		
		if(sta!=S_30sec)
			money_sum2=0;
	end
	  
	always@(posedge clk)
	begin
		case(sta)
		S_init:
		begin
			select_sta=4'b0000;
			timeing=0;
			money_sum=0;
			money_out=0;
			item_out=4'b0000;

			sta=S_select;
		end
	 
		S_select:
		begin
			select_sta=select|select_sta;
			if(item_confirm)
			begin
				sta=S_soldout;
			end
			if(cancel)
			begin
				sta=S_init;
			end
		end
		
		S_soldout:
		begin
			if(select_sta&sold_out!=4'b0000) //若相同位同时为1
				sta=S_init;
			else
				sta=S_30sec;
		end
		
		S_30sec:
		begin
			timeing=1;
			if(money_confirm==1 || time_cnt_l>=288) //经计算,若时钟频率为48MHz,30s则对应288个time_cnt_l
			begin
				timeing=0;
				money_sum=money_sum1+money_sum2;
				sta=S_enough;
			end
			else
				sta=S_30sec;
		end
	 
		S_enough:
		begin
			case(select_sta)
			4'b1000:
			begin
				if(money_sum>=item1)
				begin
					money_sum=money_sum-item1; //求出找钱数
					sta=S_change;
				end
				else
				begin
					money_out=money_sum; //退钱
					sta=S_init;
				end
			end
			4'b0100:
			begin
				if(money_sum>=item2)
				begin
					money_sum=money_sum-item2;
					sta=S_change;
				end
				else
				begin
					money_out=money_sum;
					sta=S_init;
				end
			end
			4'b0010:
			begin
				if(money_sum>=item3)
				begin
					money_sum=money_sum-item3;
					sta=S_change;
				end
				else
				begin
					money_out=money_sum;
					sta=S_init;
				end
			end
			4'b0001:
			begin
				if(money_sum>=item4)
				begin
					money_sum=money_sum-item4;
					sta=S_change;
				end
				else
				begin
					money_out=money_sum;
					sta=S_init;
				end
			end
			default:
			begin
				money_out=money_sum;
				sta=S_init;
			end
			endcase
		end
	 
		S_change:
		begin
			if(money_sum>0)
			begin
				money_out=money_sum;
				sta=S_out;
			end
			else
				sta=S_out;
		end
	 
		S_out:
		begin
			item_out=select_sta;
			sta=S_init;
		end
		endcase
	end
endmodule

关于相关变量定义的说明都在注释中

功能验证:

我们利用quartus软件,编写激励文件,模拟输入,观察波形。
激励文件代码如下:

`timescale 1 ps/ 1 ps
module machine_vlg_tst();
// constants                                           
// general purpose registers
reg eachvec;
// test vector input registers
reg cancel;
reg clk;
reg item_confirm;
reg money_confirm;
reg money_in1;
reg money_in2;
reg [1:4] select;
reg [1:4] sold_out;
// wires                                               
wire [1:4]  item_out;
wire [2:0]  money_out;
wire [2:0]  sta;

// assign statements (if any)                          
machine i1 (
// port map - connection between master ports and signals/registers   
	.cancel(cancel),
	.clk(clk),
	.item_confirm(item_confirm),
	.item_out(item_out),
	.money_confirm(money_confirm),
	.money_in1(money_in1),
	.money_in2(money_in2),
	.money_out(money_out),
	.select(select),
	.sold_out(sold_out),
	.sta(sta)
);
initial                                                
begin                                                  
// code that executes only once                        
// insert code here --> begin                          
                                                       
// --> end                                             
clk=0;
money_in1=0;
money_in2=0; //投入一元硬币
select=4'b0010; //选择货物,只有一位有效
item_confirm=1;money_confirm=0;cancel=0;
sold_out=4'b0000;


#100 money_in2=1;
#10 money_in2=0;
#10 money_in2=1; 
#10 money_in2=0;
#10 money_confirm=1;              
end                                                    
always                                                 
// optional sensitivity list                           
// @(event1 or event2 or .... eventn)                  
begin                                                  
// code executes for every event on sensitivity list   
// insert code here --> begin                          
                                                       
#5 clk=~clk;                                              
// --> end                                             
end                                                    
endmodule

产生的波形如下:
在这里插入图片描述
在我们输入选定货物第三个:0010,并选择货物确定键item_confirm后,
sta从初始状态000进入状态011:等待投币状态。
在这里插入图片描述
在我们利用投币的下降沿投入了两个一元硬币后,此时还没有到30s的计时,我们选择投币确认键money_confirm=1,sta进入下一个状态100:判断钱够不够。
在这里插入图片描述
在这里插入图片描述
经过系统计算,我们选择的货物是1.5元,而我们投入了2元钱,所以钱是够的,进入下一状态,此时money_out是0.5元,即找给了我们零钱0.5元。
在这里插入图片描述
下一状态为出货状态,item_out是0010,恰好符合我们所需要的货物。
之后sta变为000,即回到了初始状态,开始下一轮的循环。

至此,一套完整的自动售货机控制系统演示完毕。功能与预期完全一致。

猜你喜欢

转载自blog.csdn.net/qq_43734019/article/details/104058539