
简介面向电子信息、自动化等专业学生及EDA初学者的Quartus II实验报告文档以FPGA驱动LED为主题帮助读者熟悉开发流程与VHDL基础。内容围绕SmartSOPC实验箱展开涵盖工程建立、器件选择EP3C5E144C8、VHDL文件添加、编译下载以及点亮LED1、LED3、LED5、LED7的验证并解释FPGA的I/O可配置为输入、输出、双向等模式输出时最大可吸收24mA电流直接驱动发光二极管。包内仅1个PDF文件约2.84MB图文并茂含实验目的、实验内容、原理分析、步骤说明与结果观察记录便于课程实验参考与报告撰写借鉴对实验报告格式与数据记录也有一定参考价值。目前已有161人学习可作为Quartus II操作、引脚分配与下载配置等环节的对照材料。1. 从一份 EDA 实验报告说起Quartus 驱动 LED 到底在做什么很多人第一次打开 Quartus面对的是空白的工程向导和几十页的器件手册实验指导书却只写了一句点亮 LED。真正卡人的从来不是那行assign而是它前面的链路用 Verilog 把逻辑写出来综合器把它映射成查找表和触发器布局布线器再把信号绑到具体的物理引脚上最后通过下载器把配置写进 FPGA灯才亮。任何一环错位板子给你的反馈都是一片漆黑。这份实验报告的价值把 Quartus 的工程流、引脚约束、功能仿真和在线调试串成一条可复现的路径比结果本身重要得多。它适合刚接触 FPGA 的物理、电子类专业学生也适合从单片机转过来、习惯GPIO_Init()却对并行执行和引脚定义在前端完成这两件事没感觉的工程师。后面我按实验台上真正会走的顺序来讲先建工程再写驱动然后仿真烧录最后落到亮度可调这种进阶需求。2. Quartus 工程搭建与 LED 驱动的引脚约束2.1 器件选型与新建工程的关键参数Quartus 现在主流是两个分支老的 Quartus II13.1 是很多教学板驱动兼容性最好的版本和新的 Quartus PrimeLite 版免费20.1、25.1 都在持续更新。教学实验里 Cyclone IV E 系列最常见型号如 EP4CE6E22C8、EP4CE10F17C8容量从 6K 到 10K 逻辑单元如果是新板子可能是 MAX 10 或 Cyclone V。选型时只看一件事板子上那颗芯片的完整型号必须和工程里选的完全一致否则后面引脚约束会报pin location 不存在。新建工程走File New Project Wizard三个地方要盯住工程目录和工作名、顶层实体名Top-Level Entity默认与工程名相同但可以改、器件家族与具体型号。EDA 工具设置那一页可以留空也可以挂 ModelSim 做仿真。提示顶层实体名一旦确定和你module后的名字必须一字不差大小写敏感。这是新手第一个高频报错来源。2.2 Verilog 顶层模块LED 驱动的第一步LED 驱动在 FPGA 上本质是一个分频加输出。下面是最小可跑版本50MHz 时钟4 位 LED2Hz 翻转module led_drive ( input wire clk, // 50MHz 板载时钟 input wire rst_n, // 低电平复位 output reg [3:0] led // 4 位 LED低电平点亮 ); reg [24:0] cnt; // 25 位计数器2^25 ≈ 33.5M // 分频计数每 25M 个时钟翻转一次得到 2Hz always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 25d0; else if (cnt 25d24_999_999) cnt 25d0; else cnt cnt 1b1; end // LED 状态翻转 always (posedge clk or negedge rst_n) begin if (!rst_n) led 4b1111; // 复位时全灭低电平点亮 else if (cnt 25d24_999_999) led ~led; // 每 0.5s 取反一次 else led led; end endmodule逻辑说明计数器cnt负责把 50MHz 降到人眼可分辨的频率cnt位宽 25 位是因为 2^25 33 554 432 25 000 000够用又不浪费触发器和布线资源。第二个always块里保留led led这一句是显式给出所有分支的赋值避免综合器推断出锁存器latch。参数说明24_999_999决定翻转周期想要 1Hz 就改成49_999_999并把位宽提到 26 位想要 10Hz 就改成4_999_999复位值4b1111和板子的点亮电平有关共阳板子低电平点亮共阴则改成4b0000。2.3 Pin Planner 引脚分配与 I/O 标准代码编译通过不代表灯会亮因为信号还没落到物理引脚。引脚编号的唯一来源是开发板原理图常见教学板的对照如下以某 Cyclone IV EP4CE6 板为例具体以你手里的原理图为准端口名板载资源示例引脚I/O 标准clk50MHz 晶振PIN_233.3-V LVTTLrst_n复位按键PIN_253.3-V LVTTLled[0]LED0PIN_873.3-V LVTTLled[1]LED1PIN_883.3-V LVTTLled[2]LED2PIN_893.3-V LVTTLled[3]LED3PIN_903.3-V LVTTL分配方式有两种图形界面用Assignments Pin Planner双击 Location 列填引脚号命令行或批量时直接写 QSF# led_drive.qsf 片段 set_location_assignment PIN_23 -to clk set_location_assignment PIN_25 -to rst_n set_location_assignment PIN_87 -to led[0] set_location_assignment PIN_88 -to led[1] set_location_assignment PIN_89 -to led[2] set_location_assignment PIN_90 -to led[3] set_instance_assignment -name IO_STANDARD 3.3-V LVTTL -to led[*]参数说明IO_STANDARD一定要和板子供电电压匹配3.3V 器件写成 2.5-V 会导致电平判定错误灯表现成常亮或半亮。led[*]是通配写法一次覆盖整个总线。2.4 编译流程与常见报错定位编译用Processing Start Compilation或 CtrlL。编译完成后看 Compilation Report重点三块Analysis Synthesis 有无 latch 警告、Fitter 报告的引脚分配成功率、Timing Analyzer 的 Fmax。高频报错和对应处理Error (12007): Top-level design entity led is undefined → 顶层实体名与 module 名不一致检查大小写 Error (176310): Cant place multiple pins assigned to pin location PIN_87 → 同一引脚被分配两次检查 QSF 是否重复 Critical Warning (332012): No clocks defined in design → 时钟端口没被识别确认 clk 引到寄存器且已分配引脚定位方法Error 里带 (数字) 的是 Quartus 内部错误码Messges 窗口双击可直接跳到源文件行号Critical Warning 不一定阻断编译但必须在烧录前处理否则行为不可预期。3. LED 驱动逻辑设计从静态点亮到流水灯与 PWM 调光3.1 分频计数器的参数计算分频是 LED 驱动的地基。公式很简单想要输出频率 f_out时钟 f_clk计数终值 N f_clk / (2 × f_out)。50MHz 出 1HzN 25 000 000位宽取 ⌈log2(N)⌉ 25 位。有人图省事写 32 位综合后多消耗几十个触发器在资源紧的 EP4CE6 上可能就是布不下某些逻辑的临界点。注意计数终值用十进制下划线分隔写清楚25000000比25_000_000更容易看错位数一个 0 的差别就是 10 倍频率。3.2 流水灯的状态机实现流水灯用移位寄存器最省资源不需要状态机// 在 led_drive 基础上增加移位逻辑 reg [24:0] div_cnt; wire tick (div_cnt 25d4_999_999); // 10Hz 节拍 always (posedge clk or negedge rst_n) begin if (!rst_n) div_cnt 25d0; else if (tick) div_cnt 25d0; else div_cnt div_cnt 1b1; end always (posedge clk or negedge rst_n) begin if (!rst_n) led 4b0001; // 初始仅 LED0 亮 else if (tick) led {led[2:0], led[3]}; // 循环左移 end逻辑说明tick是一个单周期脉冲把 50MHz 降成 10Hz 的节拍移位操作只在节拍到来时发生。{led[2:0], led[3]}是拼接运算把低 3 位和高 1 位重新组合实现环形左移。想改成右移写成{led[0], led[3:1]}想 8 路就用[7:0]并把拼接改为{led[6:0], led[7]}。4_999_999决定流水速度改小变快。3.3 PWM 呼吸灯的占空比控制LED 亮度可调靠 PWM核心是一个比较器计数器小于占空比时输出点亮电平否则熄灭。占空比缓慢变化就成了呼吸效果。// PWM 呼吸灯核心14 位计数器 reg [9:0] pwm_cnt; // 0 ~ 1023PWM 周期 1024 个时钟 reg [9:0] duty; // 当前占空比 reg dir; // 0 递增1 递减 wire pwm_clk; // 由前面降频得到约 1kHz always (posedge pwm_clk or negedge rst_n) begin if (!rst_n) begin pwm_cnt 10d0; duty 10d0; dir 1b0; end else begin // 计数器循环 0~1023 pwm_cnt (pwm_cnt 10d1023) ? 10d0 : pwm_cnt 1b1; // 每个 PWM 周期末更新占空比 if (pwm_cnt 10d1023) begin if (dir 1b0) begin if (duty 10d1023) dir 1b1; else duty duty 1b1; end else begin if (duty 10d0) dir 1b0; else duty duty - 1b1; end end end end assign led (pwm_cnt duty) ? 1b0 : 1b1; // 低电平点亮逻辑说明PWM 周期由pwm_cnt从 0 数到 1023 决定占空比duty决定高电平时间。dir控制占空比在 0 到满之间往返形成呼吸。参数说明PWM 频率要高于 200Hz 才不会被人眼察觉闪烁pwm_clk取 1kHz 时 1024 个周期对应约 1Hz 的呼吸节奏想要呼吸更快就把pwm_clk提到 2kHz 或把duty步进改成2/-2。若同时驱动多路 LED 且亮度要独立每路各带一个duty寄存器和一个比较器即可。3.4 驱动模式与资源配置对照模式触发器关键参数频率要点静态点亮4无无2Hz 闪烁25 4计数终值人眼可分辨≤5Hz流水灯25 4节拍终值5~20Hz 视觉效果最好PWM 呼吸10 10 1PWM 周期、步进PWM ≥ 200Hz呼吸 0.5~2Hz资源不是越省越好而是要刚好够。PWM 的 10 位分辨率对应 1/1024 的亮度级差人眼已经基本看不出台阶如果想要更平滑升到 12 位代价是计数器多 2 位触发器几乎无感。4. 仿真、烧录与在线调试4.1 功能仿真Testbench 与 ModelSim烧板子前先仿真能省掉反复插拔下载器的功夫。写一个最小 Testbenchtimescale 1ns/1ps module tb_led_drive; reg clk; reg rst_n; wire [3:0] led; led_drive uut (.clk(clk), .rst_n(rst_n), .led(led)); // 产生 50MHz 时钟周期 20ns initial begin clk 1b0; forever #10 clk ~clk; end initial begin rst_n 1b0; #100 rst_n 1b1; // 100ns 后释放复位 #2000 $stop; // 仿真 2us 后停止 end endmodule逻辑说明#10 clk ~clk产生半周期 10ns 的时钟即 50MHz。复位在 100ns 后拉高观察led是否按 0.5s 周期翻转。因为仿真时间通常跑不到 0.5s实际验证时把计数终值临时改小比如改成 49再跑波形否则仿真器要跑很久。工具上挂 ModelSim-Altera在 Quartus 里Tools Run Simulation Tool RTL Simulation直接调用用 Vivado 的小伙伴换成 xsim思路一样。4.2 下载器驱动与 JTAG 烧录USB-Blaster 下载器插上电脑后如果 Quartus Programmer 里 Hardware 下拉框是空的九成是驱动没装。驱动位置在安装目录下Quartus安装目录\quartus\drivers\usb-blaster设备管理器里找到带感叹号的未知设备右键更新驱动程序手动指到上面这个文件夹。装完后 Quartus 里Tools Programmer能自动识别。烧录流程Auto Detect 确认检测到芯片型号 → Add File 添加.sof文件 → 勾选 Program/Configure → Start。.sof是 SRAM Object File掉电即失要断电保存需要在Convert Programming Files里生成.jicJTAG Indirect Configuration再烧到配置芯片里。注意烧录前务必回到 Programmer 确认 Mode 是 JTAG、下载器型号与实物一致。选错下载器Start 会一直卡在进度条不动。4.3 SignalTap 抓波形定位 LED 不亮灯不亮时别急着改代码先用 SignalTap 看内部信号。Tools SignalTap Logic Analyzer打开新建实例采样时钟选clk把cnt、led、tick拖进 Setup 列表Sampled 设为 1024编译后用 JTAG 下载并运行。典型结论分三种cnt完全不动时钟没进 FPGA 或引脚分配错误回查 QSF 的clk和原理图晶振脚。cnt正常但led不变看两处复位信号是否一直有效按键极性反了以及翻转条件里的终值是否写错位数。led在跳但板子不亮99% 是极性或引脚问题把led输出取反测试或换一个已知好的引脚。5. LED 亮度可调多路驱动与资源时序优化技巧要做多路、多级亮度先把驱动方式想清楚。FPGA 直接驱动几个 LED 是最简单的但如果要驱动 LED 模组或点阵常见的做法是用扫描行选通 列数据配合刷新率不低于 100Hz。若亮度还要逐路独立可调把第 3 章的 PWM 复制一份每路给一个独立的duty寄存器和比较器共享同一个pwm_cnt这样计数器只占一份资源。// 多路独立亮度4 路共享 PWM 计数器 wire [3:0] pwm_out; genvar i; generate for (i 0; i 4; i i 1) begin : gen_pwm assign pwm_out[i] (pwm_cnt duty[i]) ? 1b0 : 1b1; end endgenerate assign led pwm_out;逻辑说明generate在综合时展开成 4 份并行的比较逻辑pwm_cnt只有一份duty是 4×10 位的寄存器组。参数上若发现时序报告里pwm_out的路径 slack 为负说明一级比较逻辑后接了太重的负载比如直接驱动大电流灯此时应把驱动电流交给外部的 LED 闪灯驱动芯片或三极管FPGA 只出逻辑信号。三件事值得在做实验报告时记下来。第一计数器位宽按需给EP4CE6 这类小容量器件上随手写 32 位最终会体现在资源占用的百分比里。第二加一条时钟约束让时序分析有依据# led_drive.sdc create_clock -name clk -period 20.000 [get_ports {clk}]-period 20.000对应 50MHzFmax 报告会告诉你当前逻辑最快能跑多少低于 50MHz 说明组合逻辑链太长考虑插一级寄存器。第三验证亮度和频率这类看不太准的参数不要靠眼睛SignalTap 抓一段时间窗内的波形计数或者干脆写个计数器记录led翻转次数再和理论值对。本文还有配套的精品资源点击获取