☰
FPGA循迹小车实战:Verilog实现与Quartus II避坑指南
2026/10/30 3:02:57 网站建设 项目流程

简介:这份资源面向电子设计入门者与FPGA学习者,提供一套基于Verilog与Quartus II的循迹小车完整工程,用于解决红外传感器数据解析、行驶方向决策与电机控制等硬件逻辑设计问题。压缩包共137个文件,约4.57MB,以Quartus II工程文件为主,包括qpf、qsf工程配置,v源文件与vt、vmake仿真相关文件,sof、pof比特流,以及大量cdb、hdb、rdb等综合与布局布线中间数据库和rpt报告,另含readme说明与testbench验证代码,覆盖设计输入、综合、仿真到下载的完整流程。已有3233人学习下载,适合课程设计、竞赛小车与自学FPGA的读者参考。工程可直接在Quartus II中打开编译,借助testbench观察传感器输入与电机输出波形,理解循迹状态机与PWM调速逻辑,并对照报告排查时序与引脚分配问题,是少见的软硬件结合实战案例。

1. 从“传感器一抖就冲出赛道”说起:FPGA 循迹小车到底要解决什么

做过循迹小车的人大多有过这样的经历:红外传感器在赛道边缘抖了一下,单片机轮询没跟上,车头一偏就压线冲出赛道。用 MCU 做循迹,本质上是“采样—判断—输出”串行执行,一旦中断被占用或者程序跑飞,实时性就打折扣。而 FPGA 用纯硬件并行处理,多路传感器可以同一时钟沿采样、同一时钟沿输出 PWM,响应延迟稳定在几个时钟周期内,这正是循迹场景最看重的确定性。这套方案面向两类人:一是要做 FPGA 课程设计、数字系统综合实验的学生,二是想用 Verilog 把“感知—决策—执行”整条链路走通、交一份完整方案的从业者。核心关键词就是 verilog、quartus ii、循迹小车、fpga——四个词对应四件事:用什么写、在哪综合、做什么、跑在什么平台上。下面按需求、架构、模块实现、避坑、验收的顺序,把这份方案拆到能照着搭的程度。

2. 需求与选型:为什么用 FPGA 而不是单片机,器件和工具链怎么定

2.1 先把功能需求钉死,别一上来就写代码

循迹小车的需求看起来简单,但真正落到 FPGA 上,必须先把指标量化,否则后面模块划分全是拍脑袋。我一般会先列一张需求表,把“能跑”翻译成“可测量”:

需求项量化指标对应设计点
循迹精度能识别 3~5 路红外传感器的黑白状态传感器采样与消抖模块
响应实时性从传感器变化到 PWM 占空比更新 ≤ 10 ms并行采样 + 组合决策
电机控制双路直流电机,PWM 频率 1~20 kHz 可调PWM 生成模块
速度调节至少 3 档基础速度分频参数化
调试可视板载 LED 指示传感器状态与运行模式状态指示模块
复位与安全上电复位、急停按键复位同步模块

这张表的价值在于:它直接决定了后面要写几个模块、每个模块的输入输出是什么。很多同学翻车的起点,就是需求没定就开写,写到一半发现“要不要加消抖”“PWM 频率定多少”全得回头改。

2.2 FPGA 与 MCU 的取舍:不是谁更强,而是谁更合适

循迹小车用 MCU 完全能做,而且成本更低。选 FPGA 的理由通常有三条:第一,多路传感器并行采样,天然适合硬件描述语言;第二,PWM、编码器计数、消抖这些逻辑可以同时跑,互不抢占;第三,课程设计本身要求体现数字系统设计能力,FPGA 能把“组合逻辑 + 时序逻辑 + 状态机”完整展示出来。反过来说,如果只是做一个能跑就行的小车,MCU 更省事。所以这套方案的定位很明确:它是一份数字系统设计训练,循迹只是载体。想清楚这一点,后面就不会纠结“为什么不用现成库”。

2.3 器件与工具链:Quartus II 下的最小可行配置

工具链锁定 Quartus II,语言用 Verilog-2001,器件选型按实验室常见的中低端 FPGA 开发板来定,通常需要满足:至少 30 个可用 I/O(5 路传感器 + 2 路 PWM + 2 路方向 + LED + 按键 + 时钟),有 50 MHz 板载晶振,支持 3.3 V I/O 电平。工程建立时几个关键设置别漏:

# Quartus II 工程关键设置(在 Assignment Editor 或 .qsf 中体现) # 1. 顶层实体与器件族 set_global_assignment -name TOP_LEVEL_ENTITY top_tracking_car set_global_assignment -name FAMILY "Cyclone IV E" # 2. 时钟约束:板载 50 MHz 晶振 create_clock -name clk -period 20.000 [get_ports clk] # 3. 未使用引脚设为三态输入,避免误驱动 set_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED" # 4. 配置方式按开发板实际选择(JTAG 调试 / AS 固化)

这段约束的含义:create_clock周期 20 ns 对应 50 MHz,是后续所有分频和计数的基准;未用引脚设三态输入是血泪经验,早期没设,烧录后个别引脚电平冲突,现象是“程序没错但行为诡异”,查了半天才发现是引脚分配问题。器件族和配置方式按实际开发板改,不要照抄。

提示:Quartus II 版本与器件支持是绑定的,选器件前先确认手头版本是否支持该器件族,否则综合阶段直接报错,白白浪费时间。

3. 总体架构设计:四层数据流与六个模块的职责划分

3.1 从传感器到电机的数据通路

整套系统按数据流分成四层:感知层(红外传感器输入)、决策层(循迹判断与速度决策)、执行层(PWM 与电机方向)、辅助层(复位、时钟、指示)。六个模块的职责划分如下:

  • sensor_sync:传感器输入同步与消抖,把异步的 GPIO 电平变成干净的内部信号;
  • track_decision:根据多路传感器组合判断当前偏差方向,输出转向指令;
  • speed_ctrl:把转向指令映射成左右轮的目标占空比,支持档位调速;
  • pwm_gen:参数化 PWM 发生器,双路独立占空比;
  • motor_drive:把 PWM 与方向信号组合成电机驱动接口;
  • top_tracking_car:顶层例化与连线,不含逻辑。

这种划分的好处是每个模块都能单独仿真、单独验收。答辩时老师问“你这个消抖怎么验证的”,你可以直接拿出sensor_sync的测试波形,而不是含糊说“整体跑通了”。

3.2 时钟域与复位策略

系统只有一个时钟域:50 MHz 板载晶振经clk_div分出 1 kHz 的采样节拍和 PWM 计数节拍。单时钟域是刻意为之——多时钟域会引入跨时钟同步问题,对循迹这种毫秒级响应的场景毫无必要,反而增加翻车概率。复位采用异步复位、同步释放的经典结构:

// 异步复位同步释放,避免复位释放时的亚稳态 module reset_sync ( input wire clk, input wire rst_async_n, output wire rst_sync_n ); reg rst_meta, rst_sync; always @(posedge clk or negedge rst_async_n) begin if (!rst_async_n) begin rst_meta <= 1'b0; rst_sync <= 1'b0; end else begin rst_meta <= 1'b1; // 第一级打拍,释放沿同步到 clk rst_sync <= rst_meta; end end assign rst_sync_n = rst_sync; endmodule

两级触发器的作用是把复位释放沿对齐到时钟,避免各模块复位释放时刻不一致导致的状态机跑飞。参数上没什么可调的,rst_async_n接按键或上电复位电路即可。这个模块虽小,但它是后面所有时序逻辑稳定工作的前提,别省。

3.3 参数化设计:让一套代码适配不同赛道

赛道宽度、传感器数量、电机特性在不同实验条件下会变,硬编码是自找麻烦。我一般把关键参数提到顶层用parameter定义:

module top_tracking_car #( parameter CLK_FREQ = 50_000_000, // 板载时钟频率 parameter PWM_FREQ = 10_000, // PWM 频率 10 kHz parameter SAMPLE_DIV = 50_000, // 1 kHz 采样节拍分频 parameter DEBOUNCE_N = 8 // 消抖连续采样次数 )( input wire clk, input wire rst_n, input wire [4:0] sensor_in, // 5 路红外,1 为黑线 input wire [1:0] speed_sel, // 速度档位 output wire pwm_l, pwm_r, output wire dir_l, dir_r );

PWM_FREQ决定电机驱动是否啸叫,10 kHz 以上人耳基本听不到;SAMPLE_DIV决定循迹响应节拍,1 kHz 对小车足够;DEBOUNCE_N是消抖强度,赛道反光强时适当加大。把这些参数集中管理,换赛道只改顶层,不动子模块。

4. 核心模块实现:消抖、决策、PWM 三段关键逻辑

4.1 传感器同步与消抖:把抖动挡在决策之前

红外传感器输出是异步信号,直接进决策逻辑会引入亚稳态和毛刺。标准做法是“两级同步 + 计数消抖”:

module sensor_sync #( parameter DEBOUNCE_N = 8 )( input wire clk, input wire rst_n, input wire [4:0] sensor_raw, output reg [4:0] sensor_clean ); reg [4:0] sync0, sync1; reg [3:0] cnt [0:4]; // 每路独立计数器 // 两级同步,消除亚稳态 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin sync0 <= 5'b0; sync1 <= 5'b0; end else begin sync0 <= sensor_raw; sync1 <= sync0; end end // 连续 DEBOUNCE_N 拍一致才更新输出 integer i; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin sensor_clean <= 5'b0; for (i = 0; i < 5; i = i + 1) cnt[i] <= 4'd0; end else begin for (i = 0; i < 5; i = i + 1) begin if (sync1[i] != sensor_clean[i]) begin if (cnt[i] == DEBOUNCE_N - 1) begin sensor_clean[i] <= sync1[i]; cnt[i] <= 4'd0; end else begin cnt[i] <= cnt[i] + 1'b1; end end else begin cnt[i] <= 4'd0; end end end end endmodule

逻辑说明:sync0/sync1两级触发器把异步输入对齐到clk;每路独立计数器在“输入与当前输出不一致”时累加,连续DEBOUNCE_N拍都一致才翻转输出,中途恢复一致则清零。参数DEBOUNCE_N越大越稳但响应越慢,8 拍在 1 kHz 节拍下约 8 ms,是精度和响应的折中。注意计数器位宽要能覆盖DEBOUNCE_N,这里 4 位最大 15,够用。

4.2 循迹决策:用组合逻辑表达“偏差方向”

5 路传感器从左到右排列,黑线压在哪几路上,就代表车相对赛道偏了多少。决策逻辑本质是一张真值表,用case表达最直观:

module track_decision ( input wire [4:0] sensor, // 1 表示检测到黑线 output reg [1:0] turn // 00 直行 01 左转 10 右转 11 丢线 ); always @(*) begin case (sensor) 5'b00100: turn = 2'b00; // 正中,直行 5'b01100, 5'b01000, 5'b11000: turn = 2'b01; // 偏左,左转 5'b00110, 5'b00010, 5'b00011: turn = 2'b10; // 偏右,右转 5'b00000: turn = 2'b11; // 全白,丢线 default: turn = 2'b00; // 多路同时触发,按直行兜底 endcase end endmodule

这里的设计取舍:default分支不能省,综合工具对未覆盖状态的处理可能产生锁存器,这是组合逻辑的经典坑。多路同时触发(比如十字路口)按直行兜底,是循迹场景下最稳妥的策略。如果赛道有急弯,可以把turn扩展成三档(微左、急左),但真值表会变长,建议先用两档跑通再迭代。

4.3 PWM 生成与电机驱动:占空比怎么算、方向怎么给

PWM 用计数器比较法,周期由PWM_FREQ决定,占空比由duty输入决定:

module pwm_gen #( parameter CLK_FREQ = 50_000_000, parameter PWM_FREQ = 10_000 )( input wire clk, input wire rst_n, input wire [7:0] duty, // 0~100,百分比 output reg pwm_out ); localparam PERIOD = CLK_FREQ / PWM_FREQ; // 5000 reg [15:0] cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= 16'd0; pwm_out <= 1'b0; end else begin if (cnt >= PERIOD - 1) cnt <= 16'd0; else cnt <= cnt + 1'b1; // 占空比比较,duty 为百分比 pwm_out <= (cnt < (PERIOD * duty) / 100); end end endmodule

参数说明:PERIOD是计数上限,50 MHz / 10 kHz = 5000;duty用 0~100 的百分比,方便上层直接给“速度 60%”这种语义。注意PERIOD * duty在综合时是常数乘,位宽要够,16 位计数器配 5000 上限没问题。电机驱动侧,方向信号和 PWM 组合:

// 左轮:turn=01 时左轮减速、右轮加速,实现左转 assign dir_l = (turn == 2'b01) ? 1'b0 : 1'b1; assign dir_r = (turn == 2'b10) ? 1'b0 : 1'b1; // 占空比由 speed_ctrl 根据档位和转向查表给出

转向的本质是差速:左转时左轮占空比降低、右轮保持或提高。speed_ctrl里用一张小查找表把{turn, speed_sel}映射成{duty_l, duty_r},比写一堆 if-else 清晰得多。

5. 避坑与排查:仿真过了上板翻车的五个高频问题

5.1 现象:仿真波形完美,上板电机不转

原因通常是引脚分配或电平不匹配。FPGA 的 I/O 电平标准(3.3 V LVTTL/LVCMOS)与电机驱动模块的输入阈值不一致,或者 PWM 引脚分配到了专用配置引脚上。解决:先在 Assignment Editor 里核对每个引脚的 Location 和 I/O Standard,用 LED 指示 PWM 输出是否在翻转,逐级定位是“没输出”还是“输出了但驱动不认”。

5.2 现象:小车原地抖动或反复左右摆

这是决策逻辑振荡,常见于消抖不足或转向档位过激。传感器在黑白边界反复跳变,决策输出跟着跳,电机就抖。解决:加大DEBOUNCE_N,并在speed_ctrl里加一个简单的“转向保持”逻辑——检测到转向后维持若干毫秒再允许切换,相当于软件上的迟滞。血泪经验是别指望调 PID,循迹这种开关量场景,迟滞比 PID 更直接有效。

5.3 现象:综合报错“multiple drivers”或行为诡异

多半是同一个reg在多个always块里赋值,或者组合逻辑里漏了default导致推断出锁存器。解决:一个信号只在一个always块里赋值;所有case写全default;综合后看 Quartus 的 Warning 列表,锁存器推断和多位驱动都会明确报出来,别只看 Error。

5.4 现象:换一块板子就不工作

参数硬编码或时钟频率假设错误。比如代码里默认 50 MHz,换到 25 MHz 板子上,PWM 频率和采样节拍全变。解决:把CLK_FREQ作为顶层参数,所有分频基于它计算;上板前用 SignalTap 抓一下实际时钟和计数器,确认节拍符合预期。这也是前面强调参数化的原因。

5.5 现象:丢线后小车乱冲

track_decision对全白状态只给了turn=11,但speed_ctrl没处理这个状态,电机保持上一拍占空比。解决:丢线时强制降速或停车,并在顶层加一个超时保护——连续 N 个采样周期都是丢线,直接输出零占空比。这个保护逻辑很短,但能避免小车冲出赛道撞墙,答辩演示时尤其重要。

6. 验收与答辩:怎么证明这套设计真的跑通了

验收不是“小车能跑一圈”就完事,得有可复现的证据链。我一般按三步走:仿真验收、上板验收、指标验收。

仿真验收看三样东西:sensor_sync的消抖波形(输入抖动、输出干净)、track_decision的真值表覆盖(每个case分支都激励到)、pwm_gen的占空比精度(给 50% 看高电平占比)。用 Quartus 自带的波形仿真或 ModelSim 都行,关键是波形截图要能对应到需求表里的指标。

上板验收按信号链逐级点灯:先让 LED 跟随sensor_clean,确认感知层正常;再让 LED 指示turn,确认决策层正常;最后接电机看差速。逐级验证的好处是出问题时能立刻定位到哪一层,而不是“小车不跑”这种笼统现象。

指标验收用一张表对照需求:

验收项方法合格标准
消抖效果手动抖动传感器,看 LED输出无毛刺
响应延迟SignalTap 抓采样到 PWM 更新≤ 10 ms
PWM 频率示波器测电机引脚与设定值误差 < 5%
循迹通过标准赛道跑 3 圈不脱线、不撞墙
丢线保护人为移出赛道1 s 内停车

答辩时老师最爱问的两个问题:“你这个消抖为什么是 8 拍不是 4 拍?”“丢线了怎么办?”——前者答“8 ms 响应与稳定性的折中,实测 4 拍仍有抖动”,后者直接指超时保护模块。能答到参数层面,说明你是真做过,不是拼凑的。

从那以后我每次交 FPGA 项目,都强制自己先跑一遍“仿真—点灯—接执行器”三级验收,再谈整体演示。这套流程救过我不止一次,也让我在答辩时心里有底。希望帮到你。

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

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

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

立即咨询