1 所选任务介绍
本项目选择了基于FPGA的综合信号处理任务。任务要求利用电赛训练板上的DAC模块实现DDS(Direct Digital Synthesis,直接数字频率合成)信号源,能够输出正弦波、方波和三角波三种波形,并支持频率和幅度的调节以及线性扫频功能。同时,需要通过板载ADC模块对生成的信号进行采样,在FPGA内部完成信号幅度计算,并通过OLED屏幕和数码管进行实时显示。此外,系统还需要支持旋转编码器与按键交互,实现参数调节与模式切换。
该任务综合涉及数字信号生成、模拟信号采样、实时数据处理以及嵌入式人机交互等多个方面,是一个典型的FPGA系统设计与信号处理综合实验。
2 项目介绍
本项目设计并实现了一个基于Intel MAX10 FPGA的综合信号处理系统。系统以FPGA为核心,通过纯Verilog硬件逻辑实现了波形生成、信号采集、实时处理以及图形显示等功能,构建了一个完整的闭环信号系统。
在系统结构上,FPGA内部首先通过DDS技术生成数字波形数据,经高速DAC转换为模拟信号输出。该信号通过外部回环连接进入ADC,再由FPGA进行实时采样与数据分析。采集到的信号数据在FPGA内部完成幅度计算与波形重构,并最终在OLED屏幕上进行实时显示,同时通过数码管显示当前频率参数。
在基本功能之外,本项目还对显示与可视化效果进行了扩展设计。通过硬件过零点触发机制,使波形能够稳定锁定在屏幕中央;通过垂直插值算法,解决了数字采样在显示方波时产生的断点问题,使OLED上的波形显示更加连续、清晰,从而实现了类似数字示波器的实时波形显示效果。
整个系统完全基于硬件逻辑实现,没有使用任何MCU或Nios II软核处理器,在资源较为有限的MAX10 10M02 FPGA上完成了较为完整的信号处理系统设计。

图1 系统实物图
3 硬件系统组成
本项目所使用的主要硬件模块如下。
系统核心为StepFPGA MAX10核心板,其搭载Intel MAX10系列10M02SCM153 FPGA芯片,负责系统全部逻辑运算与时序控制。FPGA通过板载12MHz有源晶振获得系统主时钟,并以此作为整个系统运行的全局时序基准。
信号输出部分使用底板上的10bit并行高速DAC模块。DDS模块生成的数字波形数据通过该DAC转换为模拟信号输出,从而形成连续波形。
信号采集部分采用10 bit并行高速ADC模块,用于对模拟信号进行离散采样。ADC采集到的数据实时送入FPGA内部进行处理和分析。
显示模块由两部分组成:一块 0.96 英寸128×64分辨率的OLED屏幕和两位七段数码管。其中OLED屏幕通过SPI接口与FPGA通信,用于显示实时波形和电压信息;数码管采用静态驱动方式,用于显示当前输出频率。
在人机交互方面,系统使用一个带按压功能的旋转编码器以及两个轻触按键。旋转编码器用于调节频率与幅度参数,按压操作用于切换调节模式,而两个按键则分别用于波形切换和扫频模式控制。
4 系统方案与设计思路
在整体设计上,本系统采用自顶向下的设计方法,将系统划分为多个功能模块,并通过统一的时钟系统进行协同工作。系统主要由四个核心模块组成:用户交互模块、DDS 波形生成模块、ADC 数据分析模块以及 OLED 显示模块。
用户交互模块负责对旋转编码器和按键输入进行采样和去抖处理,并根据用户操作更新系统状态,例如当前频率、幅度参数、波形类型以及扫频模式等。
DDS 波形生成模块是系统的核心信号源模块。该模块通过 32 位相位累加器实现直接数字频率合成,并通过查找表方式生成正弦波,同时利用组合逻辑生成方波和三角波。生成的数字波形数据经过幅度缩放处理后送入 DAC 输出。
ADC 数据分析模块负责对 ADC 采集到的数据流进行实时处理。通过在一定时间窗口内记录采样数据的最大值与最小值,计算其差值获得信号的峰峰值,从而得到信号幅度。
OLED 显示模块则负责将采集到的信号数据转化为二维图形显示。系统通过实时采样 ADC 数据并进行波形缓存,再通过图形渲染逻辑在 OLED 上绘制连续波形。为了保证显示稳定性,系统还引入了过零点触发机制,使每帧波形从相同相位开始绘制,从而实现稳定的波形显示。

图2 系统结构框图

图3 DDS原理图
5 开发环境与关键代码
本项目的开发环境为 Intel Quartus Prime Lite Edition,编程语言采用 Verilog HDL。
系统输入端包含旋转编码器和独立按键,机械触点闭合时会产生随机抖动,直接接入状态机会导致误触发。针对此问题,代码中设计了基于计数器的状态延时采样逻辑。对于编码器采用约1ms的防抖周期,对于按键采用约20ms的防抖周期。在编码器方向判断上,系统首先对A、B两相信号进行打拍同步,随后通过边沿检测提取 A 相的下降沿。在 A 相下降沿触发时,读取 B 相的稳定电平状态,从而准确判定编码器是顺时针还是逆时针旋转。
reg [15:0] enc_cnt; reg a_st;
always @(posedge clk) begin
if (enc_a == a_st) enc_cnt <= 0; else begin enc_cnt <= enc_cnt + 1; if(enc_cnt == 12000) a_st <= enc_a; end
end
reg k1_last, k2_last, a_last, sw_last;
reg [23:0] sweep_timer;
reg edit_mode;
initial edit_mode = 0;
always @(posedge clk) begin
k1_last <= k1_st; k2_last <= k2_st; a_last <= a_st; sw_last <= sw_st;
if (k1_last && !k1_st) wave_type <= wave_type + 1;
if (k2_last && !k2_st) sweep_en <= ~sweep_en;
if (sw_last && !sw_st) edit_mode <= ~edit_mode;
if (!sweep_en) begin
sweep_x <= 0;//手动模式X坐标归零
if (a_last && !a_st) begin
if (enc_b) begin
if (edit_mode == 0) begin
if (!(khz_tens == 9 && khz_ones == 9)) begin
freq_word <= freq_word + STEP_1KHZ;
if (khz_ones == 9) begin khz_ones <= 0; khz_tens <= khz_tens + 1; end
else khz_ones <= khz_ones + 1;
end
end else begin if (amp_scale < 1000) amp_scale <= amp_scale + 50; end
end else begin
if (edit_mode == 0) begin
if (!(khz_tens == 0 && khz_ones == 0)) begin
freq_word <= freq_word - STEP_1KHZ;
if (khz_ones == 0) begin khz_ones <= 9; khz_tens <= khz_tens - 1; end
else khz_ones <= khz_ones - 1;
end
end else begin if (amp_scale > 50) amp_scale <= amp_scale - 50; end
end
end
end else begin
sweep_timer <= sweep_timer + 1;
if (sweep_timer > 24'd1200000) begin //0.1秒步进一次
sweep_timer <= 0;
//X坐标推进(0-127对应OLED屏幕宽度)
if (sweep_x < 127) sweep_x <= sweep_x + 1;
else sweep_x <= 0;
//频率增加1kHz
freq_word <= freq_word + STEP_1KHZ;
//数码管BCD码同步增加
if (khz_ones == 9) begin
khz_ones <= 0;
if (khz_tens == 9) khz_tens <= 0; //99kHz 后绕回 00
else khz_tens <= khz_tens + 1;
end else khz_ones <= khz_ones + 1;
end
end
end
在资源受限的FPGA中实现高精度DDS,ROM资源的消耗是最大的瓶颈。本项目采用32位高精度相位累加器保证了扫频时绝佳的相位连续性。同时,在正弦波查找表设计上,摒弃了全周期建表法,采用了的1/4 周期象限映射算法。代码中仅存储了0~π/2区间的64个离散点,通过提取32位相位的最高两位作为象限标志位,利用正弦波的奇偶对称性,在纯组合逻辑中实时重构出完整的360度连续正弦波。此举将ROM资源占用压缩了75%,极大地优化了片上逻辑单元的利用率。
reg [31:0] acc;
always @(posedge clk) acc <= acc + freq_word;
wire [9:0] squ = acc[31] ? 1023 : 0;
wire [9:0] tri_w = acc[31] ? (~acc[30:21]) : acc[30:21];
reg [9:0] sin_lut [0:63];
initial begin
sin_lut[0]=0; sin_lut[1]=25; sin_lut[2]=50; sin_lut[3]=75; sin_lut[4]=100; sin_lut[5]=125; sin_lut[6]=150; sin_lut[7]=175; sin_lut[8]=199; sin_lut[9]=223; sin_lut[10]=248; sin_lut[11]=272; sin_lut[12]=296; sin_lut[13]=319; sin_lut[14]=342; sin_lut[15]=365; sin_lut[16]=388; sin_lut[17]=410; sin_lut[18]=432; sin_lut[19]=454; sin_lut[20]=475; sin_lut[21]=496; sin_lut[22]=516; sin_lut[23]=536; sin_lut[24]=555; sin_lut[25]=574; sin_lut[26]=593; sin_lut[27]=611; sin_lut[28]=629; sin_lut[29]=646; sin_lut[30]=663; sin_lut[31]=679; sin_lut[32]=695; sin_lut[33]=710; sin_lut[34]=725; sin_lut[35]=739; sin_lut[36]=752; sin_lut[37]=765; sin_lut[38]=778; sin_lut[39]=789; sin_lut[40]=800; sin_lut[41]=810; sin_lut[42]=820; sin_lut[43]=829; sin_lut[44]=838; sin_lut[45]=846; sin_lut[46]=853; sin_lut[47]=860; sin_lut[48]=866; sin_lut[49]=872; sin_lut[50]=877; sin_lut[51]=881; sin_lut[52]=885; sin_lut[53]=888; sin_lut[54]=891; sin_lut[55]=893; sin_lut[56]=895; sin_lut[57]=896; sin_lut[58]=897; sin_lut[59]=897; sin_lut[60]=897; sin_lut[61]=897; sin_lut[62]=897; sin_lut[63]=897;
end
wire [5:0] lut_addr = acc[29:24];
wire [1:0] quadrant = acc[31:30];
reg [9:0] sin_val;
always @(*) begin
case(quadrant)
2'b00: sin_val = 512 + (sin_lut[lut_addr] >> 1);
2'b01: sin_val = 512 + (sin_lut[~lut_addr] >> 1);
2'b10: sin_val = 512 - (sin_lut[lut_addr] >> 1);
2'b11: sin_val = 512 - (sin_lut[~lut_addr] >> 1);
endcase
end
在波形显示过程中,由于ADC采样是离散的,在显示方波等突变信号时,OLED上可能出现断裂点。为了解决这一问题,系统设计了垂直插值算法,通过检测相邻两列波形的纵坐标差值,在OLED像素层面自动补全垂直连线,从而形成连续的波形边缘。
// 提取当前列和上一列的 Y 坐标
wire [5:0] y_curr = wave_ram[cur_col];
wire [5:0] y_prev = (cur_col == 0) ? y_curr : wave_ram[cur_col - 1];
// 找出两点之间的垂直边界
reg [5:0] y_min, y_max;
always @(*) begin
if (y_curr < y_prev) begin y_min = y_curr; y_max = y_prev; end
else begin y_min = y_prev; y_max = y_curr; end
end
// 生成画线掩码:判断当前 OLED 扫描的 8 个像素位是否落在两点之间
reg [7:0] wave_mask;
integer i;
always @(*) begin
wave_mask = 8'h00;
for (i=0; i<8; i=i+1) begin
if ((page_y_start + i) >= y_min && (page_y_start + i) <= y_max)
wave_mask[i] = 1'b1;
end
end

图4 系统流程图
6 功能展示与测试结果
本系统实现了以下主要功能:
系统能够生成正弦波、方波和三角波三种波形,并支持通过旋转编码器对输出频率和幅度进行实时调节。数码管实时显示当前输出频率,OLED屏幕显示对应波形及电压信息。
在扫频模式下,系统通过DDS技术实现频率连续变化,并在OLED屏幕上动态显示幅频特性曲线。由于DDS的相位累加过程具有相位连续性,因此扫频过程中不会出现相位突变或信号毛刺,从而保证了输出信号的稳定性。
此外,系统通过峰峰值检测算法实现信号幅度测量。该方法在 FPGA 内部建立时间窗口,持续记录 ADC 数据的最大值与最小值,并通过两者之差计算信号峰峰值。这种方法不仅资源开销较小,而且能够有效消除系统直流偏置带来的测量误差。

图5.1 OLED波形显示正弦波图

图5.2 OLED波形显示方波图

图5.1 OLED波形显示三角波图

图5.1 OLED波形显示扫频图
7 项目难点与解决方法
在项目开发过程中,主要遇到了以下几个技术问题。
首先是是ADC采样存在明显的直流偏置。当 ADC 输入接地时,读数仍保持在约486左右。经过分析,这是单电源模拟电路中的直流偏置设计所导致。为此系统改用峰峰值检测算法,通过计算最大值与最小值的差值消除了直流偏置对测量结果的影响。
第二个问题是在OLED波形显示时出现马赛克现象。原因是波形刷新频率与信号采样频率不同步。为了解决这一问题,在系统中引入硬件过零点触发机制,使波形采样始终从同一相位开始,从而实现稳定的波形显示效果。
8 心得体会
通过本次寒假练项目,我对FPGA系统设计有了更加深入的理解。在开发过程中逐渐认识到,FPGA开发不仅仅是编写正确的逻辑代码,更重要的是对系统时序、硬件结构以及物理特性的综合理解。
例如引脚复用问题、机械开关抖动、模拟电路直流偏置等因素,都可能对系统功能产生影响。只有在设计中同时考虑硬件特性与逻辑结构,才能构建出稳定可靠的系统。
通过不断调试和优化,本项目最终实现了一个功能较为完整的FPGA信号处理系统。这一过程不仅提升了我在Verilog编程方面的能力,也加深了我对数字系统设计与信号处理原理的理解。

图6 FPGA资源占用图