【FPGA入门八】自动售货机
文章目录
- 一.任务
- 二.工程项目
- Verilog HDL编写
- ①设计按键消抖模块
- ②设计数码管位选驱动
- ③设计数码管显示模块
- ④设计售货机购物模块
- ⑤设计顶层模块
- 三.总结
一.任务
功能完整描述:
KEY4:开关机按键,复位时,默认是关机状态,数码管和LED灯均不亮,同时蜂鸣器响,其余按键按下无效。
KEY3:在开机状态时,投币1元
KEY2:在开机状态时,投币为0.5元
KEY1:当投币但少于货物的价格时,取消订单,数码管显示为0.0,同时LED灯实现跑马灯2s然后熄灭
当投币为2.5元时,刚好能够购买货物,4个LED灯同时闪烁2s然后熄灭,同时数码管数字清零
当投币为3元时,购买货物还需找零,4个LED灯实现流水灯2s然后熄灭,同时数码管数字清零
思路:
位选信号:
两种状态 6’b111_110,6’b111_101让这两个状态频繁切换,看起来像是一直亮着一样。
售货机总共有8个状态:
1:用户投币总数为0,也是初始状态,此时数码管显示0.0
①当用户选择投币0.5,跳转到第2个状态
②当用户选择投币1,跳转到第3个状态
2:用户投币总数为0.5,数码管显示0.5
①当用户选择投币0.5,跳转到第3个状态
②当用户选择投币1,跳转到第4个状态
③当用户取消,跳转到第8个状态,同时开始计时
3:用户投币总数为1,数码管显示1.0
①当用户选择投币0.5,跳转到第4个状态
②当用户选择投币1,跳转到第5个状态
③当用户取消,跳转到第8个状态,同时开始计时
4:用户投币总数为1.5,数码管显示1.5
①当用户选择投币0.5,跳转到第5个状态
②当用户选择投币1,跳转到第6个状态,同时开始计时
③当用户取消,跳转到第8个状态,同时开始计时
5:用户投币总数为2.0,数码管显示2.0
①当用户选择投币0.5,跳转到第6个状态,同时开始计时
②当用户选择投币1,跳转到第7个状态,同时开始计时
③当用户取消,跳转到第8个状态,同时开始计时
6:用户投币总数为2.5,数码管显示2.5
①该状态保持2s后结束计时
7:用户投币总数为3,数码管显示3.0
①该状态保持2s后结束计时
8:用户取消订,数码管显示0.0
①该状态保持2s后结束计时
二.工程项目
Verilog HDL编写
①设计按键消抖模块
key_debounce.v
module key_debounce(input wire clk,input wire rst_n,input wire key,output reg flag, //判断抖动是否消除的标志信号,0为抖动,1为抖动结束output reg key_value //消抖后稳定的按键值给到蜂鸣器模块
);//定义20ms延迟计数器,0.2s,1_000_000次
reg [19:0] delay_cnt;//寄存依次key的值用来判断按键是否消抖成功
reg key_reg;//20ms延时计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginkey_reg <= 1'b1; //复位信号,设置按键无效delay_cnt <= 1'b0; //计数器设置为0endelsebeginkey_reg <= key; if(key_reg == 1 && key == 0) //当这一次key值和上一次key值不一样,证明正在抖动delay_cnt <= 20'd1_000_000; //延迟时间20mselse if(delay_cnt > 0)delay_cnt <= delay_cnt - 1; //没有抖动,开始20ms倒计时elsedelay_cnt <= 1'b0; end
end//根据延时计数器获取按键状态以及按键值
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginflag <= 1'b0; //复位信号,设置信号标志为抖动key_value <= 1'b1; //设置抽样值为1endelsebeginif(delay_cnt == 20'd1) //倒计时1_000_000到1beginflag <= 1'b1;key_value <= key; //稳定20ms后将key值给到key_valueendelse beginflag <= 1'b0;key_value <= key_value; //20ms内先不取样endend
endendmodule
②设计数码管位选驱动
sel_drive.v
module sel_drive(input wire clk,input wire rst_n,input wire boot_flag,output wire [5:0] sel
);
parameter MAX_NUM = 10'd999;//20us
reg [5:0] sel_r;
reg [9:0] cnt;
reg sel_flag;//20us计时器
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt <= 10'd0;endelse if(cnt == MAX_NUM)begincnt <= 10'd0;endelse begincnt <= cnt + 1'd1;end
end//状态切换计数器模块
always@(posedge clk or negedge rst_n)beginif(!rst_n)sel_flag <= 1'b0;else if(cnt == MAX_NUM)sel_flag <= ~sel_flag; elsesel_flag <= sel_flag;
end //位选信号切换功能
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginsel_r <= 6'b111_111;endelse begincase(boot_flag)1'b0:begin //关机状态sel_r <= 6'b111_111;end1'b1:begin //开机状态case(sel_flag)1'b0: sel_r <= 6'b111_110;1'b1: sel_r <= 6'b111_101;default:sel_r <= sel_r;endcaseendendcaseend
endassign sel = sel_r;endmodule
③设计数码管显示模块
seg_scan.v
module seg_scan(input wire clk,input wire rst_n,input wire [3:0] money_flag,//用户投币标识input wire [5:0] sel,//位选信号output wire [7:0] seg//段选信号
);reg [3:0] number;
reg [7:0] seg_r;reg flag;//标识当前显示的数字是否是小数点后面的值 1:是 0:不是//根据用户投币标识的不同显示不同的数字
always@(*)begincase(money_flag)4'd0:begin//显示0.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;//第二个零不带小数点end6'b111_101: beginnumber = 4'd0;flag = 1'b1;//第一个零带小数点enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd1:begin//显示0.5case(sel)6'b111_110: beginnumber = 4'd5;flag = 1'b0;end6'b111_101: beginnumber = 4'd0;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd2:begin//显示1.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;end6'b111_101: beginnumber = 4'd1;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd3:begin//显示1.5case(sel)6'b111_110: beginnumber = 4'd5;flag = 1'b0;end6'b111_101: beginnumber = 4'd1;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd4:begin//显示2.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;end6'b111_101: beginnumber = 4'd2;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd5:begin//显示2.5case(sel)6'b111_110: beginnumber = 4'd5;flag = 1'b0;end6'b111_101: beginnumber = 4'd2;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd6:begin//显示3.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;end6'b111_101: beginnumber = 4'd3;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseend4'd7:begin//显示0.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;end6'b111_101: beginnumber = 4'd0;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseenddefault:begin//显示0.0case(sel)6'b111_110: beginnumber = 4'd0;flag = 1'b0;end6'b111_101: beginnumber = 4'd0;flag = 1'b1;enddefault : beginnumber = 4'd0;flag = 1'b0;endendcaseendendcase
endalways@(*)begincase(number)4'd0 : begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0100_0000;//数码管显示0 带小数点endelse beginseg_r = 8'b1100_0000;//数码管显示0 不带小数点endend4'd1 : begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0111_1001;//数码管显示1 带小数点endelse beginseg_r = 8'b1111_1001;//数码管显示1 不带小数点endend4'd2 : begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0010_0100;//数码管显示2 带小数点endelse beginseg_r = 8'b1010_0100;//数码管显示2 不带小数点endend4'd3 : begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0011_0000;//数码管显示3 带小数点endelse beginseg_r = 8'b1011_0000;//数码管显示3 不带小数点endend4'd5 : begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0001_0010;//数码管显示5 带小数点endelse beginseg_r = 8'b1001_0010;//数码管显示5 不带小数点endenddefault: begin if(flag == 1'b1)begin//显示小数点seg_r = 8'b0100_0000;//数码管显示0 带小数点endelse beginseg_r = 8'b1100_0000;//数码管显示0 不带小数点endendendcase
endassign seg = seg_r;endmodule
④设计售货机购物模块
vending_machine.v
module vending_machine(input wire clk,//时钟input wire rst_n,//复位input [3:0] key_value,//按键值input [3:0] flag,output reg beep,output reg [3:0] led,output wire [3:0] money_flag_w,output wire boot_flag_w
);parameter MAX_NUM = 9_999_999;//定义最大计时0.2s LED灯闪烁间隔
parameter MAX_NUM2 = 10;//2s计数基于0.2s//定义状态计数器,4个灯4个状态
reg [1:0] led_flag; reg boot_flag; //标识售货机是否处于开机状态 0:关机 1:开机reg [3:0] money_flag;//用户投币的状态有8种 分别是0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 退货的0reg [23:0] cnt = 0; //计时器赋初值为0reg [26:0] cnt_1 = 0; //计时器赋初值为0reg time_flag;//开始计时标记 1:开始计时 0:不开始//0.2s计数器模块
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt <= 1'b0; //按下复位键,清零else if(cnt == MAX_NUM) //计时器达到最大值,清零重新计数cnt <= 1'b0;elsecnt <= cnt + 1'b1;
end//2s计数器模块
always@(posedge clk or negedge rst_n)beginif(!rst_n)cnt_1 <= 1'b0; //按下复位键,清零else if(time_flag == 1)begin//开始计时if(cnt == MAX_NUM)beginif(cnt_1 <MAX_NUM2)begincnt_1 <= cnt_1 + 1'b1;endelse begincnt_1 <= 1'b0;//cnt_1计数达到最大就清空endendelse begincnt_1 <= cnt_1;//其余时间保持endendelse begincnt_1 <= 1'b0;//不计时,cnt_1清空end
end//状态切换计数器模块 led_flag的状态基于0.2s
always@(posedge clk or negedge rst_n)beginif(!rst_n)led_flag <= 1'b0;else if(cnt == MAX_NUM)led_flag <= led_flag + 1'b1; //超出宽度截取低两位elseled_flag <= led_flag;
end //售货机开关机状态控制 key4
always@(posedge clk or negedge rst_n)beginif(!rst_n)begin boot_flag <= 1'b0; //初始状态关机中beep <= 1'b0;//关机状态,蜂鸣器响 endelse if(flag[3] == 1'b1 && key_value[3] == 1'b0)begin//当按键key4按下时,售货机开关机状态取反boot_flag <= ~boot_flag;beep <= ~beep;//当开机状态时蜂鸣器停止endelse beginboot_flag <= boot_flag;//其余情况售货机开关机状态保持beep <= beep;//蜂鸣器状态保持end
endassign boot_flag_w = boot_flag;//当投入人民币购买功能
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginmoney_flag <= 4'd0;//初始用户投币为0endelse if(boot_flag)begin//只有当售货机是开机状态才可以购买case(money_flag)4'd0:begin//用户投币为0的情况下if(flag[1] == 1'b1 && key_value[1] == 1'b0)begin//用户投币0.5money_flag <= 4'd1;//记下第二个状态endelse if(flag[2] == 1'b1 && key_value[2] == 1'b0)begin//用户投币1money_flag <= 4'd2;//记下第三个状态end
// else if(flag[0] == 1'b1 && key_value[0] == 1'b0)begin//用户退货
// money_flag <= 4'd7;//记下第八个状态
// time_flag <= 1'b1;//开始计时
// endelse begin//用户没操作,状态保持money_flag <= money_flag;endend4'd1:begin//用户投币为0.5的情况下if(flag[1] == 1'b1 && key_value[1] == 1'b0)begin//用户投币0.5money_flag <= 4'd2;//记下第三个状态endelse if(flag[2] == 1'b1 && key_value[2] == 1'b0)begin//用户投币1money_flag <= 4'd3;//记下第四个状态endelse if(flag[0] == 1'b1 && key_value[0] == 1'b0)begin//用户退货money_flag <= 4'd7;//记下第八个状态time_flag <= 1'b1;//开始计时endelse begin//用户没操作,状态保持money_flag <= money_flag;endend4'd2:begin//用户投币为1的情况下if(flag[1] == 1'b1 && key_value[1] == 1'b0)begin//用户投币0.5money_flag <= 4'd3;//记下第四个状态endelse if(flag[2] == 1'b1 && key_value[2] == 1'b0)begin//用户投币1money_flag <= 4'd4;//记下第五个状态endelse if(flag[0] == 1'b1 && key_value[0] == 1'b0)begin//用户退货money_flag <= 4'd7;//记下第八个状态time_flag <= 1'b1;//开始计时endelse begin//用户没操作,状态保持money_flag <= money_flag;endend4'd3:begin//用户投币为1.5的情况下if(flag[1] == 1'b1 && key_value[1] == 1'b0)begin//用户投币0.5money_flag <= 4'd4;//记下第五个状态endelse if(flag[2] == 1'b1 && key_value[2] == 1'b0)begin//用户投币1money_flag <= 4'd5;//记下第六个状态time_flag <= 1'b1;//开始计时endelse if(flag[0] == 1'b1 && key_value[0] == 1'b0)begin//用户退货money_flag <= 4'd7;//记下第八个状态time_flag <= 1'b1;//开始计时endelse begin//用户没操作,状态保持money_flag <= money_flag;endend4'd4:begin//用户投币为2的情况下if(flag[1] == 1'b1 && key_value[1] == 1'b0)begin//用户投币0.5money_flag <= 4'd5;//记下第六个状态time_flag <= 1'b1;//开始计时endelse if(flag[2] == 1'b1 && key_value[2] == 1'b0)begin//用户投币1money_flag <= 4'd6;//记下第七个状态time_flag <= 1'b1;//开始计时endelse if(flag[0] == 1'b1 && key_value[0] == 1'b0)begin//用户退货money_flag <= 4'd7;//记下第八个状态time_flag <= 1'b1;//开始计时endelse begin//用户没操作,状态保持money_flag <= money_flag;endend4'd5:begin//用户投币为2.5的情况下 保持2sif(cnt_1 == MAX_NUM2)beginmoney_flag <= 4'd0;//返回第一种状态time_flag <= 1'b0;//结束计时endelse beginmoney_flag <= money_flag;endend4'd6:begin//用户投币为3的情况下 保持2sif(cnt_1 == MAX_NUM2)beginmoney_flag <= 4'd0;//返回第一种状态time_flag <= 1'b0;//结束计时endelse beginmoney_flag <= money_flag;endend4'd7:begin//用户退货的情况下 保持2sif(cnt_1 == MAX_NUM2)beginmoney_flag <= 4'd0;//返回第一种状态time_flag <= 1'b0;//结束计时endelse beginmoney_flag <= money_flag;endendendcaseendelse beginmoney_flag <= 4'd0;end
end//led控制模块
always@(posedge clk or negedge rst_n)beginif(!rst_n)led <= 4'b0000;else if(money_flag == 4'd5)begin//2.5块购买成功不找零 闪烁case(led_flag)2'b00 :led <= 4'b1111;2'b01 :led <= 4'b0000;2'b10 :led <= 4'b1111;2'b11 :led <= 4'b0000;default:led <= 4'b1111;endcaseendelse if(money_flag == 4'd6)begin//3块购买成功找零 流水灯case(led_flag)2'b00 :led <= 4'b1000;2'b01 :led <= 4'b0100;2'b10 :led <= 4'b0010;2'b11 :led <= 4'b0001;default:led <= 4'b1000;endcaseendelse if(money_flag == 4'd7)begin//退货 跑马灯(蜂鸣器)case(led_flag)2'b00 :led <= 4'b1000;2'b01 :led <= 4'b1100;2'b10 :led <= 4'b1110;2'b11 :led <= 4'b1111;default:led <= 4'b1000;endcaseendelseled <= 4'b0000;//其余情况led不亮
endassign money_flag_w = money_flag;endmodule
⑤设计顶层模块
top_vending_machine.v
module top_vending_machine(input wire clk,//时钟input wire rst_n,//复位input wire [3:0] key,//三个按键output wire beep,output wire [3:0] led,//led灯output wire [7:0] seg,//段选信号output wire [5:0] sel//位选信号
);wire [3:0] money_flag;
wire [3:0] key_value;
wire [3:0] flag;
wire boot_flag;//例化按键key1消抖模块
key_debounce inst_key_debounce1(
.clk (clk ),
.rst_n (rst_n ),
.key (key[0] ),.flag (flag[0] ),
.key_value (key_value[0])
);//例化按键key2消抖模块
key_debounce inst_key_debounce2(
.clk (clk ),
.rst_n (rst_n ),
.key (key[1] ),.flag (flag[1] ),
.key_value (key_value[1])
);//例化按键key3消抖模块
key_debounce inst_key_debounce3(
.clk (clk ),
.rst_n (rst_n ),
.key (key[2] ),.flag (flag[2] ),
.key_value (key_value[2])
);//例化按键key4消抖模块
key_debounce inst_key_debounce4(
.clk (clk ),
.rst_n (rst_n ),
.key (key[3] ),.flag (flag[3] ),
.key_value (key_value[3])
);sel_drive inst_sel_drive(
.clk (clk ),
.rst_n (rst_n ),
.boot_flag (boot_flag),.sel (sel )
);seg_scan inst_seg_scan(
.clk (clk ),
.rst_n (rst_n ),
.money_flag(money_flag),.sel (sel ),//位选信号
.seg (seg )//段选信号
);vending_machine inst_vending_machine(
.clk (clk ),//时钟
.rst_n (rst_n ),//复位
.key_value ({key_value[3],key_value[2],key_value[1],key_value[0]}),//按键值
.flag ({flag[3],flag[2],flag[1],flag[0]}),.beep (beep) ,
.led (led ),
.money_flag_w(money_flag),
.boot_flag_w(boot_flag)
);
endmodule
编译:
查看RTL门级电路:
引脚绑定:
硬件测试:
三.总结
售货机的思路和电子锁类似,主要是找出所有状态及切换条件。
【FPGA入门八】自动售货机相关推荐
- 基于FPGA状态机的自动售货机功能实现
用FPGA制作一个简单的自动售货机 这篇博客讲了如何用FPGA模拟实现自动售卖机的功能. 文章目录 用FPGA制作一个简单的自动售货机 1. 程序功能和总体框架详解 2.Divider分频模块 3.D ...
- FPGA自动售货机设计
1.总体设计 采用FPGA来设计的原理图如图1.1所示.它由控制输入电路.FPGA.显示电路电路组成. 图1.1 采用FPGA设计的自动售货机原理方框图 控制输入电路主要是为用户设计的,起到一个输 ...
- 【FPGA】自动售货机综合实现
自动售货机综合实现 一.项目需求 1. 售货机模拟项目. 二.要求 三.售货机原理 1. 基本原理 2. 思路架构 3. RTL物理模型实现 四.项目分析解决 五.总结 参考 一.项目需求 1. 售货 ...
- FPGA入门学习记录(1)----自动售货机(VM_FSM)
综述 在写一个自动售货机状态机并仿真,上板的过程中遇到了各种问题.在解决这些零碎的问题后,在防止自己忘记这些经验以及用电子介质取代纸质记录的目的下,写下此文. 这些零碎的经验主要分VeilogHDl语 ...
- FPGA学习altera 系列 第十七篇 自动售货机设计
今天给大侠带来"FPGA学习系列 altera"系列,持续更新.此学习心得是本人之前所写,所用设计软件为Quartus II 13.1,现Quartus II 新版本已更新到19+ ...
- FPGA系统性学习笔记连载_Day15【状态机、自动售货机】 【原理及verilog仿真】篇
FPGA系统性学习笔记连载_Day15[状态机.自动售货机] [原理及verilog仿真]篇 本系列为FPGA系统性学习学员学习笔记整理分享,如有学习或者购买开发板意向,可加交流群联系群主. 连载&l ...
- 奋斗的小孩系列 FPGA学习altera系列: 第十七篇 自动售货机设计
奋斗的小孩系列 FPGA学习altera系列: 第十七篇 自动售货机设计 作者:奋斗的小孩 郝旭帅(转载请注明出处) 大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术 ...
- 源码系列:基于FPGA的自动售货机设计(附源工程)
今天给大侠带来基于FPGA的自动售货机设计,附源码,获取源码,请在"FPGA技术江湖"公众号内回复" 自动售货机设计源码",可获取源码文件.话不多说,上货. 设 ...
- FPGA实现简易的自动售货机模型
文章目录 前言 一.系统设计 1.模块框图 2.状态机框图 3.RTL视图 二.源码 1.蜂鸣器驱动模块 2.按键消抖模块 3.PWM模块 4.sale_goods模块(状态机部分) 5.数码管驱动模 ...
- 连载《叁芯智能fpga设计与研发-第15天》 【状态机、自动售货机】 【原理及verilog仿真】
一.状态机基本概念 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调 相关信号动作.完成特定操作的控制中心.有限状态机简写为FSM(Finite State ...
最新文章
- 建议收藏,清华高材生准备的90条Python程序建议
- html中加入好看的行号,仅使用CSS在pre上创建行号
- 三菱服务器显示d01,MR-J3-D01基础知识三菱MR-J3-D01指导手册(通用接口) - 广州正凌...
- 实验三 类的继承和多态性
- Flume架构以及应用介绍[转]
- vue github开源项目_2018 年最好的 45 个 Vue 开源项目汇总
- PHP for 循环
- 【Go】优雅的读取http请求或响应的数据-续
- linux(centos)系统安装activemq
- 29. JavaScript - 测试 jQuery
- 2022年投影仪推荐 家用投影仪推荐一下哪款比较好2022
- Navicat Premium15 注册出现No all pattern found! file already patched?
- 数据库并发控制、悲观锁(Pessimistic Lock)、乐观锁(Optimistic Lock)、排他锁(Exclusivelocks X锁)、共享锁(Shared Lock S锁)
- 在Mac上阻止相机和麦克风的方法
- 【设计模式】七大原则之“依赖倒转原则”
- Cadence每日一学_11 | OrCAD原理图DRC检查、BOM表导出、PDF导出、网表导出
- 谷歌FLoC与禁用第三方Cookie后的江湖道术
- 解决口袋动画产生的片头动画无法使用powerpoint导出成视频的问题
- 高赞的6款办公神器,好用到飞起
- 从新生宿舍到浙江大学计算机学院,2020年浙江大学新生宿舍环境条件,大一新生男生女生宿舍内部图片【多图】...
热门文章
- TypeScript设计模式之策略、模板方法
- 银行信贷流程系统解决方案
- DFX标准: GR-2841
- 基于三维时空卷积网络的自监督点云预测(CoRL2021)
- [深入研究4G/5G/6G专题-34]: URLLC-5-《中国联通5G URLLC技术白皮书3.0版本》解读-1-业务场景
- 趣头条递交招股书:最高融资3亿美元 挖掘下沉人群市场
- Android动态部署一:Google原生Split APK浅析
- 51单片机串口通讯c语言程序,如何使用51单片机实现串口通信
- 江苏省普通话水平测试计算机评分细则,苏州语言文字网--[江苏省普通话水平测试评分细则(试行)]...
- 扩展名是WMF的文件是什么文件?