先陈述下本次设计实现的功能:用FPGA实现了自动贩卖机。首先可以通过拨码开关选择货物,货物价格有三种,分别为2元,2.5元,3元,然后还是通过拨码开关,输入投币金额,面额为0.5元,1元,2元。当投币金额小于货物价格时,数码管显示所投金额,当投币金额大于货物价格时,数码显示找零的金额,同时蜂鸣器报警。
先给张实物图:
接下来介绍设计的思路。本次开发板是基于EP4CE6E22C8N的Cyclone IV系列的FPGA、(型号)的FLASH芯片和50MHz的晶振,通过对板子的元器件进行控制,从而实现特定功能。拨动开关的管脚配置和电路原图如下:
从图中可以看出,当拨码开关拨上时,管脚电平为1。所以可以根据检测电平变换,来决定货物的价格。 本次代码的接口定义如下:
其中三位宽的chose用于选择货物价格。选择价格的代码如下:
接下来,程序执行的核心是一个二段式状态机。状态机代码如下:
状态机执行流程:首先s0状态是输入投币金额,用三个变量分别保存并且记录输入的金额,最后用一个加法式子算出总的投币金额
s1状态是判断所投金额有没有大于货物价格,如果没有大于货物价格,那么继续回到s0状态,继续进行投币。如果所投金额大于货物价格,那么就执行到s2状态。
s2状态,主要执行蜂鸣器响的操作,然后在数码管上显示出找零的金额。
在本模块调用了数码管显示模块,将要显示的数据(货物价格、投币金额/找零金额)显示在数码管上。
在这个模块,取出要显示数据的各个位。举个例子,要显示的货物价格是25(2.5元),那么首先取出十位数字2,再取出个位数字5。将数字传递给显示模块,接下来的任务就是显示模块显示数据了。
显示模块的代码:
module seg(
input sys_clk,sys_rest,
input [15:0] data,
output reg [3:0] sel,
output reg [6:0] seg_led
);
parameter CLK_NUM=4'd10;
parameter MSNUM=14'd5000;
reg [3:0] CNT_NUM;
reg CLK;
reg [12:0] MSCNT;
reg MS_flag;
reg [3:0] num_display;
reg [15:0] num;
reg [2:0] sel_num; //选择哪一位数码管被点亮
//wire define
wire [3:0] data0 ; // 个位数
wire [3:0] data1 ; // 十位数
wire [3:0] data2 ; // 百位数
wire [3:0] data3 ; // 千位数
//提取显示数值所对应的十进制数的各个位
assign data0 = data[3:0]; // 个位数
assign data1 = data[7:4]; // 十位数
assign data2 = data[11:8]; // 百位数
assign data3 = data[15:12]; // 千位数
always @(posedge sys_clk or negedge sys_rest) begin
if(!sys_rest)
begin
CNT_NUM<=4'd0;
CLK<=1'd1;
end
else if(CNT_NUM<=CLK_NUM/2-1'b1)
begin
CLK<=~CLK;
CNT_NUM<=4'd0;
end
else
begin
CNT_NUM<=CNT_NUM+1;
CLK<=CLK;
end
end
always @(posedge CLK or negedge sys_rest) begin
if(!sys_rest)
num<=16'd0;
else
begin
num[15:12] <= data3; //则依次给4位数码管赋值
num[11:8] <= data2;
num[ 7:4] <= data1;
num[ 3:0] <= data0;
end
end
always @(posedge CLK or negedge sys_rest) begin //产生1ms脉冲
if(!sys_rest)
begin
MSCNT<=13'd0;
MS_flag<=1'b0;
end
else if(MSCNT==MSNUM-1)
begin
MSCNT<=13'd0;
MS_flag<=1'b1;
end
else
begin
MSCNT<=MSCNT+1;
MS_flag<=1'b0;
end
end
always @(posedge CLK or negedge sys_rest) begin
if(!sys_rest)
sel_num<=0;
else if(MS_flag)
begin
if(sel_num<3'd3)
sel_num<=sel_num+1;
else
sel_num<=0;
end
else
sel_num<=sel_num;
end
always @(posedge CLK or negedge sys_rest) begin
if(!sys_rest)
sel<=4'b1111;
else
begin
case(sel_num)
3'd0: begin
sel<= 4'b1110; //显示数码管最低位
num_display<=num[3:0];
end
3'd1: begin
sel<= 4'b1101; //显示数码管第1位
num_display<=num[7:4];
end
3'd2: begin
sel<= 4'b1011; //显示数码管第2位
num_display<=num[11:8];
end
3'd3: begin
sel<= 4'b0111; //显示数码管第3位
num_display<=num[15:12];
end
default sel<= 4'b1111;
endcase
end
end
always @(posedge CLK or negedge sys_rest) begin
if(!sys_rest)
seg_led<=7'b100000;
else
begin
case(num_display)
4'h0 : seg_led <= 7'b1000000;
4'h1 : seg_led <= 7'b1111001;
4'h2 : seg_led <= 7'b0100100;
4'h3 : seg_led <= 7'b0110000;
4'h4 : seg_led <= 7'b0011001;
4'h5 : seg_led <= 7'b0010010;
4'h6 : seg_led <= 7'b0000010;
4'h7 : seg_led <= 7'b1111000;
4'h8 : seg_led <= 7'b0000000;
4'h9 : seg_led <= 7'b0010000;
4'd10: seg_led <= 7'b1111111; //不显示任何字符
default : seg_led <= 7'b1000000;
endcase
end
end
endmodule
这样就大功告成啦。后续会给出完整工程项目!