《EDA技术应用》学生项目报告
二、项目目的
1. 项目原理
通过VHDL多层文本设计,实现自动售货机控制系统的设计,通过仿真来模拟售货机内部进行选择商品、投入货币、计算金额,数码管显示等等售货机日常应用功能。
2. 项目意义
本次自动售货机控制系统的设计主要是为了锻炼VHDL文本编写能力,VHDL项目的实践。而自动售货机系统也具有很好的设计理念融入,涵盖了许多VHDL设计的核心方法和知识点。项目也具有较高的实用性,可以很好的提高购物的便利性,自动售货机可以部署在人流量大的地区,提供24小时不间断服务,增加消费者购物的欲望和随时需求。降低人力成本:自动化销售减少了对店员的依赖,降低了长期的运营成本。数据收集与分析:现代自动售货机可收集销售数据,帮助商家分析消费行为,优化商品结构和补货策略。
三、项目内容
1. EDA设计流程
- 首先是需求分析,确定售货机的功能需求,如支持的商品类型、支付方式,显示内容,等待规则等。
- 系统文本架构设计:确定好功能模块划分,确定好底层和顶层设计内容。
- VHDL文本设计编写:使用Quartus编写VHDL文本,编写多层设计。
2. VHDL设计文本
顶层文本:
LIBRARY IEEE; ---- ieee标准库声明(电子工程协会标准)
USE IEEE.STD_LOGIC_1164.all; -- 使用ieee.std_logic_1164库(标准逻辑库)的所有数据包
USE IEEE.STD_LOGIC_UNSIGNED.ALL; -- 使用ieee.std_logic_unsigned库的所有数据包,引入重载运算符
USE IEEE.STD_LOGIC_ARITH.ALL; -- 引入新的类型,像是unsigned类型等等
ENTITY mall IS
PORT(clk20:IN STD_LOGIC; --时钟
rst2:IN STD_LOGIC; --复位键
coin2:IN STD_LOGIC_VECTOR(1 DOWNTO 0); --投币
pp:IN STD_LOGIC_VECTOR(3 DOWNTO 0); --选择商品
l12:OUT STD_LOGIC_VECTOR(6 DOWNTO 0) ; --显示商品价格
l22:OUT STD_LOGIC_VECTOR(6 DOWNTO 0); --显示需要补交的钱数
l32:OUT STD_LOGIC_VECTOR(6 DOWNTO 0); --显示找零钱数
suc2:OUT STD_LOGIC; --交易成功信号
pd: out std_logic_vector(3 downto 0);
showout2:OUT STD_LOGIC); --找零成功信号
END mall;
ARCHITECTURE jie OF mall IS
COMPONENT fp IS --分频模块端口信息
PORT(CLK20:IN STD_LOGIC;
co:OUT STD_LOGIC);
END COMPONENT fp;
COMPONENT xianshi IS --显示模块端口信息
PORT(price: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
need: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
mout: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
l1: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
l2: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
l3: OUT STD_LOGIC_VECTOR(6 DOWNTO 0));
END COMPONENT xianshi;
COMPONENT shouhuoji IS --主控状态机模块端口信息
PORT( clk: IN STD_LOGIC;
rst: IN STD_LOGIC;
coIN: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
p: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
price: OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
need: OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
mout: OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
suc: OUT STD_LOGIC;
showout:OUT STD_LOGIC;
pt: OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END COMPONENT shouhuoji;
SIGNAL co1:STD_LOGIC;
SIGNAL need1:STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL price1:STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL mout1:STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
U1:fp PORT MAP (clk20,co=>co1); --各模块间的映射连接
U2:shouhuoji PORT MAP(co1, rst2, coin2, pp, price1, need1, mout1, suc2, showout2, pd);
U3:xianshi PORT MAP(price1, need1, mout1, l12, l22, l32);
END jie;
底层u1,进行延时:
LIBRARY IEEE; ---- ieee标准库声明(电子工程协会标准)
USE IEEE.STD_LOGIC_1164.all; -- 使用ieee.std_logic_1164库(标准逻辑库)的所有数据包
USE IEEE.STD_LOGIC_UNSIGNED.ALL; -- 使用ieee.std_logic_unsigned库的所有数据包,引入重载运算符
USE IEEE.STD_LOGIC_ARITH.ALL; -- 引入新的类型,像是unsigned类型等等
ENTITY fp IS
PORT(CLK20:IN STD_LOGIC;
co:OUT STD_LOGIC);
END fp;
ARCHITECTURE a OF fp IS
SIGNAL q:STD_LOGIC_VECTOR(24 DOWNTO 0);
BEGIN
PROCESS(CLK20)
BEGIN
IF CLK20' EVENT AND CLK20='1' THEN
-- IF q="1001100010010110011111111" THEN -- 这里是延时的10秒在仿真里看不出来
IF q="0000000000000000000000011" THEN
q<="0000000000000000000000000";co<='1'; ELSE
q<=q+1;co<='0';
END IF;
END IF;
END PROCESS;
END a;
底层u2进行售货机内部运算,传输数据:
LIBRARY IEEE; ---- ieee标准库声明(电子工程协会标准)
USE IEEE.STD_LOGIC_1164.all; -- 使用ieee.std_logic_1164库(标准逻辑库)的所有数据包
USE IEEE.STD_LOGIC_UNSIGNED.ALL; -- 使用ieee.std_logic_unsigned库的所有数据包,引入重载运算符
USE IEEE.STD_LOGIC_ARITH.ALL; -- 引入新的类型,像是unsigned类型等等
ENTITY shouhuoji IS
PORT( clk:IN STD_LOGIC; --时钟
rst:IN STD_LOGIC; --复位信号
coin:IN STD_LOGIC_VECTOR(1 DOWNTO 0); --投币
p:IN STD_LOGIC_VECTOR(3 DOWNTO 0); --选择商品
price:OUT STD_LOGIC_VECTOR(3 DOWNTO 0); --商品价格
need:OUT STD_LOGIC_VECTOR(3 DOWNTO 0); --需要补交的钱数
mout:OUT STD_LOGIC_VECTOR(3 DOWNTO 0); --显示找零钱数
suc:OUT STD_LOGIC; --交易成功信号
showout:OUT STD_LOGIC;
pt:OUT STD_LOGIC_VECTOR(3 DOWNTO 0)); --找零成功信号
END shouhuoji;
ARCHITECTURE H OF shouhuoji IS
TYPE state_type IS(a,b,c,d,g); --枚举数据类型定义状态机共a、b、c、d、g五种状态
SIGNAL current_state:state_type:=a; --定义初始状态为a
SIGNAL q: integer range 0 to 100; --投币过程中10秒计时的计数器
SIGNAL paidtemp: STD_LOGIC_VECTOR(3 DOWNTO 0); --已付的钱数
SIGNAL needtemp: STD_LOGIC_VECTOR(3 DOWNTO 0); --投币后与单价的差价
SIGNAL backmoney: STD_LOGIC_VECTOR(3 DOWNTO 0); --需要找零的钱数
SIGNAL pricetemp: STD_LOGIC_VECTOR(3 DOWNTO 0); --商品的单价
begin
PROCESS(clk)
BEGIN
IF rst='0' THEN
current_state<=b;
ELSIF clk' EVENT AND clk='1' THEN
pt<="1111";
CASE current_state IS
WHEN a=>
paidtemp<="0000";
backmoney<="0000";
pricetemp<="0000";
q<=0;
mout<="0000";
need<="0000";
price<="0000";
suc<='0';
showout<='0';
IF p="0001" THEN
pricetemp<=pricetemp+2;
needtemp<=pricetemp;
current_state<=b;
ELSIF p="0010" THEN
pricetemp<=pricetemp+3;
needtemp<=pricetemp;
current_state<=b;
ELSIF p="0100" THEN
pricetemp<=pricetemp+4;needtemp<=pricetemp;
current_state<=b;
ELSIF p="1000" THEN pricetemp<=pricetemp+6;needtemp<=pricetemp;
current_state<=b;
ELSE
current_state<=a;
END IF;
price<=pricetemp;
pt<=pricetemp;
WHEN b=>
q<=q+1; -- 10秒开始计时
CASE coin IS
WHEN "10"=> paidtemp<=paidtemp+1; --投入1元则已付钱数加1
WHEN "01"=> paidtemp<=paidtemp+5; --投入5元则已付钱数加5
WHEN others=> paidtemp<=paidtemp; --其余情况已付钱数不变
END CASE;
IF paidtemp>=pricetemp THEN
needtemp<="0000";
backmoney<=paidtemp-pricetemp;
current_state<=d;
END IF;
IF paidtemp<pricetemp THEN
needtemp<=pricetemp-paidtemp;backmoney<="0000";
IF q=10 THEN
IF paidtemp="0000" THEN
current_state<=a; --到了延时的10秒,如果已付钱数为0就回到a状态
ELSE
q<=0;
current_state<=c;--已付钱数不为零则重新开始计时,进入状态c
END IF;
ELSE
current_state<=b; --不到10秒,继续停留在状态b
END IF;
END IF;
price<=pricetemp;
need<=needtemp;
pt<=pricetemp;
WHEN c=>
q<=q+1; --10秒开始计时
CASE coin IS
WHEN "10"=> paidtemp<=paidtemp+1; --投币1元则已付加1
WHEN "01"=> paidtemp<=paidtemp+5; --投币5元则已付加5
WHEN others=> paidtemp<=paidtemp; --其余情况已付不变
END CASE;
IF paidtemp>=pricetemp THEN --接下来判断与b状态类似
needtemp<="0000";backmoney<=paidtemp-pricetemp;current_state<=d;
END IF; --当已付钱数大于单价,则还需要钱数为0,找零钱数等于已付钱数减去单价,进入状态d
WHEN d=>
suc<='1'; --交易成功指示灯亮
price<=pricetemp; --显示单价
pt<=pricetemp;
need<=needtemp; --显示需补交的钱
IF backmoney>"0000" THEN
mout<=backmoney;showout<='1'; --如果已付大于单价即找零不为0,显示找零同时,找零指示灯亮
ELSE
mout<="0000";showout<='0'; --已付小于单价即找零为0并显示,找零指示灯不亮
END IF;
current_state<=a; --返回到状态a
WHEN g=>
suc<='0';price<=pricetemp;need<=needtemp; --交易成功指示灯不亮,同时显示单价和还需多少钱
IF backmoney>"0000" THEN
mout<=backmoney;
showout<='1';--如果找零大于0(即投币了但小于单价),退钱并且找零指示灯亮
ELSE mout<="0000";showout<='0'; --如果为0(未投币),找零指示灯不亮
END IF;
current_state<=a; --回到状态a
end case;
END IF;
END process;
END H;
底层u3制作显示数码馆段选数据:
LIBRARY IEEE; ---- ieee标准库声明(电子工程协会标准)
USE IEEE.STD_LOGIC_1164.all; -- 使用ieee.std_logic_1164库(标准逻辑库)的所有数据包
USE IEEE.STD_LOGIC_UNSIGNED.ALL; -- 使用ieee.std_logic_unsigned库的所有数据包,引入重载运算符
USE IEEE.STD_LOGIC_ARITH.ALL; -- 引入新的类型,像是unsigned类型等等
entity xianshi is
PORT(price: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
need: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
mout: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
l1: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
l2: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
l3: OUT STD_LOGIC_VECTOR(6 DOWNTO 0));
end xianshi;
architecture tt of xianshi is
begin
PROCESS(price,need,mout)
BEGIN
CASE price IS
WHEN "0000"=>l1<="0111111";
WHEN "0001"=>l1<="0000110";
WHEN "0010"=>l1<="1011011";
WHEN "0011"=>l1<="1001111";
WHEN "0100"=>l1<="1100110";
WHEN "0101"=>l1<="1101101";
WHEN "0110"=>l1<="1111101";
WHEN "0111"=>l1<="0000111";
WHEN "1000"=>l1<="1111111";
WHEN "1001"=>l1<="1101111";
WHEN "1010"=>l1<="1110111";
WHEN OTHERS=>l1<=null;
END CASE;
CASE need IS
WHEN "0000"=>l2<="0111111";
WHEN "0001"=>l2<="0000110";
WHEN "0010"=>l2<="1011011";
WHEN "0011"=>l2<="1001111";
WHEN "0100"=>l2<="1100110";
WHEN "0101"=>l2<="1101101";
WHEN "0110"=>l2<="1111101";
WHEN "0111"=>l2<="0000111";
WHEN "1000"=>l2<="1111111";
WHEN "1001"=>l2<="1101111";
WHEN "1010"=>l2<="1110111";
WHEN OTHERS=>l2<=null;
end case;
CASE mout IS
WHEN "0000"=>l3<="0111111";
WHEN "0001"=>l3<="0000110";
WHEN "0010"=>l3<="1011011";
WHEN "0011"=>l3<="1001111";
WHEN "0100"=>l3<="1100110";
WHEN "0101"=>l3<="1101101";
WHEN "0110"=>l3<="1111101";
WHEN "0111"=>l3<="0000111";
WHEN "1000"=>l3<="1111111";
WHEN "1001"=>l3<="1101111";
WHEN "1010"=>l3<="1110111";
WHEN OTHERS=>l3<=null;
end caSE;
end process;
end tt;
3. VHDL仿真以及时序分析
因为实际效果延时时间到达10秒,而在仿真中最大时间是10ns这也导致一开始一直没有现象改变,后来再次进入深度理解,才知道不是文本设计问题而只是单纯的显示问题,通过调整代码中的一些配置,成功做到了仿真效果。以下是仿真的结果
80ns处,成功取得商品价格信息
116.21ns时,投入了一定的硬币后,发现需要找补的价格l22逐渐从需找补整个价格“1100110”到现在的“0111111”也就是数码管的4到了现在的0,成功补齐了要付的货币,suc2置为高电平,输出表明交易成功。甚至还可以发现在l32这里,也有了数字,说明在延时时间内,多投了钱, 现在成功进行了“0000110”的找零也就是1的找零,最后也有showout2的高电平,输出表明找零成功。
最后再让我们确定一下投入的货币数,因为我们设置的延时为“0011”三次时钟上升沿,在到来三次开始进行投币计数,记三次后结束时刚好投入为5,5-4=1,正好对应找零数,设计完成。
因为实际效果延时时间到达10秒,而在仿真中最大时间是10ns这也导致一开始一直没有现象改变,后来再次进入深度理解,才知道不是文本设计问题而只是单纯的显示问题,通过调整代码中的一些配置,成功做到了仿真效果。以下是仿真的结果
80ns处,成功取得商品价格信息
116.21ns时,投入了一定的硬币后,发现需要找补的价格l22逐渐从需找补整个价格“1100110”到现在的“0111111”也就是数码管的4到了现在的0,成功补齐了要付的货币,suc2置为高电平,输出表明交易成功。甚至还可以发现在l32这里,也有了数字,说明在延时时间内,多投了钱, 现在成功进行了“0000110”的找零也就是1的找零,最后也有showout2的高电平,输出表明找零成功。
最后再让我们确定一下投入的货币数,因为我们设置的延时为“0011”三次时钟上升沿,在到来三次开始进行投币计数,记三次后结束时刚好投入为5,5-4=1,正好对应找零数,设计完成。