1.总体设计
采用FPGA来设计的原理图如图1.1所示.它由控制输入电路、FPGA、显示电路电路组成。
图1.1 采用FPGA设计的自动售货机原理方框图
控制输入电路主要是为用户设计的,起到一个输入控制的作用。FPGA是现场可编程逻辑器件,也是本设计方案的核心内容,它是实现自动售货机运作的主要控制模块。将编写好的HDL程序烧制到现场可编程逻辑器件FPGA中,然后通过控制输入电路把信号输入到FPGA,由八个开关控制输入信号即消费者选择商品和消费者投币及确认消费,动态数码管显示输出信号即找零环节和所选择的商品、消费者投币。
2、设计说明
本设计是以现场可编程逻辑器件(FPGA)为核心的自动售货机,利用Modesim软件编写verilog硬件描述语言程序以实现自动售货功能。
1.1、系统设计描述
(1)用四个发光二极管分别模拟售出价值为2元、5元、7元和10元的商品,购买者可以通过开关选择任意一种标价中的小商品;
(2)灯亮时表示该小商品售出;
(3)用开关分别模拟1元、5元,10元和20元货币投入,用四支发光二极管代表投入的货币的面值;
(4)每次只能售出一种小商品,当所投硬币达到或超过购买者所选面值时,售出货物并找回剩余的硬币,回到初始状态;
(5)当所投硬币值不足面值时,可通过一个复位键退回所投硬币,回到初始状态。
系统框图如图1.1所示:
3、详细状态描述
3.1 初始状态
rst为复位键,低电平有效,实现系统复位。
3.2 选商品状态
分别有价格为2元、5元、7元和10元的商品,每次选择商品前,设置一个标志位btn_sel_goods表示选择商品状态。此自动售货机每一次售货时只能一次选择一种商品,当同时选择两种以上时,选择商品无效,数码管显示清零,重新进行商品选择。选择商品后,数码管显示所选商品价格。
3.3 投币状态
当选好商品后,开始投币。同样有一标志位btn_price表示投币金额。投币口只接受面值为1元、5元10元和20元的货币,可以同时投入多种面值钱币。投完币后,先有一个确认买商品的过程,若投了币但又不购买商品了,就将全部投币金额退回;若确认购买商品,则进入下一状态。
3.4 找零状态
投完币,并确认购买商品后,进入找零状态。首先要将所投的金额与所选商品的价格做比较,若所投金额小于商品价格,则退回所投钱币;若大于等于商品价格,则两者做差,得到需要找零的钱。
4、仿真结果分析
设置测试程序各参数,运行tb文件,仿真结果如下图3.1、3.2所示。
图3.1
图3.1所示,商品价格选择为7元,即btn_sel_goods=2'b10,投币总额大于商品价格的情况,上图中btn_mony从右到左依次代表投币1元、10元、20元、10元、20元、1元、20元和10元,共92元。red灯实时显示所投货币的面值。dis_price显示为0000001_0001111,对应数码管显示数字为07,即7元。 dis_mony为0000000_0100100,对应数码管显示数字为85。可以看出,实验结果很理想,说明代码符合设计要求。
图3.2
图3.2所示,商品价格选择为7元,即btn_sel_goods=2'b10,投币总额小于商品价格的情况,上图中btn_mony从右到左依次代表投币1元、5元,共6元。red灯实时显示所投货币的面值。dis_price显示为0000001_0001111,对应数码管显示数字为07,即7元。 dis_mony为0000001_0100000,对应数码管显示数字为06,即6元,可以看出,实验结果很理想,说明代码符合设计要求。
附录:
/
//自动售货机
//btn_sel_goods 为所选商品,由于实验条件的限制,本实验中只给出4种价格的商品
//btn_ok 购买确认键
//led[3:0] 客户单次投币币值
//beep 客户所投金额或购买商品后剩余金额大于所选商品价格时,该灯亮,表示可以继续购买
//led_warn 客户所投金额或购买商品后剩余金额小于所选商品价格时,该灯亮,表示不可以继续购买
//charge 找零按键,按下后找零
/
`timescale 1ns / 1ps
module auto_machine(rst,clk,charge,dis_mony,dis_price,
btn_ok,btn_mony,btn_sel_goods,led_warn,led,beep);
input clk,rst;
input charge,btn_ok; //找零/确定
input [3:0] btn_mony; //选择放入的钱1元5元10元20元
input [1:0] btn_sel_goods; //选择2元5元7元10元商品
output [3:0] led; //分别显示投入的是1元5元10元还是20元货币
output led_warn; //钱不足,指示灯
output [13:0] dis_mony; //数码管显示投入总金额
output [13:0] dis_price; //数码管显示价钱
output beep; //交易完成提醒
reg [3:0] led;
reg led_warn;//投入钱币不够时警告
reg [13:0] dis_mony;
reg [13:0] dis_price;
reg beep;
parameter S2=2'b00,S5=2'b01,S7=2'b10,S10=2'b11;
parameter M1=4'b0001,M5=4'b0010,M10=4'b0100,M20=4'b1000;
wire[3:0] btn_mony_buf0;
wire[3:0] btn_mony_buf1;
wire btn_ok_b0;
wire btn_ok_b1;
wire[1:0] btn_sel_goods_b;
wire charge_buf;
reg [6:0] price,price_all;
key_down uut4(.rst(rst),.clk(clk),.det(btn_mony[0]),.key_don(btn_mony_buf0[0]));
key_down uut5(.rst(rst),.clk(clk),.det(btn_mony[1]),.key_don(btn_mony_buf0[1]));
key_down uut6(.rst(rst),.clk(clk),.det(btn_mony[2]),.key_don(btn_mony_buf0[2]));
key_down uut7(.rst(rst),.clk(clk),.det(btn_mony[3]),.key_don(btn_mony_buf0[3]));
edge_det uut0(.rst(rst),.clk(clk),.det(btn_mony_buf0[0]),.pos_edge(btn_mony_buf1[0]));
edge_det uut1(.rst(rst),.clk(clk),.det(btn_mony_buf0[1]),.pos_edge(btn_mony_buf1[1]));
edge_det uut2(.rst(rst),.clk(clk),.det(btn_mony_buf0[2]),.pos_edge(btn_mony_buf1[2]));
edge_det uut3(.rst(rst),.clk(clk),.det(btn_mony_buf0[3]),.pos_edge(btn_mony_buf1[3]));
key_down uut8(.rst(rst),.clk(clk),.det(btn_ok),.key_don(btn_ok_b0));
edge_det uut9(.rst(rst),.clk(clk),.det(btn_ok_b0),.pos_edge(btn_ok_b1));
key_down uut10(.rst(rst),.clk(clk),.det(btn_sel_goods[0]),.key_don(btn_sel_goods_b[0]));
key_down uut11(.rst(rst),.clk(clk),.det(btn_sel_goods[1]),.key_don(btn_sel_goods_b[1]));
key_down uut12(.rst(rst),.clk(clk),.det(charge),.key_don(charge_buf));
always @(negedge rst or posedge clk)
begin
if(!rst)
begin
led <= 4'b0;
led_warn <=1'b0;
beep <=1'b0;
price_all =7'd0;
price <= 7'd0;
dis_mony <=14'h3fff;
dis_price <=14'h3fff;
end
else
begin
case(btn_mony_buf1)
M1: //所投钱币+1
price_all =price_all + 7'd1;
M5: //所投钱币+5
price_all =price_all + 7'd5;
M10: //所投钱币+10
price_all =price_all+ 7'd10;
M20: //所投钱币+20
price_all =price_all + 7'd20;
default: led <= 4'b0;
endcase
case(btn_mony)
M1: //显示所投钱币币值
led <= M1;
M5:
led <= M5;
M10:
led <= M10;
M20:
led <= M20;
default:
led <= 4'b0;
endcase
case(btn_sel_goods_b)
S2:begin price <= 7'd2;
end
S5:begin price <= 7'd5;
end
S7:begin price <= 7'd7;
end
S10:begin price <= 7'd10;
end
default:begin price <= 7'd0;
end
endcase
if(!charge_buf)
price_all = 7'd0; //退回零钱
else
begin
if(btn_ok_b1)
begin
if(price_all < price)
led_warn = 1'b1;
else
price_all = price_all-price;
end
else
begin
led_warn <= 1'b0;
beep <= 1'b0;
end
end
if(price_all led_warn <= 1'b1; //投币差额小于商品价格时警告 else begin led_warn <= 1'b0; beep <= 1'b1; //所投金额大于商品价格时,亮灯表示可以购买 end dis_mony[6:0] <= led7(price_all%10);//找零金额个位显示 dis_mony[13:7] <= led7(price_all/10);//找零金额十位显示 dis_price[6:0] <= led7(price%10);//商品价格个位显示 dis_price[13:7] <= led7(price/10);//商品价格十位显示 end end /*数码管段码表*/ function [6:0] led7; input [6:0] dis_input; begin case (dis_input) 0 : led7 = 7'b1000000; 1 : led7 = 7'b1111001; 2 : led7 = 7'b0100100; 3 : led7 = 7'b0110000; 4 : led7 = 7'b0011001; 5 : led7 = 7'b0010010; 6 : led7 = 7'b0000011; 7 : led7 = 7'b1111000; 8 : led7 = 7'b0000000; 9 : led7 = 7'b0011000; default : led7 = 7'b111_1111; endcase end endfunction endmodule / //实现边沿检测的功能 //det为原输入需检测信号 //pos_edge为输出的一个脉冲长度的上升沿信号 // / module edge_det(rst,clk,det,pos_edge); input rst,clk; input det; output pos_edge; wire pos_edge; reg p_edge1; reg p_edge2; assign pos_edge = (!p_edge1 & p_edge2); always @(negedge rst or posedge clk) begin if (!rst) begin p_edge1<=1'b0; p_edge2<=1'b0; end else begin p_edge1<= det; p_edge2<=p_edge1; end end endmodule / //实现按键消抖工功能 //det为原输入信号 //key_don为输出确认信号 // //CONT实现计数,经过CONT个脉冲的延时 // / module key_down(rst,clk,det,key_don); input rst,clk; input det; // output key_don; // reg p_edge1; reg p_edge2; reg[24:0] cont; parameter CONT = 25'd050; assign key_don = (det & p_edge1 & p_edge2); always @(negedge rst or posedge clk) begin if (!rst) begin p_edge1<=1'b0; p_edge2<=1'b0; cont<=25'b0; end else begin if(cont==CONT) begin cont<=0; p_edge1 <= det; p_edge2 <=p_edge1; end else cont<=cont+1'b1; end end endmodule /// //tb文件 /// `timescale 1ns / 1ps module tb_mb; reg clk,rst,btn_ok; reg [3:0] btn_mony; //选择放入的钱1元5元10元20元 reg [1:0] btn_sel_goods; //选择商品2、5、7、10元 wire [3:0] led; //led_5,led_10,led_15,led_20 wire led_warn; //钱不足,指示灯 wire [6:0] dis_mony; //数码管显示投入面值 wire [6:0] dis_price; //数码管显示价钱 wire beep; auto_machine uut(.clk,.rst,.dis_mony,.dis_price, .btn_ok,.btn_mony,.btn_sel_goods, .led_warn,.led,.beep); initial begin clk = 1'b0; rst = 1'b0; btn_ok=1'b0; #20 rst=1; #10 btn_sel_goods=2'b10; #20 btn_mony=4'b0001; #30 btn_mony=4'b0100; #50 btn_mony=4'b0010; #100 btn_ok=1'b1; end always #2 clk=~clk; endmodule