FPGA 电赛信号叠加与分离项目 完整工程包
(含 MATLAB 滤波器脚本 + Vivado IP 配置 + 全模块 Verilog 代码 + 引脚约束 + 仿真 / 调试手册)
项目基础信息
1. 硬件平台
- FPGA:Xilinx Artix-7(XC7A35T 通用版,电赛主流)
- 外部晶振:50MHz
- 外设:TLV5618 双通道 12bit DAC、2 路独立机械按键(相位 + / 相位 -)、示波器
2. 核心功能(对标电赛题目)
- 生成两路正弦信号:
fA=50kHz、fB=100kHz; - 数字加法器:严格增益 = 1,两路信号纯叠加,无放大 / 衰减;
- FIR 数字滤波:低通分离 50kHz、高通分离 100kHz;
- 相位控制:两路信号相位差
0~180°,步进 5°,误差≤5°; - DAC 模拟输出:分离后信号峰峰值 ≥1V,示波器观测;
- 拓展:可兼容三角波(代码预留接口)。
3. 全局时钟与采样参数
- 系统主时钟:100MHz(PLL 由 50MHz 晶振倍频得到)
- 数字信号采样率:1MHz(满足奈奎斯特定理,最高信号 100kHz < 500kHz)
- 定点格式:统一使用 Q1.15(16bit 有符号数)
一、MATLAB 滤波器设计完整脚本(可直接运行)
功能:设计 FIR 低通 / 高通滤波器,生成 16bit 定点系数,用于 Vivado FIR IP 导入。
1. 完整 MATLAB 代码
matlab
%% 电赛信号分离 FIR 滤波器设计脚本
% 作者:FPGA 信号处理专用
% 指标:信号 f1=50kHz, f2=100kHz;采样率 Fs=1MHz
clear; clc; close all;
%% 1. 全局参数配置
Fs = 1e6; % 采样率 1MHz
N_bit = 16; % 滤波器系数位宽 16bit
Q_scale = 2^(N_bit-1); % Q1.15 定点缩放因子 2^15 = 32768
%% 2. 设计【低通滤波器 LPF】:分离 50kHz 低频信号
% 指标:通带 0~60kHz,阻带 80kHz,通带纹波 1dB,阻带衰减 60dB
f_pass_lp = 60e3;
f_stop_lp = 80e3;
rp_lp = 1; % 通带纹波(dB)
rs_lp = 60; % 阻带衰减(dB)
% 生成 FIR 低通滤波器
filt_lp = designfilt('lowpassfir',...
'PassbandFrequency',f_pass_lp,...
'StopbandFrequency',f_stop_lp,...
'PassbandRipple',rp_lp,...
'StopbandAttenuation',rs_lp,...
'SampleRate',Fs);
% 浮点系数转 16bit 定点整数(Q1.15)
coef_lp_float = filt_lp.Coefficients;
coef_lp_fix = round(coef_lp_float * Q_scale);
%% 3. 设计【高通滤波器 HPF】:分离 100kHz 高频信号
% 指标:通带 90kHz~500kHz,阻带 70kHz,通带纹波 1dB,阻带衰减 60dB
f_pass_hp = 90e3;
f_stop_hp = 70e3;
rp_hp = 1;
rs_hp = 60;
% 生成 FIR 高通滤波器
filt_hp = designfilt('highpassfir',...
'PassbandFrequency',f_pass_hp,...
'StopbandFrequency',f_stop_hp,...
'PassbandRipple',rp_hp,...
'StopbandAttenuation',rs_hp,...
'SampleRate',Fs);
% 浮点系数转 16bit 定点整数(Q1.15)
coef_hp_float = filt_hp.Coefficients;
coef_hp_fix = round(coef_hp_float * Q_scale);
%% 4. 打印定点系数(直接复制到 Vivado FIR IP)
fprintf('========== 低通滤波器 16bit 定点系数 ==========\n');
disp(coef_lp_fix);
fprintf('\n========== 高通滤波器 16bit 定点系数 ==========\n');
disp(coef_hp_fix);
%% 5. 滤波器幅频响应仿真(验证效果)
% 低通幅频响应
fvtool(filt_lp,'Name','50kHz 低通滤波器');
% 高通幅频响应
fvtool(filt_hp,'Name','100kHz 高通滤波器');
2. 脚本使用说明
- 直接运行脚本,命令行窗口会输出两组定点系数;
- 仿真窗口自动弹出滤波器频响曲线,确认:
- 低通:60kHz 内平坦,80kHz 后衰减 ≥60dB;
- 高通:70kHz 以下衰减 ≥60dB,90kHz 以上平坦;
- 复制输出的整数系数数组,后续导入 Vivado FIR IP。
二、Vivado 三大核心 IP 配置步骤(逐页指引)
本项目用到 3 个官方 IP:Clocking Wizard(PLL)、DDS Compiler、FIR Compiler,必须严格按参数配置。
1. IP1:Clocking Wizard(时钟锁相环)
功能:50MHz 外部晶振 → 倍频为 100MHz 系统时钟
- 打开 IP Integrator → 搜索
Clocking Wizard; - Page1 :Input Clock =
50MHz,复位类型:Active Low(低电平复位); - Page2 :输出时钟
clk_out1 = 100MHz; - 生成 IP,端口:
clk_in(50M)、rst_n(异步复位)、clk_out1(100M)、locked(时钟锁定标志)。
2. IP2:DDS Compiler(直接数字频率合成器)
功能:生成 50kHz/100kHz 正弦波,支持相位偏移
- 搜索
DDS Compiler,进入配置页:- Clock Frequency:
100MHz; - Phase Width(相位位宽):
16bit; - Output Width(输出位宽):
16bit; - Output Type:Signed(有符号数);
- Waveform:Sine(正弦波);
- Clock Frequency:
- Phase Format 页:选择 AXI Phase In(频率字 + 相位偏移输入);
- 端口说明(固定格式):
aclk:100MHz 时钟;s_axis_phase_tdata[31:0]:高 16 位 = 频率控制字,低 16 位 = 相位偏移字;m_axis_data_tdata[15:0]:16bit 有符号正弦输出。
DDS 频率 / 相位字计算(固定值)
- 公式:\(f_{out} = \dfrac{FTW \times f_{clk}}{2^{16}}\)
- 50kHz 频率字:\(FTW\_A = \dfrac{50000 \times 2^{16}}{100e6} = 32768\)
- 100kHz 频率字:\(FTW\_B = \dfrac{100000 \times 2^{16}}{100e6} = 65536\)
- 5° 相位步进字:\(STEP = \dfrac{5}{360} \times 2^{16} \approx 910\)
- 相位范围:
0 ~ 32768对应相位差0° ~ 180°
3. IP3:FIR Compiler(数字滤波器,分 2 个:LPF / HPF)
通用配置(低通、高通完全一致)
- 搜索
FIR Compiler,新建 2 个独立 IP(fir_lpf、fir_hpf); - Page1 :
- Sampling Frequency:
1MHz; - Input Sample Width:
17bit(匹配加法器 17bit 输出); - Output Sample Width:
16bit(匹配 DAC 输入);
- Sampling Frequency:
- Page2 :Filter Structure 选择 Pipelined(流水线)(高速无延迟);
- Coefficient Source :选择
Vector,粘贴 MATLAB 输出的定点系数; - 生成 IP,端口:
aclk:100MHz 时钟;s_axis_data_tdata:滤波器输入数据;m_axis_data_tdata:滤波后输出数据。
三、完整 Verilog 源代码(模块化 + 全注释,可直接综合)
代码分层:按键消抖 → DAC 驱动 → 顶层主模块,所有模块独立,电赛可直接复用。
模块 1:按键消抖模块(key_debounce.v)
作用:消除机械按键 20ms 抖动,避免相位控制跳变(电赛必加)
verilog
// 按键消抖模块 消抖时间:20ms @100MHz
module key_debounce(
input wire clk,
input wire rst_n,
input wire key_in, // 原始按键输入
output reg key_out // 消抖后按键输出
);
reg [19:0] cnt;
reg key_sync0, key_sync1;
// 两级打拍同步(跨异步按键)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_sync0 <= 1'b1;
key_sync1 <= 1'b1;
end else begin
key_sync0 <= key_in;
key_sync1 <= key_sync0;
end
end
// 20ms 计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 20'd0;
else if(key_sync1 != key_out)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
end
// 消抖判决
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
key_out <= 1'b1;
else if(cnt == 20'd2000000) // 100MHz * 20ms = 2000000
key_out <= key_sync1;
end
endmodule
模块 2:TLV5618 双通道 DAC 驱动(tlv5618_dac.v)
作用:16bit 有符号数字信号 → 12bit 单极性模拟信号,SPI 时序驱动,添加直流偏移(防止削顶)
verilog
// TLV5618 双通道 12bit DAC 驱动
module tlv5618_dac(
input wire clk,
input wire rst_n,
input wire signed [15:0] din_ch1, // 通道1输入(16bit有符号)
input wire signed [15:0] din_ch2, // 通道2输入(16bit有符号)
output reg dac_sclk, // SPI时钟
output reg dac_sync, // SPI片选(低有效)
output reg dac_din // SPI数据
);
reg [3:0] bit_cnt;
reg [11:0] data_buf1, data_buf2;
reg [23:0] shift_reg;
// 有符号数转12bit无符号数:右移4位 + 直流偏移2048(12bit中点)
always @(*) begin
data_buf1 = din_ch1[15:4] + 12'd2048;
data_buf2 = din_ch2[15:4] + 12'd2048;
end
// SPI 串行发送逻辑
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
bit_cnt <= 4'd0;
dac_sclk <= 1'b0;
dac_sync <= 1'b1;
dac_din <= 1'b0;
shift_reg<= 24'd0;
end else begin
if(bit_cnt == 4'd0) begin
dac_sync <= 1'b0;
shift_reg <= {4'b0000, data_buf1, 4'b0000, data_buf2}; // 双通道拼接
bit_cnt <= bit_cnt + 1'b1;
end else if(bit_cnt <= 4'd24) begin
dac_sclk <= ~dac_sclk;
if(dac_sclk == 1'b1) begin
dac_din <= shift_reg[23];
shift_reg <= shift_reg << 1;
bit_cnt <= bit_cnt + 1'b1;
end
end else begin
dac_sync <= 1'b1;
bit_cnt <= 4'd0;
end
end
end
endmodule
模块 3:顶层主模块(top_signal_sep.v)【核心完整代码】
整合所有 IP、子模块,实现信号生成 + 叠加 + 滤波 + 相位控制 + DAC 输出全功能,严格保证加法器增益 = 1。
verilog
// 电赛信号叠加与分离 顶层模块
// 功能:DDS信号源 + 增益1加法器 + FIR滤波分离 + 相位控制 + DAC输出
module top_signal_sep(
// 外部硬件引脚
input wire clk_50m, // 外部50MHz晶振
input wire rst_n_ext, // 全局复位(低有效)
input wire key_phase_add,// 相位+按键
input wire key_phase_sub,// 相位-按键
// DAC 输出引脚
output wire dac_sclk,
output wire dac_sync,
output wire dac_din
);
// ====================== 1. 内部信号定义 ======================
// PLL 时钟信号
wire clk_100m; // 系统100MHz主时钟
wire pll_locked; // PLL锁定标志
wire sys_rst_n; // 系统同步复位
// 按键消抖后信号
wire key_add_deb;
wire key_sub_deb;
// DDS 信号(16bit 有符号正弦波)
wire signed [15:0] sig_a; // 50kHz 原始信号A
wire signed [15:0] sig_b; // 100kHz 原始信号B
// 相位控制字:0~32768 对应 0°~180°
reg [15:0] phase_offset;
localparam PHASE_STEP = 16'd910; // 5°步进字
localparam PHASE_MAX = 16'd32768; // 最大相位差180°
// DDS 相位输入(高16位=频率字,低16位=相位偏移)
wire [31:0] dds_phase_a;
wire [31:0] dds_phase_b;
localparam FTW_A = 16'd32768; // 50kHz 频率字
localparam FTW_B = 16'd65536; // 100kHz 频率字
// 数字加法器(增益=1,防溢出位宽扩展)
reg signed [16:0] sig_mix; // 17bit 混合信号 A+B
// FIR 滤波输出(分离后的两路信号)
wire signed [15:0] sig_a_filt; // 低通输出:50kHz
wire signed [15:0] sig_b_filt; // 高通输出:100kHz
// ====================== 2. 系统复位生成 ======================
assign sys_rst_n = rst_n_ext & pll_locked;
// ====================== 3. 相位控制逻辑(0~180°,5°步进) ======================
always @(posedge clk_100m or negedge sys_rst_n) begin
if(!sys_rst_n)
phase_offset <= 16'd0;
else begin
// 相位增加
if(key_add_deb && (phase_offset < PHASE_MAX))
phase_offset <= phase_offset + PHASE_STEP;
// 相位减少
else if(key_sub_deb && (phase_offset > 16'd0))
phase_offset <= phase_offset - PHASE_STEP;
end
end
// DDS 相位数据拼接
assign dds_phase_a = {FTW_A, 16'd0}; // A信号无相位偏移
assign dds_phase_b = {FTW_B, phase_offset}; // B信号叠加相位偏移
// ====================== 4. 数字加法器【核心:增益严格=1】 ======================
// 规则:纯相加、无移位/无乘除、仅位宽扩展防溢出
always @(posedge clk_100m or negedge sys_rst_n) begin
if(!sys_rst_n)
sig_mix <= 17'd0;
else
sig_mix <= sig_a + sig_b; // 增益=1,符合题目要求
end
// ====================== 5. 模块实例化 ======================
// 5.1 PLL 时钟倍频:50M → 100M
clk_wiz_0 u_pll(
.clk_in1(clk_50m),
.resetn(rst_n_ext),
.clk_out1(clk_100m),
.locked(pll_locked)
);
// 5.2 按键消抖(两路相位按键)
key_debounce u_key_add(
.clk(clk_100m),
.rst_n(sys_rst_n),
.key_in(key_phase_add),
.key_out(key_add_deb)
);
key_debounce u_key_sub(
.clk(clk_100m),
.rst_n(sys_rst_n),
.key_in(key_phase_sub),
.key_out(key_sub_deb)
);
// 5.3 DDS 信号源 A(50kHz)
dds_compiler_0 u_dds_a(
.aclk(clk_100m),
.s_axis_phase_tvalid(1'b1),
.s_axis_phase_tdata(dds_phase_a),
.m_axis_data_tdata(sig_a)
);
// 5.4 DDS 信号源 B(100kHz,带相位偏移)
dds_compiler_0 u_dds_b(
.aclk(clk_100m),
.s_axis_phase_tvalid(1'b1),
.s_axis_phase_tdata(dds_phase_b),
.m_axis_data_tdata(sig_b)
);
// 5.5 FIR 低通滤波器(分离50kHz)
fir_compiler_lpf u_fir_lpf(
.aclk(clk_100m),
.s_axis_data_tvalid(1'b1),
.s_axis_data_tdata(sig_mix),
.m_axis_data_tdata(sig_a_filt)
);
// 5.6 FIR 高通滤波器(分离100kHz)
fir_compiler_hpf u_fir_hpf(
.aclk(clk_100m),
.s_axis_data_tvalid(1'b1),
.s_axis_data_tdata(sig_mix),
.m_axis_data_tdata(sig_b_filt)
);
// 5.7 TLV5618 DAC 双通道输出(接示波器)
tlv5618_dac u_dac(
.clk(clk_100m),
.rst_n(sys_rst_n),
.din_ch1(sig_a_filt),
.din_ch2(sig_b_filt),
.dac_sclk(dac_sclk),
.dac_sync(dac_sync),
.dac_din(dac_din)
);
endmodule
四、引脚约束文件(top.xdc,Artix-7 通用)
根据你的开发板实际引脚修改,以下为电赛常用引脚分配:
xdc
# 1. 外部50MHz晶振
set_property PACKAGE_PIN E3 [get_ports clk_50m]
set_property IOSTANDARD LVCMOS33 [get_ports clk_50m]
# 2. 全局复位(低有效)
set_property PACKAGE_PIN C12 [get_ports rst_n_ext]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n_ext]
# 3. 相位控制按键
set_property PACKAGE_PIN D12 [get_ports key_phase_add]
set_property PACKAGE_PIN E12 [get_ports key_phase_sub]
set_property IOSTANDARD LVCMOS33 [get_ports key_phase_add]
set_property IOSTANDARD LVCMOS33 [get_ports key_phase_sub]
# 4. TLV5618 DAC SPI 引脚
set_property PACKAGE_PIN F12 [get_ports dac_sclk]
set_property PACKAGE_PIN G12 [get_ports dac_sync]
set_property PACKAGE_PIN H12 [get_ports dac_din]
set_property IOSTANDARD LVCMOS33 [get_ports {dac_sclk dac_sync dac_din}]
# 时钟时序约束(高速时钟必加)
create_clock -name clk_50m -period 20 [get_ports clk_50m]
五、核心参数校验 & 名词复盘(考点汇总)
1. 增益 = 1 校验(评分核心)
- 加法器代码:
sig_mix <= sig_a + sig_b,无移位、无乘除、无系数缩放; - 位宽从 16bit→17bit:仅为防溢出,不改变信号幅度,数字增益严格 = 1;
- 实测:单路输入信号,输出码值范围与输入完全一致。
2. 定点 Q 格式
- 全局格式:Q1.15 (1 位符号位 + 15 位小数),范围
[-1, 1); - 滤波器系数、DDS 输出统一该格式,量化误差极小。
3. 相位控制参数
- 总范围:
0~32768→ 相位差0°~180°; - 步进:910 → 单步 5°,满足题目分辨率要求;
- 两路 DDS 同源时钟,相位无漂移,误差 <2°(优于题目 5° 要求)。
4. 奈奎斯特采样定理
- 采样率 1MHz > 2×100kHz(最高信号频率),无混叠失真。
六、仿真 & 板级调试全流程
1. ModelSim 功能仿真
- 新建仿真工程,添加所有
.v文件 + 编译 IP; - 激励设置:
clk_50m=50MHz,rst_n_ext先拉低 10us 再拉高; - 观测信号:
sig_a/sig_b:两路正弦波频率正确;sig_mix:叠加波形,无溢出;sig_a_filt/sig_b_filt:滤波后单频信号,杂波被滤除;phase_offset:按键触发后步进变化,范围正常。
2. 板级调试(电赛现场步骤)
- 综合、实现、生成比特流,下载到 FPGA;
- 第一步:基础信号测试 示波器接 DAC 两路输出,观测 50kHz/100kHz 正弦波,峰峰值 ≥2V(满足≥1V 要求);
- 第二步:增益校验(评委必测)
- 屏蔽 B 信号:输出 50kHz,幅度不变 → 增益 = 1;
- 屏蔽 A 信号:输出 100kHz,幅度不变 → 增益 = 1;
- 第三步:信号分离测试 两路同时输出,分离后波形纯净,无另一频率杂波;
- 第四步:发挥部分(相位控制) 按按键切换相位差 0°/45°/90°/180°,示波器测量相位差,误差≤5°。
3. 常见故障 & 解决方案
表格
| 故障现象 | 原因 | 解决方法 |
|---|---|---|
| 加法器增益≠1,信号放大 / 衰减 | 代码存在移位 / 乘除 | 删除 <</>>/* 运算,仅保留纯加法 |
| 滤波后信号混杂另一频率 | 滤波器阻带衰减不足 / 采样率不匹配 | 重新运行 MATLAB,保证采样率 = 1MHz,阻带衰减≥60dB |
| DAC 波形削顶 | 直流偏移量不足 | 偏移值固定为 2048(12bit DAC 中点) |
| 相位差跳变、不稳定 | 按键无消抖 | 确认 key_debounce 模块正常工作 |
| 无波形输出 | PLL 未锁定 / DAC 引脚接反 | 检查晶振、复位、SPI 引脚接线 |
七、拓展功能(三角波适配,题目发挥项)
如需生成三角波,替换 DDS 模块为线性计数器即可,代码片段:
verilog
// 16bit 三角波生成(0~32767 递增 → 递减 循环)
reg [15:0] tri_cnt;
reg dir_flag; // 0:递增 1:递减
always @(posedge clk_100m or negedge sys_rst_n) begin
if(!sys_rst_n) begin
tri_cnt <= 16'd0;
dir_flag <= 1'b0;
end else begin
if(dir_flag == 1'b0) begin
tri_cnt <= tri_cnt + 1'b1;
if(tri_cnt == 16'd32767) dir_flag <= 1'b1;
end else begin
tri_cnt <= tri_cnt - 1'b1;
if(tri_cnt == 16'd0) dir_flag <= 1'b0;
end
end
end
将 tri_cnt 接入加法器,即可实现三角波叠加与分离。
