复杂可乐机(野火升腾拓展)

题目

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

流程图

代码

1. 顶层模块 cola_machine.v

module cola_machine

(

input wire sys_clk, // 50MHz 时钟

input wire sys_rst_n, // 全局复位,低电平有效

input wire key_half_raw, // 原始0.5元按键

input wire key_one_raw, // 原始1元按键

input wire key_rst_raw, // 原始复位按键

output reg 3:0 led, // 4个LED灯

output reg po_cola, // 出可乐信号

output reg po_money // 找零信号

);

// 消抖后的按键信号

wire key_half_sync;

wire key_one_sync;

wire key_rst_sync;

// 状态机输出信号

wire 2:0 state_code; // 状态编码,传递给LED模块

wire po_cola_w;

wire po_money_w;

// ================ 按键消抖模块实例化 ================

key_filter #(.CNT_MAX(20'd999_999)) u_filter_half (

.sys_clk(sys_clk),

.sys_rst_n(sys_rst_n),

.key_in(key_half_raw),

.key_flag(key_half_sync)

);

key_filter #(.CNT_MAX(20'd999_999)) u_filter_one (

.sys_clk(sys_clk),

.sys_rst_n(sys_rst_n),

.key_in(key_one_raw),

.key_flag(key_one_sync)

);

key_filter #(.CNT_MAX(20'd999_999)) u_filter_rst (

.sys_clk(sys_clk),

.sys_rst_n(sys_rst_n),

.key_in(key_rst_raw),

.key_flag(key_rst_sync)

);

// ================ 核心状态机模块实例化 ================

cola_fsm u_fsm (

.sys_clk(sys_clk),

.sys_rst_n(sys_rst_n),

.key_half(key_half_sync),

.key_one(key_one_sync),

.key_rst(key_rst_sync),

.state_code(state_code),

.po_cola(po_cola_w),

.po_money(po_money_w)

);

// ================ LED控制模块实例化 ================

led_controller u_led_ctrl (

.sys_clk(sys_clk),

.sys_rst_n(sys_rst_n),

.state_code(state_code),

.po_cola(po_cola_w),

.led(led)

);

// 输出赋值

assign po_cola = po_cola_w;

assign po_money = po_money_w;

endmodule

2. 核心状态机模块 mealy_cola_fsm.v

module mealy_cola_fsm

(

input wire sys_clk,

input wire sys_rst_n,

input wire key_half,

input wire key_one,

input wire key_rst,

output reg 2:0 current_state,

output wire po_cola,

output wire po_money

);

// ================ 参数定义 ================

parameter IDLE = 3'd0;

parameter HALF = 3'd1;

parameter ONE = 3'd2;

parameter ONE_HALF = 3'd3;

parameter TWO = 3'd4;

// ================ 内部信号 ================

reg 2:0 next_state;

reg 26:0 timeout_cnt;

reg timeout_10s;

// ================ 第一段状态机:时序逻辑,描述状态转移 ================

always @(posedge sys_clk or negedge sys_rst_n) begin

if (!sys_rst_n)

current_state <= IDLE;

else if (key_rst || timeout_10s)

current_state <= IDLE;

else

current_state <= next_state;

end

// ================ 超时计数器 ================

always @(posedge sys_clk or negedge sys_rst_n) begin

if (!sys_rst_n)

timeout_cnt <= 0;

else if (current_state != IDLE && !key_half && !key_one && !key_rst)

timeout_cnt <= timeout_cnt + 1'b1;

else

timeout_cnt <= 0;

end

assign timeout_10s = (timeout_cnt >= 27'd500_000_000);

// ================ 组合逻辑:计算下一状态 ================

always @(*) begin

case (current_state)

IDLE:

if (key_half)

next_state = HALF;

else if (key_one)

next_state = ONE;

else

next_state = IDLE;

HALF:

if (key_half)

next_state = ONE;

else if (key_one)

next_state = ONE_HALF;

else

next_state = HALF;

ONE:

if (key_half)

next_state = ONE_HALF;

else if (key_one)

next_state = TWO;

else

next_state = ONE;

ONE_HALF:

if (key_half)

next_state = TWO;

else if (key_one)

next_state = IDLE; // 1.5 + 1 = 2.5 → 出可乐

else

next_state = ONE_HALF;

TWO:

if (key_half)

next_state = IDLE; // 2 + 0.5 = 2.5 → 出可乐

else if (key_one)

next_state = IDLE; // 2 + 1 = 3 → 出可乐+找零

else

next_state = TWO;

default:

next_state = IDLE;

endcase

end

// ================ 第二段状态机:组合逻辑,描述 Mealy 输出 ================

// 注意:虽然是组合逻辑,但为了清晰和可维护性,仍使用 `always @(*)` 块

always @(*) begin

case (current_state)

ONE_HALF:

if (key_one) begin // 1.5 + 1 = 2.5

po_cola = 1'b1;

po_money = 1'b0;

end else begin

po_cola = 1'b0;

po_money = 1'b0;

end

TWO:

if (key_half) begin // 2 + 0.5 = 2.5

po_cola = 1'b1;

po_money = 1'b0;

end else if (key_one) begin // 2 + 1 = 3

po_cola = 1'b1;

po_money = 1'b1;

end else begin

po_cola = 1'b0;

po_money = 1'b0;

end

default:

po_cola = 1'b0;

po_money = 1'b0;

endcase

end

endmodule

3. LED控制模块 led_controller.v

module led_controller (

input wire sys_clk,

input wire sys_rst_n,

input wire 2:0 current_state,

input wire po_cola,

input wire po_money,

output reg 3:0 led

);

parameter IDLE = 3'd0;

parameter HALF = 3'd1;

parameter ONE = 3'd2;

parameter ONE_HALF = 3'd3;

parameter TWO = 3'd4;

reg 26:0 clk_div;

reg 3:0 led_reg;

reg dir;

reg in_single_flow; // 记录是否处于单向流水灯模式

reg in_double_flow; // 记录是否处于双向流水灯模式

wire led_tick = (clk_div == 27'd100_000_000 - 1);

// ================ 时钟分频计数器 ================

always @(posedge sys_clk or negedge sys_rst_n) begin

if (!sys_rst_n)

clk_div <= 0;

else

clk_div <= clk_div + 1'b1;

end

// ================ LED 基础状态 ================

always @(*) begin

case (current_state)

HALF: led_reg = 4'b0001;

ONE: led_reg = 4'b0011;

ONE_HALF: led_reg = 4'b0111;

TWO: led_reg = 4'b1111;

default: led_reg = 4'b0000;

endcase

end

// ================ 流水灯效果 ================

always @(posedge sys_clk or negedge sys_rst_n) begin

reg last_po_cola;

reg last_po_money;

if (!sys_rst_n) begin

led <= 4'b0000;

dir <= 1'b0;

in_single_flow <= 0;

in_double_flow <= 0;

end else begin

// 检测模式切换边沿

if (po_cola && !po_money && (!in_single_flow)) begin

// 刚进入单向流水灯

led <= 4'b1000; // 强制从最左侧开始

in_single_flow <= 1;

in_double_flow <= 0;

end else if (po_cola && po_money && (!in_double_flow)) begin

// 刚进入双向流水灯

led <= 4'b1000; // 强制从最左侧开始

dir <= 1'b0; // 强制向右开始

in_single_flow <= 0;

in_double_flow <= 1;

end else if (!po_cola) begin

// 退出流水灯模式

led <= led_reg;

in_single_flow <= 0;

in_double_flow <= 0;

end else if (po_cola && !po_money) begin

// 单向流水灯

if (led_tick)

led <= {led2:0, led};

end else if (po_cola && po_money) begin

// 双向流水灯

if (led_tick) begin

if (dir == 1'b0) begin // 向右

if (led == 4'b0001) begin

led <= 4'b0010;

dir <= 1'b1;

end else begin

led <= {led2:0, led};

end

end else begin // 向左

if (led == 4'b1000) begin

led <= 4'b0100;

dir <= 1'b0;

end else begin

led <= {led0, led3:1};

end

end

end

end

end

end

endmodule

4.消抖模块(野火自己取)

相关推荐
仲南音2 天前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner2 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20203 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy3 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia3 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20203 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子3 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安4 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun5 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun5 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议