FPGA初学记录——可乐机系统搭建
野火征途Pro开发板教程——状态机拓展训练,可乐机系统搭建
文章目录
前言
最近在学习FPGA,因为研究生需要用到,所用的开发板是野火的征途Pro,也是在跟着野火的教程学习,学到了状态机,看到拓展训练,是一个比较综合的练习,可以回顾前面学的按键消抖和计数器的一些知识,还有一些基本的语法,感觉有必要记录下来,说不定以后会有用。
一、问题简述
以状态机为核心搭建一个小系统:以可乐机为背景,一瓶可乐的价格是2.5元。用按键控制投币(加入按键消抖功能),可以投0.5元硬币和1元硬币,投入0.5元后亮一个灯,投入1元后亮2个灯,投入1.5元亮3个灯,投入2元亮4个灯,如果投币后10s不再继续进行投币操作则可乐机回到初始状态。投入2.5元后可乐不找零,此时led灯实现双向流水操作,流水10s后自动停止。这里也有复位键,其功能是终止本次投币操作,使可乐机立刻回到初始状态。
二、功能解析和源代码
1.功能解析
1、首先要求是以状态机为核心,那么可能得写一个基本的状态机,分析题目可知有5个状态,分别是IDLE-HALF-ONE-ONE_HALF-TWO,具体解析可以参考野火的教程,有详细的解析,这里就不再过多赘述。
2、然后要求用按键控制投币,不同价格对应不同的灯亮,其实就是按键控制led的亮灭(加入按键消抖功能),这个好解决,不同的状态控制不同组合的led灯就行。
3、led灯组合形式有7种,初始状态-全灭、0.5元-亮1个灯、1元-亮2个灯、1.5元-亮三个灯、2元-亮四个灯,2.5元-单向流水灯,3元-双向流水灯。比较难的点主要在流水灯,单向流水灯野火教程里有详细解析,双向流水灯是流水灯那一章的拓展训练,搞懂原理分析之后可以解决。
4、流水10s后自动停止,虽然题干中说了挺多个10s后停止,但思路可以简单点,就是10s没操作就回到初始状态。只要没有投币,即按键没有按下,开始计数,计数期间只要有按键按下就计数清零,如果10s没操作,就设置一个标志位,利用这个标志位使得状态返回初始状态,灯全灭即可。因为10s有点长,为了提高调试的效率,所以改成了5s。
2.源代码——整体代码
先把整体代码奉上。
module complex_fsm_system
#(
parameter CNT_MAX = 20'd999_999, // 按键消抖计数器
parameter CNT_MAX_WATER = 25'd24_999_999, // 流水灯计数器
parameter CNT_MAX_INIT = 28'd249_999_999
)
(
input wire sys_clk, //系统时钟50MHz
input wire sys_rst_n, //全局复位
input wire pi_money_one, //投币1元
input wire pi_money_half, //投币0.5元
output wire [3:0] led_out // 输出控制led灯
);
//只有6种状态,使用独热码
parameter IDLE = 5'b00001;
parameter HALF = 5'b00010;
parameter ONE = 5'b00100;
parameter ONE_HALF = 5'b01000;
parameter TWO = 5'b10000;
/*10s归零 定义中间参数*/
reg [27:0] cnt_init;
reg cnt_init_flag;
/*流水灯 定义中间参数*/
reg [24:0] cnt ;
reg cnt_flag ;
reg cnt_flag_left;
/*按键消抖 定义中间参数*/
reg [19:0] cnt_20ms; // 按键消抖计数器
reg key_flag;//1:表示消抖后检测到按键被按下;0:表示没有检测到按键被按下
/*状态机 定义中间参数*/
reg [4:0] state;
reg [3:0] led_out_reg;
reg po_money; //1:表示找零;0:表示不找零
reg po_cola; //1:出可乐;0:不出可乐
wire [1:0] pi_money;
/**********************10s归零代码****************************/
//cnt_init:计数器计数10s
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_init <= 28'b0;
else if(pi_money_half == 1'b0 || pi_money_one == 1'b0)
cnt_init <= 28'b0;
else if(cnt_init == CNT_MAX_INIT)
cnt_init <= cnt_init;
else
cnt_init <= cnt_init + 1'b1;
//cnt_init_flag:计数器计数满10s标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_init_flag <= 1'b0;
else if(cnt_init == CNT_MAX_INIT)
cnt_init_flag <= 1'b1;
else
cnt_init_flag <= 1'b0;
/************************************************************************流水灯代码****************************************************************************/
//cnt:计数器计数500ms
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX_WATER)
cnt <= 25'b0;
else
cnt <= cnt + 1'b1;
//cnt_flag:计数器计数满500ms标志信号
//cnt_flag_left:左移的标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == CNT_MAX_WATER - 1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag_left <= 1'b0;
else if((led_out_reg == 4'b1000 || led_out_reg == 4'b1111) && cnt_flag == 1'b1)
cnt_flag_left <= 1'b1;
else if(led_out_reg == 4'b0001 && cnt_flag == 1'b1 && cnt_flag_left == 1'b1)
cnt_flag_left <= 1'b0;
/************************************************************************按键消抖代码****************************************************************************/
//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_20ms <= 20'b0;
else if(pi_money_half == 1'b1 && pi_money_one == 1'b1)
cnt_20ms <= 20'b0;
else if((cnt_20ms == CNT_MAX) && ((pi_money_half == 1'b0) || (pi_money_one == 1'b0)))
cnt_20ms <= cnt_20ms;
else
cnt_20ms <= cnt_20ms + 1'b1;
//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
key_flag <= 1'b0;
else if(cnt_20ms == CNT_MAX-1)
key_flag <= 1'b1;
e

本文记录了使用FPGA进行可乐机系统的设计,包括状态机、按键消抖、流水灯和10s归零等功能。通过野火征途Pro开发板实现,系统以状态机为核心,根据用户投入的硬币数量控制LED灯显示和出可乐。同时,介绍了状态机、按键消抖和流水灯的代码实现,以及在调试过程中遇到的问题和解决方案。
最低0.47元/天 解锁文章
2150

被折叠的 条评论
为什么被折叠?



