一、 问题描述设计一个自动售货机控制模块,信息如下:
1. 售货机可提供8种可售商品,其中01号商品(代表第一种商品),价格为2元,02商品为4元,依次类推,08号商品售价为16元。
2. 售货机可接受1元,5元和10元的纸币收入。
3. 交易开始,用户需要先投入一定量的货币;在外部货币投入后,售货机累计输入货币总值;完成投币后,用户选择所有需要的商品;选择结束后,按下交易键;交易成功,售货机出货、找零,更新系统的存量货币信息和货物信息;交易不成功,退回用户投入货币。
4. 交易不成功可能可能因为投币不足;货物不足;零钱不足等原因造成;
5. 管理员可以通过外部设备访问和修改可售商品信息和存量货币信息。
二、 功能概括
自动售货机刚初始化时,需要管理员对其剩余钱数和商品数进行管理,之后用户可以进行商品购买。用户首先选择商品,无误后对商品进行确认,否则取消,在用户选择了某种商品后,售货机会对其存货进行确认,如果该商品已经售罄,则交易失败,回到空闲状态。该商品有存货时,售货机延时一段时间(30S)留给用户进行投币,用户只能投入1,5,10元的钱币,否则售货机会将其退出。用户在延时内投币后,售货机进行找零计算,如果售货机内现金不足找零,则退出所有钱币,售货机进入管理员管理状态,此时需要管理员管理现金。正确情况下,零钱足够多,则正常找零,如果找零数额为0直接出货,找零数不为0则是先找零再出货。出于实际情况的考虑,我认为自动售货机更适合使用先选择产品,再投入钱币的方式更为合理,因此在设计时使用的是先选择确认,再延时投币的流程。自动售货机的整个管理和售货流程如下图所示:
![](https://img-blog.csdn.net/20170106091942010?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0Mzg3Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图1 自动售货机工作流程图
三、 接口定义
表1 系统接口与描述表
名称
I/O
描述
时钟与复位
clk
I
工作时钟
init
I
交易取消信号
rst
I
初始化与复位
管理员接口
manage
I
管理使能信号
manage_money
I
管理售货机内剩余钱数
load
I
外部串口接收(暂定)
left01
I
管理第01号商品数量
left02
I
管理第02号商品数量
left03
I
管理第03号商品数量
left04
I
管理第04号商品数量
left05
I
管理第05号商品数量
left06
I
管理第06号商品数量
left07
I
管理第07号商品数量
left08
I
管理第08号商品数量
用户接口
select
I
选择商品
confirm
I
确认购买商品
money_input
I
投入钱币
change
O
得到售货机找零
out
O
得到商品
四、 电路设计
系统设计:
根据题目要求,本项目关注点在于自动售货机控制的模块,因此对于一些软件、机械、电气方面的错误无法进行处理,如退钱时卡纸,突然停电等。出于集成化的考虑,采用了3个always块,always39为初始化状态模块,即将state的状态初始化为S_idle(可综合的Verilog不能使用initial进行初始化),always39为计时模块,在选定商品后,必须在规定的时间内投币。always63为自动售货机控制管理模块,是本设计主要电路。自动售货机控制模块的整体功能概括如下:
![](https://img-blog.csdn.net/20170106092002777?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0Mzg3Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图2 自动售货机控制模块功能概括
数据通路:
考虑测试输入输出模块,数据流图和控制流图如下图所示,其中蓝线为数据线,红线为控制线。
![](https://img-blog.csdn.net/20170106092019464?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0Mzg3Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图3 自动售货机控制模块数据通路
状态机:
自动售货机定义了10种状态,分别是选择(S_select),确认(S_confirm),是否售罄(S_allselled),延时(S_60s),投币是否足够(S_enough),是否需要找零(S_requirechange),找零(S_change),出货(S_out),空闲(S_idle),管理(S_management)。状态机如下图所示。
![](https://img-blog.csdn.net/20170106092057168?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0Mzg3Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图4自动售货机控制模块状态机
时序设计:
首先要将售货机初始化,然后管理员进行第一次管理,在管理员管理阶段,需要设定各个商品的剩余数量,以及售货机里用于找零的现金。管理结束后进入用户使用阶段,用户首先选择商品号,在规定的延迟之内完成投币,之后在不缺货和现金的情况下,会进行找零和出货,售货机会更新商品数量和现金信息。如果出现缺货,现金不足,投币超时等情况会进行特殊处理,返回空闲或管理状态。
![](https://img-blog.csdn.net/20170106092114434?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI0Mzg3Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图5自动售货机控制模块时序图
内部信号定义:
自动售货机控制模块的内部信号主要用于保存信息和中间过程处理,管理员在初始化管理时会输入商品数量以及内部现金余量,这些都需要寄存器进行寄存,在进行正常售货时,因为有找零和出货,内部的信息也在不断的更新。
表2 内部信号描述表
名称
性质
描述
内部信号
cash
reg
内部剩余现金
left1
reg
第01号商品数量
left2
reg
第02号商品数量
left3
reg
第03号商品数量
left4
reg
第04号商品数量
left5
reg
第05号商品数量
left6
reg
第06号商品数量
left7
reg
第07号商品数量
left8
reg
第08号商品数量
select_sta
reg
选择了某个商品
state
reg
状态机状态
isSelled
reg
是否售罄
timeCnting
reg
是否开始计时
time_cnt
reg
延时期限
opt
reg
是否取消交易
//2017.1.5 葛兴
//全定制电路课程课程设计
//Verilog自动售货机控制模块
//Vending Machine
module VendingMac(clk,money_input,select,confirm,init,left01,left02,left03,left04,
left05,left06,left07,left08,change,out,manage,manage_money,rst);
input clk;//时钟信号
input rst;//使能信号
input confirm,init;//?idle=init
input [3:0] money_input;//1,5,10,最大为1010,4位
input [1:8] select;//8种商品,选择上升沿触发
input [9:0] manage_money;//输入的钱数
input [5:0] left01,left02,left03,left04,left05,left06,left07,left08;//剩余商品数,认为不超过63
input [1:0] manage;//这个是管理员功能
reg [9:0] cash;//自动售货机内部的钱数
reg [3:0] state;//状态机的各种状态
reg [5:0] left1,left2,left3,left4,left5,left6,left7,left8;
output integer change;//找零,整数
output reg [1:8] out;//出货,8种商品之一
reg [1:8] select_sta;//选择某种商品
reg isSelled;//判断是否售罄
reg timeCnting;//延时计时
S_enough=4'b0100,S_requirechange=4'b0101,S_change=4'b0110,S_out=4'b0111,
reg [8:0] time_cnt;//500个时间单位,2^9=512
reg [1:0] opt;//检查init是否启用
integer money_sum;//剩余总钱数
parameter S_select=4'b0000,S_confirm=4'b0001,S_allselled=4'b0010,S_60s=4'b0011,
S_idle=4'b1000,S_management=4'b1001;
//9种state的编号
start==4b'0000
/* initial
begin
state=S_idle;//所以应该在输入中输入?还是直接在45行那儿初始化?
end */
always@(posedge clk or posedge rst)
begin
if(rst)
begin
state <= S_idle;
end
end
/* always @(posedge clk)
begin
if (start==4b'1000)
state=S_idle;
end */
select_sta=8'b00000000;
always @(posedge clk)
begin
if(clk && timeCnting)//根据clk上跳延计时
time_cnt=time_cnt+1;
if(timeCnting==0)
time_cnt=0;
end
always @(posedge init or posedge clk or manage)
begin
if(clk)
begin
case(state)
S_idle://初始化状态
begin
timeCnting=0;
left2 = left02;
money_sum=0;
change=0;
out=0;
opt=0;
if(manage==1)
state=S_management;
else
begin
state=S_select;
end
end
S_management://管理员可以通过外部设备访问和修改可售商品信息和存量货币信息
begin
left1 = left01;
left3 = left03;
if(confirm||init)//确认和初始化居其一(到下一步可以取消交易)
left4 = left04;
left5 = left05;
left6 = left06;
left7 = left07;
left8 = left08;
cash = manage_money;//
//manage=0;
state=S_idle;
end
S_select://选择某种商品
begin
select_sta=select|select_sta;//按位或,应对选择和商品;
begin
S_allselled://判断是否售罄
opt={confirm,init};
state=S_confirm;
end
end
S_confirm:
begin
if(opt==2'b10)
state=S_allselled;
//也就是说当confirm=1时,进入判判断货物状态?那init是做什么用的?
else if(opt==2'b00||2'b01||2'b11)
begin
state=S_idle;
end
end
begin
if(select_sta[6]==1 && left6<=0)
isSelled=0;//判断选择的商品是否售罄
if(select_sta[1]==1 && left1<=0)
isSelled=1;
if(select_sta[2]==1 && left2<=0)
isSelled=1;
if(select_sta[3]==1 && left3<=0)
isSelled=1;
if(select_sta[4]==1 && left4<=0)
isSelled=1;
if(select_sta[5]==1 && left5<=0)
isSelled=1;
timeCnting=1;//有商品则进入到计时付款阶段
isSelled=1;
if(select_sta[7]==1 && left7<=0)
isSelled=1;
if(select_sta[8]==1 && left8<=0)
isSelled=1;
if(cash < 16)
state=S_management;//没有足够零钱则进入管理员状态
else if(isSelled)
begin
state=S_idle;//没有商品,回到空闲状态
end
else
begin
state=S_60s;
end
state=S_idle;
end
S_60s:
begin
if(time_cnt<25)
begin
if(money_input!=0)
begin
//只接收1,5,10元
if(money_input==1||money_input==5||money_input==10||money_input==0)//检查是否为1,5,10元,不是则退钱并回到空闲状态
//添加0是因为可能有连续的钱无法区分,实际上是投币间隔
money_sum = money_sum + money_input;
else
if(select_sta[3]==1)
end
end
else if(time_cnt==25)
if(money_sum!=0)
state=S_enough;//在计时结束后有钱放入
else
state=S_idle;
end
S_enough:
begin
//其中01号商品价格为2元,02商品为4元,依次类推,08号商品售价为16元。
if(select_sta[1]==1)
money_sum=money_sum-2;
if(select_sta[2]==1)
money_sum=money_sum-4;
state=S_requirechange;//余额大于等于零,判断是否需要找钱
money_sum=money_sum-6;
if(select_sta[4]==1)
money_sum=money_sum-8;
if(select_sta[5]==1)
money_sum=money_sum-10;
if(select_sta[6]==1)
money_sum=money_sum-12;
if(select_sta[7]==1)
money_sum=money_sum-14;
if(select_sta[8]==1)
money_sum=money_sum-16;
if(money_sum>=0)
S_out:
else//找零余额不够,退出所有钱,并进入管理状态
state=S_management;
end
S_requirechange:
begin
if(money_sum==0)//余额为零,不用找零,直接出货
state=S_out;
else if(money_sum>0)//余额不为零,进入找零模式
state=S_change;
end
S_change:
begin
change = money_sum;//将余额找零
cash = cash - money_sum;//减去找零
state = S_out;
end
if(select_sta[8]==1)
begin//对应的货物减1
if(select_sta[1]==1)
left1=left1-1;
if(select_sta[2]==1)
left2=left2-1;
if(select_sta[3]==1)
left3=left3-1;
if(select_sta[4]==1)
left4=left4-1;
if(select_sta[5]==1)
left5=left5-1;
if(select_sta[6]==1)
left6=left6-1;
if(select_sta[7]==1)
left7=left7-1;
left8=left8-1;
out=select_sta;//出货
state=S_idle;//进入空闲状态
end
endcase
end
end
endmodule
`timescale 1ns/1ns
`include "./vending_machine_gx.v"
module VendingMac_top;
reg clk;
reg rst;//复位
reg manage;//管理员管理信号
reg [9:0] manage_money;//管理售货机内现金
reg [3:0] money_input;//用户输入钱
reg [7:0] select;
reg confirm,init;//确认取消
reg [5:0] left01,left02,left03,left04,left05,left06,left07,left08;
//剩余商品数,认为不超过63
wire integer change;//找零
wire [7:0] out;//出货
initial
begin
clk=0;
select=8'b00000000;//初始并没有选择任何商品
confirm=0;
init=0;
money_input=0;
manage=0;
end
always
begin
#5 clk=~clk;//产生时钟
#5 clk=~clk;
end
initial
begin
#10 rst = 1;//进行初始化,使售货机初始状态为S_idle
#10 rst = 0;
#10 manage=1;
#10 left01=63;//每种商品的数量
left02=62;
left03=61;
left04=60;
left05=59;
left06=58;
left07=57;
left08=56;
manage_money=1000;//开始售货机里放1000元
#10 manage=0;//管理结束
#30
#10 select=8'b00000010;//posedge1
#10 select=8'b00000000;
#10 confirm=1;//确认购买
#10 confirm=0;
#50 money_input=10;//投币10元
#10 money_input=0;//投币间隔
#10 money_input=5;
#10 money_input=0;
#10 money_input=1;
#10 money_input=0;
end
initial
begin
$monitor($time,,,"select=%b,money_input=%d,confirm=%d,init=%d,out=%b,change=%d",select,money_input,confirm,init,out,change);
#60000 $finish;
end
VendingMac v1(clk,money_input,select,confirm,init,left01,left02,left03,left04,left05,left06,left07,left08,change,out,manage,manage_money,rst);
endmodule