FPGA—可乐机拓展训练题(状态机)

news/2024/10/22 16:19:47/

      题目:以可乐机为背景,一瓶可乐的价格还是 2.5 元。用按键控制投币(加入按键消抖功能),可以投 0.5 元硬币和 1 元硬币,投入 0.5 元后亮一个灯,投入 1 元后亮 2 个灯,投入 1.5 元后亮 3 个灯,投入 2 元后亮 4 个灯,如果投币后 10s 不再继续进行投币操作则可乐机回到初始状态。投入 2.5 元后出可乐不找零,此时 led 灯实现单向流水操作,流水10s后自动停止;投入 3 元后出可乐找零,此时 led 灯实现双向流水操作,流水 10s 后自动停止。有复位键,其功能是终止本次投币操作,使可乐机立刻回到初始状态。

   套用三要素法来分析:

输入:不投币、投入 0.5 元硬币、投入 1 元硬币;

输出:不出可乐/不找零、出可乐/不找零、出可乐/找零;

状态:可乐机中有 0 元、可乐机中有 0.5 元、可乐机中有 1 元、可乐机中有 1.5 元、可乐机中有 2 元、可乐机中有 2.5 元、可乐机中有 3 元。

1. 模块框图

2. 状态转换图

3. RTL代码

3.1 按键消抖部分

       实现按键消抖,注意有两个输入故调用两次。效果为当检查到按键被按下后,输出的key_flag被拉高一个时钟,作为标志信号输入给下一个模块。

`timescale  1ns/1nsmodule  key_filter
#(parameter CNT_MAX = 20'd999_999 
)
(input   wire    sys_clk     ,   input   wire    sys_rst_n   ,   input   wire    key_in      ,   output  reg     key_flag        );reg     [19:0]  cnt_20ms    ;   //计数器//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_20ms <= 20'b0;else    if(key_in == 1'b1)cnt_20ms <= 20'b0;else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)cnt_20ms <= cnt_20ms;elsecnt_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'b1)key_flag <= 1'b1;elsekey_flag <= 1'b0;endmodule

3.2 complex_fsm部分

        实现随着状态切换后小灯的各种显示效果。注意影响状态切换的条件除了投币还有10s有效时间。使用两类均采用时序逻辑的 always 块,第一类 always 块描述状态(state)的转移为第一段状态机,第二类 always 块描述数据的输出为第二段状态机,所以写状态机一般则采用两段式。

       总结一下套用的格式有哪些主要部分构成:第一部份端口列表部分; 第二部份状态编码部; 第三部份是定义的状态变量; 第四部份分为第一段状态机和第二段状态机。一共有四部分,写状态机代码的时候根据这种格式依次编写,非常容易的就可以实现。

`timescale  1ns/1nsmodule  complex_fsm
#(parameter   CNT_MAX  = 25'd24_999_999,       //0.5s定时器计时parameter   CNT_KEY = 20'd999_999            //20ns按键稳定状态
)
(input   wire         sys_clk         ,   input   wire         sys_rst_n       ,   input   wire         pi_money_one    ,   //投币1元input   wire         pi_money_half   ,   //投币0.5元output  reg   [3:0]  led
);//只有七种状态,使用独热码                        
parameter   IDLE     = 7'b0000001;
parameter   HALF     = 7'b0000010;
parameter   ONE      = 7'b0000100;
parameter   ONE_HALF = 7'b0001000;
parameter   TWO      = 7'b0010000;   
parameter   TWO_HALF = 7'b0100000;
parameter   THREE    = 7'b1000000;               reg     [6:0]   state  ;reg     [3:0]   led_a  ;
reg     [3:0]   led_b  ;
reg     [3:0]   led_c  ;reg     [24:0]  cnt    ;
reg     [4:0]   cnt_10s = 5'd21;reg             move   ;reg     po_money     ;                     
reg     po_cola      ;wire    [1:0]   pi_money;
wire            po_money_half;
wire            po_money_one ;wire            cola_flag;                 //pi_money:为了减少变量的个数,用 位拼接 把输入的两个1bit信号拼接成1个2bit信号
//投币方式可以为:不投币(00)、投0.5元(01)、投1元(10),每次只投一个币
assign pi_money = {po_money_one, po_money_half};//满足出可乐条件的所有情况
assign  cola_flag = (state == TWO && pi_money == 2'b01) || (state == TWO && pi_money == 2'b10) || (state == ONE_HALF && pi_money == 2'b10);//第一段状态机,当前状态state如何根据输入跳转到下一状态
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)state <= IDLE;  else    case(state)IDLE    : if(pi_money == 2'b01)   //有几个输入就有几种跳转情况state <= HALF;else    if(pi_money == 2'b10)state <= ONE;elsestate <= IDLE;HALF    : if(pi_money == 2'b01)state <= ONE;else    if(pi_money == 2'b10)state <= ONE_HALF;else  if(cnt_10s == 5'd20)state <= IDLE;ONE     : if(pi_money == 2'b01)state <= ONE_HALF;else    if(pi_money == 2'b10)state <= TWO;else  if(cnt_10s == 5'd20)state <= IDLE;ONE_HALF: if(pi_money == 2'b01)state <= TWO;else    if(pi_money == 2'b10)state <= TWO_HALF;else  if(cnt_10s == 5'd20)state <= IDLE;TWO     : if(pi_money == 2'b01)state <= TWO_HALF;else   if(pi_money == 2'b10)state <= THREE;else  if(cnt_10s == 5'd20)state <= IDLE;TWO_HALF: if(cnt_10s == 5'd20)    //10秒没有投币     state <= IDLE;else if(pi_money == 2'b01)  //继续投币 0.5元state <= HALF;else if(pi_money == 2'b10)  //继续投币 1元state <= ONE;THREE   : if(cnt_10s == 5'd20)     //10秒没有投币 state <= IDLE;else if(pi_money == 2'b01)  //继续投币 0.5元state <= HALF;else if(pi_money == 2'b10)  //继续投币 1元state <= ONE;default :     state <= IDLE;endcase//第二段状态机,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)cnt <= 25'b0;else if(cnt_10s != 5'd21)     //开始计时cnt <= cnt + 1'b1;
//第二段状态机,10s计数器
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_10s <= 5'd21;else    if((pi_money != 2'd0)  //开始计时cnt_10s <= 5'd0;else    if(cnt == CNT_MAX)cnt_10s <= cnt_10s + 1'b1;else    if(cnt_10s == 5'd20)cnt_10s <= 5'd0 ;
//第二段状态机,led_a:投币控制led状态
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)led_a <= 4'b1111;else    if(state == TWO)led_a <= 4'b0000;else    if(state == ONE_HALF)led_a <= 4'b0001;else    if(state == ONE)led_a <= 4'b0011;else    if(state == HALF)led_a <= 4'b0111;else    if(state == IDLE)led_a <= 4'b1111;	
//第二段状态机,led_b:led 单向循环流水
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)led_b <= 4'b0001;else if(led_b == 4'b1000 && cnt == CNT_MAX)led_b <= 4'b0001;else if(cnt == CNT_MAX)led_b <= led_b << 1'b1; //左移  
//第二段状态机,led_c:led双向循环流水
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)led_c <=  4'b0001;else    if(move == 1'b0 && cnt == CNT_MAX)led_c <= led_c  >>1'b1; //右移else    if(move == 1'b1 && cnt == CNT_MAX)led_c <= led_c  <<1'b1; //左移elseled_c <= led_c ;
//led循环流水使能信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)move <= 1'b0;else    if(led_c == 4'b1000)move <= 1'b0;else    if(led_c == 4'b0001)move <= 1'b1;//根据状态机控制led状态
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)led <= 4'b1111;else    if((state == THREE)&&(cnt_10s != 5'd20))led <= led_c;else    if((state == TWO_HALF)&&(cnt_10s != 5'd20))led <= led_b;else   led <=~led_a;//第二段状态机,描述当前状态state和输入pi_money如何影响po_cola输出
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)po_cola <= 1'b0;else    if(cola_flag == 1'b1)po_cola <= 1'b1;elsepo_cola <= 1'b0;
//第二段状态机,描述当前状态state和输入pi_money如何影响po_money输出
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n ==	1'b0)po_money <= 1'b0;else if((state == TWO) && (pi_money == 2'b10))po_money <= 1'b1;elsepo_money <= 1'b0;//分别调用两个按键模块
key_filter
#(.CNT_MAX(CNT_KEY)
)
key_filter_inst1
(.sys_clk   (sys_clk         ),.sys_rst_n (sys_rst_n       ),.key_in    (pi_money_half   ),.key_flag  (po_money_half   )   
);
key_filter
#(.CNT_MAX(CNT_KEY)
)
key_filter_inst2
(.sys_clk   (sys_clk     ),.sys_rst_n (sys_rst_n   ),.key_in    (pi_money_one),.key_flag  (po_money_one)         );
endmodule

5. 总结

1. 编写状态机的步骤,格式。

2. 如何绘制状态转换图,抓住“三要素”。

3. 处理LED输出按显示效果分类后输出。


http://www.ppmy.cn/news/79068.html

相关文章

美国国际留学生超136万,本科、硕士和博士各占多少?

国际教育市场研究机构ICEF Monitor近日公布&#xff0c;2022年美国持有F-1和M-1有效学习签证的国际学生数量共计136.2万人&#xff0c;与2021年相比增长了10.1%。 其中&#xff0c;国际学生来美国读本科学士学位的占37%&#xff0c;读硕士学位的占41%&#xff0c;读博士学位的…

由浅入深Dubbo核心源码剖析环境介绍

目录 1 框架介绍1.1 概述1.2 运行架构1.3 整体设计 2 环境搭建2.1 源码拉取2.2 源码结构2.3 环境导入2.4 测试2.5 管理控制台 1 框架介绍 1.1 概述 Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架&#xff0c;使得应用可通过高性能的 RPC 实现服务的输出和输入功能&#…

数字图像处理-绪论

数字图像处理-绪论 文章目录 前言一、闲谈二、什么是数字图像处理&#xff1f;2.1. 什么是数字图像&#xff1f;2.1.1. 可见光图像2.1.2. 不可见光图像 2.2. 什么是数字图像处理&#xff1f; 三、数字图像处理的前世今生3.1. 数字图像处理的前世3.2. 数字图像处理的今生 四、数…

哪个产品功能重要?KANO模型帮你

哪个产品功能重要&#xff1f;KANO模型来帮你 模型工具可以协助思考和系统化改进 KANO模型是小日本一个教授提出 趣讲大白话&#xff1a;往往&#xff0c;怎么思考&#xff0c;比思考什么重要 【趣讲信息科技175期】 **************************** 东京理工大学教授狩野纪昭(No…

Netty重试一定次数后调用System.exit(n)退出应用程序(二)

System.exit()方法 原型&#xff1a;System.exit(int status) 其功能主要是调用Runtime.getRuntime().exit(status); 作用是终止当前正在运行的Java虚拟机&#xff0c;这个status表示退出的状态码&#xff0c;非零表示异常终止。(可以返回给其他进程的调用者一个调用的返回码…

shiro环境搭建

源码部署 这种方法相对复杂&#xff0c;如果不需要分析源码直接用docker就行 前置条件&#xff1a;Maven Ideal Tomcat 下载方式1&#xff1a;https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4&#xff0c;然后将文件夹导入ideal下载方式2&#xff1a;将shiro…

Java中链表的实现(超详细)

在Java中&#xff0c;链表可以通过创建节点和链接节点来实现。以下是一个简单的链表实现示例&#xff1a; public class LinkedList {Node head; // 头结点// 节点类class Node {int data;Node next;Node(int d) {data d;next null;}}// 在链表头部插入节点public void push…

Linux - 第17节 - 网络基础(应用层三)

1.HTTPS协议 1.1.HTTPS简介 HTTPS也是⼀个应⽤层协议&#xff0c;是在HTTP协议的基础上引⼊了⼀个加密层。 HTTP协议内容都是按照文本的方式明文传输的&#xff0c;明文传输是不安全的&#xff0c;所以现在主流的解决 方式是在http所在的应用层和tcp所在的传输层之间加一层SSL…