一、所选任务介绍
本项目选择的任务为“基于高速 ADC 的数字频率计与幅度测量仪”。任务要求利用电赛板上的 ADC 模块,由 FPGA 产生稳定的采样时钟 ADC_CLK,对外部输入的正弦波或方波进行采样,并在 100 Hz~1 MHz 范围内完成频率测量;同时计算信号的幅值参数,对方波进一步测量占空比,并通过七段数码管、OLED 和 LED 等多种方式显示结果。该任务覆盖了模拟前端理解、ADC 采样、FPGA 时序设计、测量算法实现以及显示驱动等多个层面,具有较强的综合性和工程实践价值。
二、项目介绍
本项目最终实现的是一个基于 FPGA 与高速 ADC 的数字测量系统。系统接收外部信号源输入的正弦波或方波,经过板上 ADC 模块采样后,在 FPGA 内部完成频率、峰峰值和占空比等参数计算,再通过两位七段数码管、OLED 显示屏和 LED 量程灯进行结果展示。项目采用模块化设计方法,先完成采样链路与频率测量,再逐步扩展到幅度显示、条形图显示、占空比测量、页面切换、条形图双量程切换以及显示慢刷新优化。经过反复联调,当前系统已经完成项目要求中的核心功能,测量结果基本正确,显示效果也较稳定。
三、硬件组成与作用简介
1. FPGA 核心板:本项目使用 Altera MAX10 系列核心板,负责时钟产生、ADC 数据接收、频率与幅度计算、OLED/数码管/LED 显示驱动等核心逻辑。
2. 高速 ADC 模块:使用 3PEAK 3PA1030 10 位、50 MSPS 并行高速 ADC。通过查阅原理图和实测发现,该模块前端并非裸 ADC 输入,而是带有运放、偏置和调理网络,因此项目最终采用“整板输入口”的实际标定关系,而不是简单套用裸芯片手册数据。
3. OLED 显示模块:用于显示峰峰值数值、条形图以及扩展后的占空比页面。
4. 两位共阴极七段数码管:用于显示频率的两位有效数字,并结合小数点和量程指示完成频率读数表达。
5. 8 个 LED 指示灯:板上 LED 为低电平点亮。本项目将 LED 用于频率量程显示,使结果更直观。
6. 信号源:调试阶段利用函数信号发生器提供正弦波和方波输入,并结合 SignalTap 观察 FPGA 内部数据变化,判断是否削顶。

四、方案框图与设计思路
系统总体结构为:外部信号输入 → ADC 模块 → FPGA 采样与处理 → 数码管 / OLED / LED 显示。
在 FPGA 内部,首先由 PLL 根据系统时钟产生 adc_clk;随后由 adc_capture 模块接收并锁存 ADC 数据;signal_preprocess 模块根据采样值及门限关系产生有效过零脉冲,并输出电平状态;freq_meter 统计相邻两次有效过零之间的采样点数,从而得到频率;amp_meter 在一个周期内统计最大值与最小值,得到峰峰值码值;duty_meter 对方波的高电平时间进行统计,求出占空比;data_format 则负责将测量结果转换为数码管、OLED 和 LED 所需的显示格式。
设计思路上,本项目重点强调“先保证测量链路正确,再优化显示效果”。在早期调试中,通过直流点测试和交流波形测试,逐步摸清了整板前端的真实输入关系,最终确认外部输入口近似满足 -5V~+5V 对应 ADC 码值 1023~0 的关系。该结论对后续门限设置、幅度换算和占空比判断都起到了决定性作用。
在代码实现中,将 vpp_filter 模块原有的单周期 amp_valid_out 脉冲输出方式修改为锁存保持方式,使其在获得有效平均结果后保持高电平,从而保证跨时钟域后的显示模块能够稳定识别幅值有效状态。
五、开发软件、编程语言、软件流程与关键代码说明
开发软件主要使用 Quartus Prime,硬件描述语言采用 Verilog HDL,内部调试工具使用 SignalTap。
软件流程为:系统上电后,PLL 产生稳定的 adc_clk,ADC 开始对输入信号采样;adc_capture 在采样时钟上升沿锁存 adc_now 与 adc_last;signal_preprocess 对采样值进行门限判断,产生 zero_cross_pulse;freq_meter 根据 zero_cross_pulse 计算周期采样点数并换算频率;amp_meter 统计一个周期内的最大值和最小值并输出 vpp_code;duty_meter 在方波模式下统计高电平占比;data_format 将结果转换为两位数码管数字、OLED 字符和 LED 量程状态;最后 OLED12864 和 segment 模块完成显示。
关键代码方面,过零检测采用双门限滞回方式,避免过零点附近噪声抖动引起重复触发;频率测量采用周期计数法,逻辑清晰、实现简单;峰峰值测量采用 max-min 的方法,资源占用较少且满足项目要求;OLED 模块使用字符字模表与状态机扫描方式,条形图经优化后采用更美观的块状显示;data_format 则根据项目最终标定关系,将 vpp_code 按整板外部输入满量程 10Vpp 进行换算显示,并实现 10V/5V 双量程条形图切换;为减轻 OLED 上幅值、占空比和条形图的闪烁,还额外加入了显示慢刷新与滤波处理。
在幅值测量优化中,系统加入了 vpp_filter 模块,对 amp_meter 输出的峰峰值码 vpp_code 进行多周期平均,以提高幅值显示稳定性。滤波模块的关键不只是平均算法本身,还包括有效标志信号 amp_valid_out 的处理方式。由于滤波模块位于 adc_clk 域,而后级数据显示模块位于 sys_clk 域,如果 amp_valid_out 仅输出单周期窄脉冲,则后级逻辑可能无法在跨时钟域后稳定采集到该脉冲。为此,本项目在获得首次有效滤波结果后,将 amp_valid_out 保持为高电平,从而保证后级逻辑能够持续读取有效幅值数据。该处理方法有效解决了“加滤波后幅值不显示”的问题。

关键代码说明:
1. ADC 采样模块
ADC 采样模块的作用是在 adc_clk 时钟域内对 ADC 输出数据进行锁存,同时保留当前采样值和前一个采样值,为后续过零检测和波形分析提供基础数据。
always @(posedge adc_clk or negedge rst_n) begin
if(!rst_n) begin
adc_now <= 10'd0;
adc_last <= 10'd0;
end
else begin
adc_last <= adc_now;
adc_now <= adc_data_in;
end
end
该段代码中,adc_now 表示当前采样值,adc_last 表示上一拍采样值。通过同时保存这两个值,可以判断波形在相邻两个采样点之间是否跨越门限,这是实现过零检测的基础。
2. 信号预处理与过零检测模块
频率测量采用过零检测法,因此需要首先在采样数据中提取有效过零脉冲。考虑到本项目 ADC 模块前端带有运放调理,板级输入关系表现为外部输入电压与 ADC 码值反相关,因此过零检测方向需要根据实测结果进行修正。此外,为减小门限附近噪声导致的重复触发,还采用了双门限滞回判断。
always @(posedge adc_clk or negedge rst_n) begin
if(!rst_n) begin
zero_cross_pulse <= 1'b0;
level_high <= 1'b0;
high_region_flag <= 1'b0;
end
else begin
zero_cross_pulse <= 1'b0;
if(adc_now <= TH_LOW)
level_high <= 1'b1;
else if(adc_now >= TH_HIGH)
level_high <= 1'b0;
if(adc_now >= TH_HIGH)
high_region_flag <= 1'b1;
if(high_region_flag && (adc_last > TH_LOW) && (adc_now <= TH_LOW)) begin
zero_cross_pulse <= 1'b1;
high_region_flag <= 1'b0;
end
end
end
其中,TH_LOW 和 TH_HIGH 构成滞回比较区间;high_region_flag 用于保证只有在波形完整经过高门限后,才允许后续的低门限跨越被识别为一次有效过零。这样可以有效抑制噪声带来的误触发。
3. 频率测量模块
频率测量模块根据相邻两次有效过零之间的采样点数来估算输入信号周期,再结合 ADC 采样时钟频率计算得到输入频率。为了避免刚启动时第一周期数据无效,还加入了首次捕获标志位。
always @(posedge adc_clk or negedge rst_n) begin
if(!rst_n) begin
period_cnt <= 32'd0;
period_latch <= 32'd0;
freq_value <= 32'd0;
freq_valid <= 1'b0;
first_capture_done <= 1'b0;
end
else begin
if(period_cnt < TIMEOUT_CNT)
period_cnt <= period_cnt + 1'b1;
else begin
period_cnt <= TIMEOUT_CNT;
freq_value <= 32'd0;
freq_valid <= 1'b0;
end
if(zero_cross_pulse) begin
if(!first_capture_done) begin
first_capture_done <= 1'b1;
period_cnt <= 32'd0;
freq_valid <= 1'b0;
end
else begin
period_latch <= period_cnt;
period_cnt <= 32'd0;
if(period_cnt >= MIN_PERIOD) begin
freq_value <= FS / period_cnt;
freq_valid <= 1'b1;
end
else begin
freq_valid <= 1'b0;
end
end
end
end
end
其中,period_cnt 用于记录相邻两次过零之间的采样点数,FS 为 ADC 采样时钟频率。通过 freq_value <= FS / period_cnt 即可得到频率值。后续调试中还发现 PLL 输出频率必须与 FS 参数保持一致,否则会导致频率整体按固定比例偏差。
4. 幅值滤波模块
在实际调试中发现,幅值测量结果会因为噪声和周期边界误差产生轻微跳动。因此在 amp_meter 后增加了 vpp_filter 模块,对多个周期的 vpp_code 进行平均处理,从而提高幅值显示稳定性。
always @(posedge adc_clk or negedge rst_n) begin
if(!rst_n) begin
sum <= 12'd0;
cnt <= 2'd0;
vpp_code_out <= 10'd0;
amp_valid_out<= 1'b0;
end
else begin
if(amp_valid_in) begin
sum <= sum + vpp_code_in;
cnt <= cnt + 1'b1;
if(cnt == 2'd3) begin
vpp_code_out <= (sum + vpp_code_in) >> 2;
amp_valid_out <= 1'b1;
sum <= 12'd0;
cnt <= 2'd0;
end
end
end
end
这里采用 4 次平均滤波。需要注意的是,由于 vpp_filter 工作在 adc_clk 域,而后续 data_format 工作在 sys_clk 域,若 amp_valid_out 仅为单周期窄脉冲,则可能在跨时钟域时被漏采样。因此最终又将 amp_valid_out 修改为锁存保持方式,以保证后级显示模块能够稳定识别幅值有效状态。
5. OLED 条形图显示模块
为了更直观地反映幅值大小,本项目在 OLED 上增加了条形图显示。起初尝试直接使用比例计算生成条形等级,但实际效果不稳定;最终改为分段比较法,将 vpp_code 映射为 0~16 级条形等级,显示效果明显改善。
if(sw[1] == 1'b0) begin
if(vpp_code < 10'd64)
bar_level <= 5'd0;
else if(vpp_code < 10'd128)
bar_level <= 5'd1;
...
else
bar_level <= 5'd16;
end
else begin
if(vpp_code < 10'd32)
bar_level <= 5'd0;
else if(vpp_code < 10'd64)
bar_level <= 5'd1;
...
else
bar_level <= 5'd16;
end
其中,sw[1] 用于切换 10V 与 5V 两档条形图量程。该方案能够在大信号和中小信号条件下都获得较好的显示效果。
六、功能实现情况与结果说明
目前系统已完成以下功能:
1. 由 PLL 产生稳定的 20 MHz ADC 采样时钟,对外部正弦波和方波信号进行采样;
2. 在 100 Hz~1 MHz 范围内实现频率测量;
3. 实现峰峰值幅度测量,并在 OLED 上显示真实幅值数值;
4. 实现方波占空比测量,并可在 OLED 页面中显示;
5. 使用两位七段数码管显示频率的两位有效数字,并结合 LED 指示当前量程;
6. OLED 显示幅值与条形图,并支持 10V/5V 双量程切换;
7. 通过拨码开关实现页面切换与条形图显示量程切换;
8. 通过显示慢刷新与幅值滤波处理,改善了显示抖动与视觉闪烁问题。
从当前测试结果看,系统在典型正弦波和方波输入条件下均已能够完成预期功能,频率、幅值和占空比测量结果基本正确,满足项目的基本要求。

实物功能展示图:
1. 输入10kHz 5V正弦波模式

2.输入1kHz 10V正弦波模式

3.输入1kHz 占空比方波模式

验收表_final:
序号 | 输入信号类型 | 设定频率 | 系统显示频率 | 结果说明 |
1 | 正弦波 | 100 Hz | 100 Hz / 99 Hz | 基本正确,存在 1 Hz 量级轻微跳动 |
2 | 正弦波 | 1 kHz | 1 kHz / 999 Hz | 基本正确,结果稳定 |
3 | 正弦波 | 10 kHz | 10 kHz | 正确 |
4 | 正弦波 | 100 kHz | 100 kHz | 正确 |
5 | 正弦波 | 1 MHz | 1 MHz | 正确 |
序号 | 输入信号类型 | 设定频率 | 系统显示频率 | 结果说明 |
1 | 方波 | 1 kHz | 1 kHz | 正确 |
2 | 方波 | 10 kHz | 10 kHz | 正确 |
频率测量结果分析:
从测试结果可以看出,系统在 100 Hz 至 1 MHz 范围内能够实现较为准确的频率测量,满足题目要求。对于低频正弦波(如 100 Hz 和 1 kHz),显示值在相邻两个整数之间存在轻微跳动,例如 100 Hz 与 99 Hz、1 kHz 与 999 Hz 之间切换。这主要是由于过零检测法存在采样点量化误差,以及门限附近噪声引起的过零时刻轻微漂移所致。对于 10 kHz 及以上频率,显示结果基本稳定且与输入一致,说明系统测频性能总体良好。
序号 | 输入峰峰值 | OLED显示峰峰值 | 绝对误差 | 相对误差 | 结果说明 |
1 | 1.00 V | 1.08 V | 0.08 V | 8.0% | 基本正确 |
2 | 2.00 V | 2.10 V | 0.10 V | 5.0% | 基本正确 |
3 | 5.00 V | 5.11 V | 0.11 V | 2.2% | 正确 |
4 | 10.00 V | 9.88 V | 0.12 V | 1.2% | 正确 |
幅值测量结果分析:
由测试结果可见,系统幅值测量整体较为准确,且随着输入幅值增大,相对误差呈下降趋势。在 1 V 和 2 V 附近,系统显示值略高于设定值,说明小幅值输入时系统对噪声和峰峰值边界较为敏感;在 5 V 和 10 V 附近,测量误差明显减小,说明系统在中高幅值范围内表现更稳定。总体来看,幅值显示已经能够较好反映输入信号变化规律,满足项目要求。
序号 | 输入方波占空比 | OLED显示占空比 | 绝对误差 | 结果说明 |
1 | 30% | 30% / 29% | 0% ~ 1% | 基本正确,存在轻微跳动 |
2 | 50% | 50% | 0% | 正确 |
3 | 70% | 69% | 1% | 正确 |
占空比测量结果分析:
占空比测试结果表明,系统能够较准确地完成方波占空比测量。对于 30%、50%、70% 三组典型测试值,系统显示结果均与输入值基本一致,最大误差约为 1%。其中 30% 附近会出现 30% 和 29% 的轻微波动,这主要与采样边界的离散性及门限判断误差有关。总体来看,占空比测量结果稳定,满足设计要求。

七、项目中遇到的难题及解决方法
项目推进过程中遇到的最大难题并不是单纯的 Verilog 代码,而是对整板输入通道的理解。起初按照裸 ADC 芯片的理论输入范围去理解板级系统,导致直流测试结果与预期不一致。后续通过查看原理图发现,ADC 输入前存在运放和偏置调理网络,因此外部输入并不会直接按裸 ADC 关系映射为码值。通过进一步实测得到 0V、1V、2V 与空接状态下的 ADC 数据变化,再结合大幅度正弦输入测试,最终确认整板输入口近似满足 -5V~+5V 对应 1023~0 的关系。

第二个难题是频率测量比例错误。调试过程中曾出现输入 100 Hz、1 kHz、10 kHz 时显示值统一偏大的现象,最终定位为 PLL 实际输出频率与频率换算参数不一致。修正 adc_clk 参数后,频率显示恢复正常。
第三个难题是过零检测方向。由于整板输入口与 ADC 码值是反相关的,外部信号上升时 ADC 码值下降,因此需要将有效过零改为“码值向下穿越中心门限”的检测方式,而不是简单照搬初始版本。
第四个难题是 OLED 条形图调试。早期在屏幕上能够看到标题文字,但条形图不显示。后续经过逐步排查,发现问题不在 OLED 驱动本身,而在于条形图等级生成方式。最终采用更鲁棒的分段比较方式生成 bar_level,使条形图稳定工作。

第五个难题是显示抖动。由于频率值、幅度值和占空比都在实时更新,数码管、LED 与 OLED 容易出现发虚、双灯亮和闪烁等视觉现象。针对这一问题,项目后期加入了显示慢刷新和幅值滤波处理,使最终展示效果明显改善。
最后在项目优化阶段,为提高幅值显示稳定性,系统在 amp_meter 后级增加了峰峰值滤波模块,对多个周期的 vpp_code 进行平均处理,以减小峰峰值法对噪声和周期边界抖动的敏感性。
然而在实际下载测试后发现,加入滤波器后 OLED 上的幅值数值无法正常显示。经过排查发现,该问题并非峰峰值滤波本身失效,而是由有效标志信号 amp_valid_out 的时序特性引起。滤波模块位于 adc_clk=20MHz 时钟域中,输出的 amp_valid_out 初始设计为单周期有效脉冲;而后级 data_format 与 OLED 显示逻辑位于 sys_clk=12MHz 时钟域中。由于该有效脉冲宽度较窄,在跨时钟域后可能无法被显示逻辑稳定采样,造成后级始终认为“幅值无效”,最终表现为幅值不显示。
为解决这一问题,本项目对滤波模块进行了修改:当滤波器累计到足够数量的 vpp_code 并生成一次有效平均值后,不再只输出单周期有效脉冲,而是将 amp_valid_out 置位并保持为高电平。这样可以保证后级显示模块稳定识别幅值数据有效,并正确完成幅值数字和条形图显示。修改后系统运行稳定,滤波后的幅值显示恢复正常,同时显示波动也明显减小。
八、测量误差说明
为保证系统测量结果尽可能准确,在系统调试完成后,对频率测量、幅度测量和占空比测量分别进行了标定。频率标定采用标准信号逐点校准的方法进行,使用函数信号发生器分别输出 1 kHz、10 kHz 等典型频率点,并通过数码管显示结果与信号源设定值进行比较,以确认时钟参数和频率换算关系是否正确。修正 PLL 输出频率和频率换算参数后,系统测频结果恢复正常。幅度标定方面,本项目未直接套用裸 ADC 手册给出的理论输入范围,而是基于整板输入通道进行实测标定。通过对前端原理图的分析以及直流、交流测试,最终建立了整板输入口近似满足 -5V~+5V 对应 1023~0 的关系,并将外部输入 10Vpp 视为当前系统的显示满量程。由此采用 Vpp = vpp_code × 10 / 1023 的关系进行幅值换算。占空比标定方面,使用方波输入并设置不同占空比(如 30%、50%、70%),观察 OLED 页面中显示结果与输入设置值是否一致。经门限方向修正后,占空比测量结果基本正确。
误差来源主要包括:采样点离散造成的周期量化误差、过零点附近噪声带来的门限抖动、小幅值时峰峰值法对噪声较敏感、模拟前端增益与偏置的非理想误差,以及显示器件对快速变化数据的视觉暂留效应。针对这些问题,本项目通过参数校正、门限修正、幅值滤波和显示慢刷新等方法进行了改善,因此系统整体测量结果已达到课程项目要求。
九、心得体会
通过本项目的设计与调试,我更加深刻地体会到:在 FPGA 项目中,代码设计固然重要,但真正决定项目能否跑通的往往是对整板硬件结构和信号链路的理解。最初如果只盯着 ADC 芯片手册,很容易做出看似正确、实际上并不适合当前板级系统的设计;只有结合原理图、SignalTap 波形和直流/交流实验,才能真正摸清系统行为。
本项目也让我体会到模块化设计的重要性。通过将系统拆分为 adc_capture、signal_preprocess、freq_meter、amp_meter、duty_meter、data_format、OLED 驱动和数码管显示等多个模块,不仅使代码层次清晰,也让调试工作有了明确抓手。每发现一个问题,都能够定位到对应模块逐步分析,而不会陷入“整套系统一起崩”的混乱状态。
另外,本项目还提高了我使用 SignalTap 进行联合调试的能力。以前更偏向于只看代码和理论结果,而本次项目中,真正有价值的信息往往来自实测数据。通过这些调试手段,我逐渐建立起了从“外部输入波形—ADC 数据变化—内部逻辑判断—最终显示结果”的完整认识。总体而言,本项目不仅完成了一个功能较完整的数字测量系统,也让我在 FPGA 工程实践、硬件分析和系统调试方面积累了宝贵经验。