☰
FPGA地铁售票系统:状态机设计与硬件实操指南
2026/10/26 17:32:41 网站建设 项目流程

简介:本资源是一套完整的FPGA课程设计项目——地铁售票模拟系统,面向计算机、电子信息、自动化等专业的本科生及初学者,解决数字电路与嵌入式系统课程设计中从理论到实践落地的典型需求。压缩包共18个文件,涵盖Verilog源码(.v)、Vivado工程文件(.xpr、.xdc、.lpr)、仿真与测试脚本(.py、.xlsx)、设计说明文档(.pdf、.md)及开源协议(LICENSE),总大小994KB,结构清晰、模块完整,便于理解FPGA开发全流程。已有147人学习下载,项目已通过导师评审并获95分高分,所有代码在Windows/macOS/Linux多平台实测运行无误。读者可直接用于课程设计提交,亦可基于metro.v核心逻辑拓展功能,如增加线路选择、票价计算或LCD显示模块;配套PDF文档含设计思路与实现细节,README.md与Verilog_Notes.md提供开发指引与语法要点,降低入门门槛。

1. 为什么用 FPGA 做地铁售票模拟系统,不是用树莓派或 STM32?

这不是一个“用 FPGA 跑个 LED 流水灯”的教学玩具——它是一套可验证状态机行为、支持多路并发响应、时序严格可控的实时交互系统。我带过三届数字电路课程设计,90% 的学生第一反应是:“用 Arduino 或 Python 写个 GUI 不就完事了?”但真正跑起来就会发现:当按下“投币”键后,系统要同时完成硬币识别(脉冲计数)、票价比对(状态跳转)、出票逻辑(使能驱动)、找零控制(减法器+蜂鸣提示),且任意两个操作不能冲突(比如投币未确认时又按“确认购票”,必须拒绝)。这些并行性、确定性、无软件中断抖动的需求,恰恰是 FPGA 的强项。这个.zip包里没有一行 Python 或 C++ 应用层代码,全是 Verilog HDL 描述的模块化结构:coin_detector、ticket_fsm、dispenser_ctrl、led_display_driver,外加完整的 Quartus II 工程文件、引脚约束.qsf、仿真测试激励.v和 PDF 版设计说明书。适合数字逻辑课设、FPGA 入门实战、嵌入式硬件方向毕设打样——尤其适合想摆脱“仿真能跑、上板就崩”魔咒的同学。


2. 从 ZIP 解压到 Quartus 编译成功:最小闭环实操路径

2.1 解压后目录结构与关键文件定位

解压课程设计 基于FPGA地铁售票模拟系统源码+全部资料齐全.zip后,你会看到标准 FPGA 项目分层结构:

├── doc/ ← 设计说明书(含状态图、接口定义、测试用例) ├── fpga/ ← 核心 RTL 源码(Verilog) │ ├── top_ticket_system.v ← 顶层模块(例化所有子模块) │ ├── coin_detector.v ← 硬币脉冲检测(防抖+边沿计数) │ ├── ticket_fsm.v ← 主状态机(IDLE → COIN_OK → SELECT → CONFIRM → DISPENSE) │ ├── led_display_driver.v ← 动态扫描 4 位数码管(BCD→段码+位选) │ └── dispenser_ctrl.v ← 出票/找零驱动(输出 3-bit 票种选择 + 4-bit 找零金额) ├── simulation/ ← ModelSim 仿真环境 │ ├── tb_top_ticket_system.v ← 测试平台(含完整按键序列激励) │ └── wave.do ← 波形脚本(自动加载信号组) ├── quartus/ ← Quartus II 工程文件 │ ├── ticket_system.qpf ← 工程文件 │ ├── ticket_system.qsf ← 引脚约束(关键!映射到开发板物理 IO) │ └── output_files/ ← 编译后生成的 .sof 文件存放处 └── hardware/ ← 开发板适配说明(明确标注:本设计适配 DE1-SoC / EP4CE10F17C8)

注意:quartus/ticket_system.qsf是成败关键。它不是自动生成的默认约束,而是人工绑定的——例如set_location_assignment PIN_R1 -to clk_50mhz表示 50MHz 主时钟接在 R1 引脚;set_location_assignment PIN_T11 -to key[0]表示 KEY0 按键对应 T11 引脚。如果你用的是其他型号开发板(如 Nexys4 DDR),必须重写此文件,否则编译会通过但上板无响应。

2.2 在 Quartus II 18.0 中完成编译与下载(以 DE1-SoC 为例)

本设计基于 Intel Cyclone IV E(EP4CE10F17C8)芯片,推荐使用 Quartus II 18.0 Standard Edition(兼容 Windows 10/11)。步骤如下:

# 1. 启动 Quartus II → File → Open Project → 选择 quartus/ticket_system.qpf # 2. 确认 Tools → Options → FPGA → Device → Family = Cyclone IV E,Device = EP4CE10F17C8 # 3. 编译前检查:Assignments → Pin Planner → 查看 key[0]~key[3]、sw[0]~sw[3]、ledr[0]~ledr[7] 是否全部有 Location Assignment # 4. 执行全编译:Processing → Start Compilation(约 3~5 分钟,取决于 CPU) # 5. 编译成功后:Tools → Programmer → Hardware Setup → 选择 USB-Blaster(II)→ Add File → 选择 output_files/ticket_system.sof # 6. 勾选 "Program/Configure" → Start

编译成功标志:
✅Fitter阶段显示Logic utilization: 32% (3212/10000)(资源余量充足)
✅Assembler阶段生成.sof文件大小 ≈ 1.2MB
❌ 若报错Can't place input pin xxx on location yyy,说明引脚约束与实际开发板不匹配,需修改.qsf

2.3 关键模块功能验证:用 ModelSim 快速抓 Bug

不要等烧录到板子才发现逻辑错误。先用simulation/tb_top_ticket_system.v做功能仿真:

// simulation/tb_top_ticket_system.v 片段(已预置典型测试序列) initial begin clk = 0; rst_n = 0; #100 rst_n = 1; // 复位释放 // 模拟用户操作:投币(key[0] 下降沿)→ 选票(sw[0]=1)→ 确认(key[1] 下降沿) #1000 key[0] = 0; #10 key[0] = 1; // 投入 1 枚硬币(脉冲) #500 sw[0] = 1; // 选择 2 元票 #500 key[1] = 0; #10 key[1] = 1; // 确认购票 #2000 $stop; // 自动停止仿真 end

在 ModelSim 中执行:

vsim -t 1ps work.tb_top_ticket_system do wave.do run -all

观察波形重点:

  • state信号是否按IDLE → COIN_OK → SELECT → CONFIRM → DISPENSE → IDLE循环跳变
  • dispense_en是否在DISPENSE状态拉高 1 个时钟周期
  • led_seg输出是否随display_data正确变化(如投入 5 元,选 2 元票,应显示0003表示找零 3 元)

逻辑说明:ticket_fsm.v使用独热码编码(parameter IDLE=4'b0001, COIN_OK=4'b0010, ...),相比二进制编码更易调试、抗毛刺——这也是本设计在fpga case用独热码和不用独热码区别这一高频搜索词中被反复提及的原因。独热码下,状态跳变时仅一位翻转,避免中间态误触发;代价是多用 2 个寄存器,但 EP4CE10 完全富余。


3. 上板运行必调的 3 个参数:时钟、按键消抖、数码管刷新率

3.1 主时钟频率必须严格匹配开发板晶振

本设计默认使用clk_50mhz作为系统主时钟(来自 DE1-SoC 板载 50MHz 晶振)。若你用的是其他开发板(如 Basys3 的 100MHz 晶振),不能直接改.qsf里的引脚,而必须重写分频逻辑:

// fpga/top_ticket_system.v 中关键分频段(不可删!) reg [25:0] clk_cnt; always @(posedge clk_50mhz or negedge rst_n) begin if (!rst_n) clk_cnt <= 0; else clk_cnt <= clk_cnt + 1; end wire clk_1khz = clk_cnt[25]; // 50MHz → 1kHz(用于数码管动态扫描) wire clk_100hz = clk_cnt[20]; // 50MHz → 100Hz(用于按键消抖采样)

⚠️ 如果你的板子是 100MHz 晶振,需将clk_cnt[25]改为clk_cnt[26](因为 2^26 = 67,108,864 ≈ 100MHz / 1kHz),否则数码管会严重闪烁或熄灭。

3.2 按键消抖:硬件级 vs 软件级,这里选前者

coin_detector.v和ticket_fsm.v中均采用10ms 硬件消抖(非简单延时):

// fpga/coin_detector.v 片段 reg [13:0] key_debounce_cnt; // 10ms @ 50MHz = 500,000 个周期 → 14-bit 计数器 always @(posedge clk_50mhz or negedge rst_n) begin if (!rst_n) key_debounce_cnt <= 0; else if (key_raw == 0) key_debounce_cnt <= key_debounce_cnt + 1; else key_debounce_cnt <= 0; end assign key_valid = (key_debounce_cnt == 14'd499999); // 计满即认为稳定低电平

参数说明:14'd499999是精确值(50MHz × 0.01s = 500,000),不是近似值。若误写成500000,会导致消抖时间偏长 20ns,虽不影响功能,但在高频按键连按时可能漏触发——这是我在第 2 届课设中帮学生 debug 时发现的血泪经验。

3.3 数码管动态扫描:刷新率低于 50Hz 会肉眼可见闪烁

led_display_driver.v控制 4 位共阴数码管,核心是scan_sel位选信号轮询:

reg [1:0] scan_cnt; always @(posedge clk_1khz or negedge rst_n) begin if (!rst_n) scan_cnt <= 0; else scan_cnt <= scan_cnt + 1; end always @(*) begin case (scan_cnt) 2'b00: {scan_sel, seg_data} = {4'b1110, display_data[15:12]}; 2'b01: {scan_sel, seg_data} = {4'b1101, display_data[11:8]}; 2'b10: {scan_sel, seg_data} = {4'b1011, display_data[7:4]}; 2'b11: {scan_sel, seg_data} = {4'b0111, display_data[3:0]}; endcase end

clk_1khz驱动scan_cnt,每 1ms 切换一位,整周期 4ms → 刷新率 250Hz。若误用clk_100hz(10ms 周期),刷新率将跌至 25Hz,人眼明显感知闪烁。务必确认scan_cnt的驱动时钟来源是clk_1khz,而非clk_100hz。


4. 避坑:上板后“没反应”“乱码”“按键失灵”的 4 类真实翻车现场

4.1 现象:下载.sof后所有 LED 熄灭,按键无任何响应

原因:.qsf中rst_n引脚未约束,或约束到高电平有效引脚但实际开发板是低电平复位。DE1-SoC 的KEY按键是低电平有效,但部分国产板(如紫光同创小蜜蜂)KEY是高电平有效。
解决:打开quartus/ticket_system.qsf,查找rst_n行,确认其Location对应开发板复位按键引脚,并检查I/O Standard是否为3.3-V LVTTL;若复位信号始终为高,手动在顶层模块中添加assign rst_n = ~key[3];(假设 KEY3 作复位键)。

4.2 现象:数码管显示乱码(如8888、EEEE、随机跳变)

原因:led_display_driver.v中seg_data未做 BCD 校验,输入非法值(如display_data > 9999)导致段码查表越界。本设计未内置校验,依赖上游模块保证display_data为 0~9999。
解决:在top_ticket_system.v中加入钳位逻辑:

assign display_data_safe = (display_data > 9999) ? 0 : display_data; // 替换原 display_data 驱动 led_display_driver 的信号

4.3 现象:投币后状态卡在COIN_OK,无法进入SELECT

原因:ticket_fsm.v中COIN_OK状态的跳转条件为sw[0:3] != 0,但开发板拨码开关sw[0:3]默认上拉为高电平(即1111),导致一上电就满足条件,状态机立即跳走。
解决:修改ticket_fsm.v第 87 行(原文):

// 原始错误写法(未考虑上拉) if (sw[0] || sw[1] || sw[2] || sw[3]) next_state <= SELECT; // 正确写法(显式读取拨码值,忽略上拉干扰) if (sw != 4'b0000) next_state <= SELECT;

4.4 现象:找零金额显示正确,但dispense_en无脉冲输出

原因:dispenser_ctrl.v中dispense_en生成逻辑依赖state == DISPENSE,但该状态持续时间仅为 1 个clk_100hz周期(10ms),而继电器或步进电机驱动需要 ≥100ms 有效电平。
解决:在dispenser_ctrl.v中扩展脉宽:

reg [6:0] dispense_pulse_cnt; // 100ms @ 100Hz = 10 个周期 → 4-bit 够用,此处用 7-bit 预留 always @(posedge clk_100hz or negedge rst_n) begin if (!rst_n) dispense_pulse_cnt <= 0; else if (state == DISPENSE && dispense_pulse_cnt == 0) dispense_pulse_cnt <= 10; else if (dispense_pulse_cnt > 0) dispense_pulse_cnt <= dispense_pulse_cnt - 1; end assign dispense_en = (dispense_pulse_cnt > 0);

5. 进阶技巧:把“模拟系统”变成“可测系统”——添加 JTAG UART 调试通道

仅仅靠 LED 和数码管观察状态太原始。本设计预留了jtag_uart接口(在top_ticket_system.v中已例化但未连接),只需 3 步即可启用串口打印:

5.1 修改顶层模块,接入 UART 发送逻辑

// 在 top_ticket_system.v 末尾添加 jtag_uart jtag_inst ( .clk(clk_50mhz), .reset_n(rst_n), .tx(usb_uart_tx), // 引出到 USB-Blaster 的 UART TX 引脚 .rx(usb_uart_rx), // 引出到 USB-Blaster 的 UART RX 引脚 .write(write_en), .data_in({state, display_data}) // 打印当前状态 + 显示值 ); // 并在 pin planner 中绑定 usb_uart_tx/usb_uart_rx 到 DE1-SoC 的 HPS_UART_TX/RX 引脚(PIN_AK14 / PIN_AL15)

5.2 编译后用 Teraterm 或 Putty 监听(波特率 115200)

连接开发板 USB Blaster,打开串口工具,设置:

  • 波特率:115200
  • 数据位:8
  • 停止位:1
  • 无校验
  • 流控:None

上电后立即看到输出:

[STATE: IDLE] DISP: 0000 [STATE: COIN_OK] DISP: 0005 [STATE: SELECT] DISP: 0005 [STATE: CONFIRM] DISP: 0005 [STATE: DISPENSE] DISP: 0003 [STATE: IDLE] DISP: 0000

参数说明:jtag_uart是 Quartus 自带 IP,无需额外 license。它利用 JTAG 链路模拟 UART,不占用 FPGA 逻辑资源,只消耗少量布线资源。比用外部 CH340 芯片更轻量,且无需焊接——这才是 FPGA 项目调试的“后悔药”。

5.3 用状态机覆盖率报告验证设计完备性

Quartus 自带 FSM Viewer,可导出状态覆盖报告:

  1. 编译完成后,点击Tools → Tcl Scripts → Run Script
  2. 加载quartus/common/tcl/fsm_report.tcl
  3. 输出fsm_coverage.rpt,查看IDLE,COIN_OK,SELECT,CONFIRM,DISPENSE是否全部被激活,以及各状态转移弧是否 100% 覆盖。
    若某条转移弧未覆盖(如DISPENSE → IDLE缺失),说明dispenser_ctrl.v中缺少状态退出逻辑,需补next_state <= IDLE。

我习惯在每次迭代后跑一次覆盖率,确保学生交的课设不是“碰巧能跑”,而是“每个分支都验证过”。这比写 10 页 Word 报告更有说服力。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询