大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
今天给大侠带来“FPGA学习系列 altera"系列,持续更新。此学习心得是本人之前所写,所用设计软件为Quartus II 13.1,现Quartus II 新版本已更新到19 ,以下仅供初学者学习参考。后续会更新其他系列,敬请关注。话不多说,上货。
对于每一个的小实验,我们都可以把它看作是一个小项目,逐步的去分析,设计,调试,最后完成功能。下面我们就开始我们的“小项目”。
项目名称:自动售货机
具体要求:一个饮料自动售货机,一听饮料的售价2.5美元,可以使用两种硬币:1美元(one_dollar),0.5美元(half_dollar),同时售货机有找零的功能。
架构图如下:
- one_dollar:代表投入1美元硬币
- half_dollar:代表投入0.5美元硬币
- half_out:表示找零信号
- dispense:表示机器售出一听饮料
系统设计:
1. 工程的名称:sell
2. 状态转移图如下:
- IDLE:没有钱的状态
- HALF:0.5美元的状态
- ONE:1美元的状态
- ONE_HALF:1.5美元的状态
- TWO:2美元的状态
由于此状态转移图较为简单,故而没有写出转移条件,读者简单分析就可理解。例如:当前已经投入1美元,若再次投入1美元时,应该进入2美元的状态。
设计代码如下:
代码语言:javascript复制/*
模块名称:sell
模块功能:一个饮料自动售货机,每听饮料的售价2.5美元,可以使用两种硬币:1美元(one_dollar),0.5美元(half_dollar),同时售货机有找零的功能。
作者:郝旭帅
邮箱:746833924@qq.com
*/
module sell (clk, rst_n, half_dollar, one_dollar, dispense, half_out);
input clk;
input rst_n;
input half_dollar;
input one_dollar;
output reg dispense;
output reg half_out;
reg [2:0] state;//状态寄存器
localparam IDLE = 3'b000,//定义状态
HALF = 3'b001,
ONE = 3'b010,
ONE_HALF = 3'b011,
TWO = 3'b100;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
state <= IDLE;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
case (state)
IDLE : begin
if (one_dollar)
begin
state <= ONE;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
if (half_dollar)
begin
state <= HALF;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
state <= IDLE;
dispense <= 1'b0;
half_out <= 1'b0;
end
end
end
HALF : begin
if (one_dollar)
begin
state <= ONE_HALF;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
if (half_dollar)
begin
state <= ONE;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
state <= HALF;
dispense <= 1'b0;
half_out <= 1'b0;
end
end
end
ONE : begin
if (one_dollar)
begin
state <= TWO;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
if (half_dollar)
begin
state <= ONE_HALF;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
state <= ONE;
dispense <= 1'b0;
half_out <= 1'b0;
end
end
end
ONE_HALF : begin
if (one_dollar)
begin
state <= IDLE;
dispense <= 1'b1;
half_out <= 1'b0;
end
else
begin
if (half_dollar)
begin
state <= TWO;
dispense <= 1'b0;
half_out <= 1'b0;
end
else
begin
state <= ONE_HALF;
dispense <= 1'b0;
half_out <= 1'b0;
end
end
end
TWO : begin
if (one_dollar)
begin
state <= IDLE;
dispense <= 1'b1;
half_out <= 1'b1;
end
else
begin
if (half_dollar)
begin
state <= IDLE;
dispense <= 1'b1;
half_out <= 1'b0;
end
else
begin
state <= TWO;
dispense <= 1'b0;
half_out <= 1'b0;
end
end
end
default : state <= IDLE;
endcase
end
end
endmodule
激励代码如下:
代码语言:javascript复制/*
模块名称:sell_tb
模块功能:为sell模块提供激励信号
作者:郝旭帅
邮箱:746833924@qq.com
*/
`timescale 1ns/1ps
module sell_tb;
reg clk;
reg rst_n;
reg half_dollar;
reg one_dollar;
wire dispense;
wire half_out;
initial begin
clk = 1'b1;
rst_n = 1'b0;
half_dollar = 1'b0;//复位期间,不投入任何硬币
one_dollar = 1'b0;
# 200.1
rst_n = 1'b1;
# 200
half_dollar = 1'b1;//0.5美元
# 20
half_dollar = 1'b0;
# 200
one_dollar = 1'b1; //1美元
# 20
one_dollar = 1'b0;
# 200
half_dollar = 1'b1; //0.5美元
# 20
half_dollar = 1'b0;
# 200
one_dollar = 1'b1; //1美元
# 20
one_dollar = 1'b0;
# 2000
$stop;
end
always # 10 clk = ~clk;//系统时钟为50MHz
sell sell_dut(
.clk(clk),
.rst_n(rst_n),
.half_dollar(half_dollar),
.one_dollar(one_dollar),
.dispense(dispense),
.half_out(half_out)
);
endmodule
解析:
由于驱动时钟(本地晶振)为50MHz,我们外部投入硬币的信号的高电平的时间必须和驱动时钟的周期相同,若时间过短,系统采集不到外部硬币投入信号,若过长,系统会认为外部连续多次投入硬币。所以:
one_dollar = 1'b1; //1美元
# 20 //驱动时钟为50MHz,故而高电平持续时间为20ns
one_dollar = 1'b0;
# 200
仿真波形如下:
当输入0.5 1 0.5 1 = 3时,输出饮料并找零。
如果本地晶振和笔者的设计不同,请自行更改设计,以保证设计的正确性。如果还是有不明白的读者可以发邮件到我邮箱或者加群询问。
END