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

题目

我们仍以可乐机为背景,一瓶可乐的价格还是 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 <= {led[2: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 <= {led[2:0], led};

end

end else begin // 向左

if (led == 4'b1000) begin

led <= 4'b0100;

dir <= 1'b0;

end else begin

led <= {led[0], led[3:1]};

end

end

end

end

end

end

endmodule

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

相关推荐
HAPPY酷2 小时前
DDR 压测与系统验证知识全集
arm开发·驱动开发·fpga开发·硬件架构·硬件工程·dsp开发·基带工程
Aaron15882 小时前
基于FPGA实现卷积方法比较分析
arm开发·算法·fpga开发·硬件架构·硬件工程·射频工程·基带工程
Terasic友晶科技3 小时前
DE10-Nano的HDMI方块移动案例——显示器时序(DMT)标准介绍
fpga开发·计算机外设·hdmi·显示器时序·dmt
search74 小时前
芯片-IP集成
fpga开发
9527华安4 小时前
紫光同创FPGA实现 TCP/IP 协议栈,千兆网服务器版本,提供5套工程源码和技术支持
服务器·tcp/ip·fpga开发
Terasic友晶科技4 小时前
【答疑解惑】如何临时解决带Nios II的FPGA设计在测试时遇到time_limited文件导致elf下载不了的问题
fpga开发·nios ii·quartus lite·opencore plus·time_limited
FPGA_小田老师5 小时前
AXI_DMA IP核实战:24路并行数据高速存储方案
fpga开发·axi_dma·adc采样并行数据存储·高速并行数据存储
崇子嵘5 小时前
为什么需要“输出锁存”
fpga开发
szxinmai主板定制专家20 小时前
柔宇柔性显示屏+x86、arm显示解决方案,还有库存
arm开发·人工智能·fpga开发