2023电赛H题|FPGA纯时域无FFT双频信号分离完整工程解析

发布时间:2026/6/29 23:13:31
2023电赛H题|FPGA纯时域无FFT双频信号分离完整工程解析 一、项目背景赛题需求2023 全国电赛 H 题为双频混合信号分离装置输入单路模拟混合信号C 低频A 高频B输出两路独立 DAC 分别还原出 A、B 波形且支持正弦 / 三角波可切换输出输出相位 0~180° 连续可调发挥题无失真分离高低频叠加波形常规方案STM32FFT频域分解、滤波、逆变换重构资源开销大、相位难控。本文方案FPGA 纯时域信号分离无 FFT、无浮点、全整数运算、稳定不跑频。二、整体核心原理全文最关键1. 为什么模拟电路要加直流偏置ADC 是单电源 0~3.3V 采集无法识别负压。而交流正弦波天然存在负半周-0.5V~0.5V。因此模拟运放强制抬升直流电平把波形整体抬高全部变为正数可采集。2. 为什么 FPGA 必须减掉直流偏置ADC 采到的数据 真实交流信号 人为直流偏置后端所有滤波、鉴相、相关运算必须以 0 为中点否则乘法相关直流饱和相位匹配失效无法区分正负半周所以adc.v 第一行核心代码i_data - DC_BIAS还原纯交流波形。3. 整套分离算法核心思想混合信号C 低频A 高频B低频A直接通过长窗口均值低通滤波滤除高频得到高频B使用外部同频DDS参考 时域最小误差锁相自动对齐相位提取全程时域处理不需要频域变换FPGA资源极小、极其稳定。三、整体工程架构Top 层级整套工程一共 8 个模块流水线结构adc.vADC 采集 去直流偏置la_filter.v128点滑动均值低通提取低频Asin_to_tri.v正弦波实时转三角波signal_mux.v正弦/三角波形二选一输出phase_adjust.v按键可调数字移相0~180°b_rescue.v自动锁相、相位搜索、高频B对齐核心lp_filter.v乘法鉴相低通判断相位是否对齐dac.vDAC 时序输出四、两条核心数据流重中之重数据流1低频信号 A 分离输出简单、稳定ADC混合信号 → 去直流 → 128点均值低通滤波 → 滤干净高频B → 得到纯低频A可选正弦转三角波 → 波形切换 → DAC1 输出原理长窗口均值滤波天然压制高频完美分离低频分量。数据流2高频信号 B 锁相分离输出核心难点外部输入纯净 DDS 高频B参考波 混合信号通过b_rescue 256级延迟线遍历所有相位计算左、中、右三点误差自动寻找误差最小相位点锁定相位后输出与混合信号内B分量完全同步的纯净波形 → DAC2输出同时支持按键手动微调相位满足赛题发挥要求。五、逐模块深度解析1. adc.v —— 去直流还原交流信号核心语句assign o_data i_data - DC_BIAS;作用模拟端人为叠加直流抬压适配ADC数字端减掉偏置还原正负对称交流波形所有后续算法必须依赖正负对称波形才能正常工作。2. la_filter.v —— 128点滑动均值低通滤波器很多同学以为只是“平滑降噪”实际是分离低频A的核心。原理128点超长窗口平均彻底抹平高频波动保留稳定低频波形内部采用三级加法树并行求和速度快、无溢出、无时序压力。输出混合信号中纯净低频A波形。3. sin_to_tri.v —— 正弦转三角波实现方式非常巧妙无ROM、无查表统计正弦波最大、最小值计算波形中点直流中轴线大于中点输出递增上坡小于中点输出递减下坡自动生成线性升降标准三角波。4. signal_mux.v —— 波形选择器1 行代码实现功能切换en0输出原始正弦波en1输出转换后的三角波5. phase_adjust.v —— 全数字可调移相发挥题核心内部 365 级深度移位寄存器延迟线按键 UP/DOWN 调整读取索引改变波形延迟时间 改变相位支持 0~180° 连续可调自带按键消抖、边沿检测不抖动、不连跳纯数字移相无温漂、无误差、精度极高。6. b_rescue.v —— 全文最核心自动锁相相位搜索功能自动找到 DDS 参考波与混合信号中高频分量的最佳匹配相位原理256级移位缓存存储所有相位延迟版本的DDS波形每次计算左移1、当前、右移1 三处误差绝对值对比误差大小自动向误差更小的方向移动相位最终收敛到全局最小误差点——相位完全对齐只要相位对齐即可完美分离出高频B信号。7. lp_filter.v —— 乘法鉴相器同步解调经典原理同频信号相乘 → 产生直流分量异频/相位不对齐 → 只有交流、无直流内部乘法32点均值滤波提取直流能量判断是否锁相成功。8. dac.v —— 高速DA输出统一DAC时序将数字波形转为模拟波形输出到示波器。六、整体工作流程极简总结ADC 采集混合波形FPGA 去除模拟直流偏置恢复纯交流信号长均值滤波提取低频A直接输出支持正弦/三角切换外部DDS提供纯净高频参考波phase_adjust 支持人工微调相位两路DAC分别输出分离后的 A、B 波形七、方案优势为什么这套代码能拿高分无 FFT、无浮点资源占用极低稳定不爆错纯时域算法延迟低、实时性强自动锁相手动移相基础题发挥题全部拉满自适应波形转换自动识别波峰波谷适配任意幅度输入多级防抖、阈值滤波抗干扰极强赛场上波形极其干净八、适用赛题完全适配2023电赛H题 信号分离装置可直接工程复现、可直接答辩、可直接跑分。九、完整工程模块清单top.v、adc.v、dac.v、la_filter.v、sin_to_tri.v、signal_mux.v、phase_adjust.v、b_rescue.v、lp_filter.vmodule adc( input clk, input [9:0] i_data, output [9:0] o_data, output adc_clk, output oe ); parameter DC_BIAS 10d380; wire adc_clk_oddr; assign adc_clk_oddr~clk; assign oe 1b0; ODDR2 #( .DDR_ALIGNMENT(NONE), // Sets output alignment to NONE, C0 or C1 .INIT(1b0), // Sets initial state of the Q output to 1b0 or 1b1 .SRTYPE(SYNC) // Specifies SYNC or ASYNC set/reset ) ODDR2_inst ( .Q(adc_clk), // 1-bit DDR output data .C0(adc_clk_oddr), // 1-bit clock input .C1(~adc_clk_oddr), // 1-bit clock input .CE(1b1), // 1-bit clock enable input .D0(1b1), // 1-bit data input (associated with C0) .D1(1b0), // 1-bit data input (associated with C1) .R(1b0), // 1-bit reset input .S(1b0) // 1-bit set input ); assign o_data i_data -DC_BIAS; endmodulemodule b_rescue( input clk, input sys_rst_n, input [9:0] rv_signal, input [9:0] input_signalA, input [9:0] dds_input, output [9:0] dds_shift ); reg [9:0] dds_input_shift [255:0]; reg [7:0] shift_reg; reg [7:0] tim_delay; wire signed [15:0] R_error; wire signed [15:0] N_error; wire signed [15:0] L_error; wire [15:0] AbsR_error; wire [15:0] AbsN_error; wire [15:0] AbsL_error; assign R_error dds_input_shift[shift_regd1]rv_signal -input_signalA; assign L_error dds_input_shift[shift_reg-d1] rv_signal -input_signalA; assign AbsR_errorR_error[15] 1b1 ?~R_error1b1:R_error; assign AbsN_errorN_error[15] 1b1 ?~N_error1b1:N_error; assign AbsL_errorL_error[15] 1b1 ?~L_error1b1:L_error; localparam THOLD d11; //Adjustable .. integer reg_index; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin for(reg_index 0;reg_index 256;reg_index reg_index 1) begin dds_input_shift[reg_index] d0; end end else begin dds_input_shift[0] dds_input; for(reg_index 0;reg_index 255;reg_index reg_index 1) begin dds_input_shift[reg_index 1] dds_input_shift[reg_index]; end end //Time_delay for 128_clks always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) tim_delay d0; else if(tim_delay !240) begin tim_delay tim_delay 1b1; end else begin tim_delay tim_delay; end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) shift_reg 8h00; else if(tim_delay 240) begin if(AbsR_error AbsN_error AbsN_error -AbsR_error THOLD) shift_reg shift_reg d1; else if(AbsL_error AbsN_error AbsN_error -AbsL_error THOLD) shift_reg shift_reg -d1; else begin shift_reg shift_reg; end end else begin shift_reg 8h00; end assign dds_shift dds_input_shift[shift_reg]; endmodule//---------------------------------------------------------------------------------------------------------- // FILE: dac.v // AUTHOR: Biggest_apple // // ABSTRACT: // KEYWORDS: fpga, basic modulesignal process // // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.25 create // Adding:DC_BIAS parameter //----------------------------------------------------------------------------------------------------------- module dac( input clk, input [9:0] i_data, output [9:0] o_data, output dac_clk ); wire dac_clk_oddr; assign dac_clk_oddr~clk; ODDR2 #( .DDR_ALIGNMENT(NONE), // Sets output alignment to NONE, C0 or C1 .INIT(1b0), // Sets initial state of the Q output to 1b0 or 1b1 .SRTYPE(SYNC) // Specifies SYNC or ASYNC set/reset ) ODDR2_inst ( .Q(dac_clk), // 1-bit DDR output data .C0(dac_clk_oddr), // 1-bit clock input .C1(~dac_clk_oddr), // 1-bit clock input .CE(1b1), // 1-bit clock enable input .D0(1b1), // 1-bit data input (associated with C0) .D1(1b0), // 1-bit data input (associated with C1) .R(1b0), // 1-bit reset input .S(1b0) // 1-bit set input ); assign o_data i_data; endmodule//---------------------------------------------------------------------------------------------------------- // FILE: La_filter.v // AUTHOR: Biggest_apple // // ABSTRACT: // KEYWORDS: fpga, basic modulesignal process // // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.25 create // Final_Tb //----------------------------------------------------------------------------------------------------------- module la_filter( input clk, input sys_rst_n, //This is a test project not for any real use input [9:0] input_signal, output [9:0] sigmaS_out, output [63:0] sum ); reg [9:0] xi[0:127]; reg [63:0] sum; reg [31:0] sum128_to_16[0:15]; reg [31:0] sum16_to_4[0:3]; reg [3:0] delay_cnt; //Adding trees delay integer reg_index; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin for(reg_index 0;reg_index 128;reg_index reg_index 1) begin xi[reg_index] d0; end delay_cnt 3d0; end else begin if(delay_cnt 3d2) begin delay_cnt 3d0; //Ultra_long shift_reg xi[0] input_signal; for(reg_index 0;reg_index 127;reg_index reg_index 1) begin xi[reg_index 1] xi[reg_index]; end end else delay_cnt delay_cnt 1b1; end always (posedge clk or negedge sys_rst_n) //Adding trees construction if(!sys_rst_n) begin for(reg_index 0;reg_index 16;reg_index reg_index 1) begin sum128_to_16[reg_index] d0; end end else begin for(reg_index 0;reg_index 16;reg_index reg_index 1) begin sum128_to_16[reg_index] xi[reg_index*8]xi[reg_index*81]xi[reg_index*82]xi[reg_index*83] xi[reg_index*84]xi[reg_index*85]xi[reg_index*86]xi[reg_index*87]; end end always (posedge clk or negedge sys_rst_n) //Final adding trees if(!sys_rst_n) begin for(reg_index 0;reg_index 4;reg_index reg_index 1) begin sum16_to_4[reg_index] d0; end end else begin for(reg_index 0;reg_index 4;reg_index reg_index 1) begin sum16_to_4[reg_index] sum128_to_16[4*reg_index] sum128_to_16[4*reg_index1] sum128_to_16[4*reg_index2] sum128_to_16[4*reg_index3]; end end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) sum d0; else sum sum16_to_4[0]sum16_to_4[1]sum16_to_4[2]sum16_to_4[3]; assign sigmaS_out sum[10:1]; //Make the code feasible is the key --Adjustable endmodule//---------------------------------------------------------------------------------------------------------- // FILE: Lp_filter.v // AUTHOR: Biggest_apple // // ABSTRACT: // KEYWORDS: fpga, basic modulesignal process // // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.8.6 create // Final_Tb //----------------------------------------------------------------------------------------------------------- module lp_filter( input clk, input sys_rst_n, //This is a test project not for any real use input signed [9:0] input_Refsignal, input signed [9:0] input_Sigsignal, output adjust ); //Pll error_up module --By IP core wire signed [19:0] o_p_multi; multix10b multix10b_dut( .clk (clk), .a (input_Refsignal), .b (input_Sigsignal), .p (o_p_multi) ); localparam THOLD_AD 32h00ff_1111; //Unsigned... //Adding these vector --32_stages signed reg [19:0] xi [31:0] ; signed reg [27:0] sum32_to_4 [3:0] ; signed reg [31:0] sum4_to_1 ; unsigned wire sum4_to_1_abs; reg [3:0] delay_cnt; //Adding trees delay integer reg_index; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin for(reg_index 0;reg_index 32;reg_index reg_index 1) begin xi[reg_index] d0; end delay_cnt 4d0; //Adjustable end else begin if(delay_cnt 4d1) begin delay_cnt 4d0; //Ultra_long shift_reg xi[0] o_p_multi; for(reg_index 0;reg_index 31;reg_index reg_index 1) begin xi[reg_index 1] xi[reg_index]; end end else delay_cnt delay_cnt 1b1; end //Low_pass filters always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin sum32_to_4[0] d0; sum32_to_4[1] d0; sum32_to_4[2] d0; sum32_to_4[3] d0; end else begin sum32_to_4[0] xi[0] xi[1] xi[2] xi[3] xi[4] xi[5] xi[6] xi[7]; sum32_to_4[1] xi[8] xi[9] xi[10] xi[11] xi[12] xi[13] xi[14] xi[15]; sum32_to_4[2] xi[16] xi[17] xi[18] xi[19] xi[20] xi[21] xi[22] xi[23]; sum32_to_4[3] xi[24] xi[25] xi[26] xi[27] xi[28] xi[29] xi[30] xi[31]; end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin sum4_to_1 d0; end else begin sum4_to_1 sum32_to_4[0] sum32_to_4[1] sum32_to_4[2] sum32_to_4[3]; end assign sum4_to_1_abs (sum4_to_1[31] 1b0) ?sum4_to_1 :~sum4_to_11b1; assign adjust (sum4_to_1_absTHOLD_AD); endmodule//---------------------------------------------------------------------------------------------------------- // FILE: phase_adjust.v // AUTHOR: Biggest_apple // // ABSTRACT: // Warning: // DESCRIBE: This is testbench file that will generater tri_wave in 8-bits formula // KEYWORDS: fpga, basic module, DSP.... // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.12 create //----------------------------------------------------------------------------------------------------------- module phase_adjust( input clk, input sys_rst_n, input [9:0] input_signal, output [9:0] output_signal, input butt_phase_down, input butt_phase_up, input butt_phase_clr //Clear the register contents ); wire butt_phase_down_o; wire butt_phase_up_o; reg butt_phase_up_1,butt_phase_up_2; reg butt_phase_down_1,butt_phase_down_2; reg [8:0] OUTPHASE; parameter PHASEFACTER 16d365; integer index; reg [9:0] signal_shiftR[PHASEFACTER-1:0]; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin for(index 0;index PHASEFACTER;index index 1) begin signal_shiftR[index] d0; end end else begin signal_shiftR[0] input_signal; for(index 0;index PHASEFACTER -1;index index 1) begin signal_shiftR[index 1] signal_shiftR[index]; end end assign output_signal signal_shiftR[OUTPHASE-1]; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin OUTPHASE d256; end else begin if(~butt_phase_down_o butt_phase_up_o !butt_phase_clr) OUTPHASE OUTPHASE d2; else if(butt_phase_down_o ~butt_phase_up_o !butt_phase_clr) OUTPHASE OUTPHASE -d2; else if (butt_phase_clr) OUTPHASE d256; else begin OUTPHASE OUTPHASE; end end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n)begin butt_phase_down_1 1b0; butt_phase_down_2 1b0; butt_phase_up_1 1b0; butt_phase_up_2 1b0; end else begin butt_phase_down_1 butt_phase_down; butt_phase_down_2 butt_phase_down_1; butt_phase_up_1 butt_phase_up; butt_phase_up_2 butt_phase_up_1; end assign butt_phase_down_o (butt_phase_down_2 ~butt_phase_down_1); assign butt_phase_up_o (butt_phase_up_2 ~butt_phase_up_1); endmodule//---------------------------------------------------------------------------------------------------------- // FILE: signal_mux.v // AUTHOR: Biggest_apple // // ABSTRACT: // Warning: // DESCRIBE: This is testbench file that will generater tri_wave in 8-bits formula // KEYWORDS: fpga, basic module, DSP.... // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.12 create //----------------------------------------------------------------------------------------------------------- module signal_mux( input en, input [9:0] input_signalA, input [9:0] input_signalB, output [9:0] output_signal ); assign output_signal (en 1b0) ?input_signalA:input_signalB; endmodule//---------------------------------------------------------------------------------------------------------- // FILE: sin_to_tri.v // AUTHOR: Biggest_apple // // ABSTRACT: // Warning: // DESCRIBE: This is testbench file that will generater tri_wave in 8-bits formula // KEYWORDS: fpga, basic module, DSP.... // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.12 create //----------------------------------------------------------------------------------------------------------- module sin_to_tri( input clk, input sys_rst_n, input [9:0] input_signal, output reg [9:0] o_signal, output [9:0] mean_sum ); reg [9:0] input_signalA; reg [9:0] input_signalN; reg [9:0] input_signalF; reg [9:0] max_e; reg [9:0] min_e; reg [9:0] st_dy; reg [9:0] end_dy; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin input_signalA d0; input_signalN d0; input_signalF d0; end else begin input_signalF input_signal; input_signalN input_signalF; input_signalA input_signalN; end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) begin max_e d0; min_e d0; end else if(st_dy 10d512) begin if(end_dy !10d1022) end_dy end_dy 1b1; else end_dy end_dy; if((input_signalN input_signalF || input_signalN input_signalA )max_e input_signalN end_dy !10d1022) max_e input_signalN; else if((input_signalNinput_signalF || input_signalNinput_signalA )min_e input_signalN end_dy !10d1022) min_e input_signalN; else begin max_e max_e; min_e min_e; end end else begin max_e max_e; min_e min_e; end always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) st_dy d0; else if(st_dy !10d512) st_dy st_dy 1b1; else st_dy st_dy; assign mean_sum (max_e min_e) 1; always (posedge clk or negedge sys_rst_n) if(!sys_rst_n) o_signal d0; else if(input_signal mean_sum) o_signal o_signal 1b1; else if(input_signal mean_sum) o_signal o_signal -1b1; else o_signal o_signal; endmoduletimescale 1ns/1ps //---------------------------------------------------------------------------------------------------------- // FILE: top.v // AUTHOR: Biggest_apple // // ABSTRACT: // KEYWORDS: fpga, basic modulesignal process // // MODIFICATION HISTORY: // $Log$ // Biggest_apple 2023.7.25 create // 2023.8.5 update:Standard naming... //----------------------------------------------------------------------------------------------------------- module top( input sys_clk, //This signal is created by DDS module input sys_rst_n, input [9:0] i_adc, output oe, output adc_clk, input [9:0] i_adc_dds, output oe_dds, output adc_dds_clk, output dac_clk, output [9:0] o_dac, output dacP_clk, output [9:0] o_dacP, //input en_tri, //Enable the tri_wave output input phaseUpA, input phaseDownA //input phaseClrA ); wire en_tri; wire phaseClrA; assign en_tri 1b0; assign phaseClrA 1b0; wire [9:0] adc_data; wire [9:0] adc_dds_data; wire [9:0] dac_data; wire [9:0] la_filter_data; wire [9:0] sin_to_tri_data; wire [9:0] dacP_data; wire [9:0] dacPMM_data; (*KEEPTRUE*) wire [63:0] sum; (*KEEPTRUE*) wire [9:0] mean_sum; adc adc_dutO( .clk (sys_clk), .i_data (i_adc), .o_data (adc_data), .adc_clk (adc_clk), .oe (oe) ); adc adc_dutDds( .clk (sys_clk), .i_data (i_adc_dds), .o_data (adc_dds_data), .adc_clk (adc_dds_clk), .oe (oe_dds) ); dac dac_dut( .clk (sys_clk), .i_data (dac_data), .o_data (o_dac), .dac_clk (dac_clk) ); la_filter la_filter_dut( .clk (sys_clk), .sys_rst_n (sys_rst_n), //This is a test project not for any real use .input_signal (adc_data), .sigmaS_out (la_filter_data), .sum (sum) ); dac dac_dutPhase( .clk (sys_clk), .i_data (dacPMM_data), .o_data (o_dacP), .dac_clk (dacP_clk) ); phase_adjust phase_adjust_dut( .clk (sys_clk), .sys_rst_n (sys_rst_n), .input_signal (adc_data), .output_signal (dacP_data), .butt_phase_down(phaseUpA), .butt_phase_up (phaseDownA), .butt_phase_clr (phaseClrA) ); sin_to_tri sin_to_tri_dut( .clk (sys_clk), .sys_rst_n (sys_rst_n), .input_signal (la_filter_data), .o_signal (sin_to_tri_data), .mean_sum (mean_sum) ); //The stream signal selected... signal_mux signal_mux_dut( .en (en_tri), .input_signalA (la_filter_data), .input_signalB (sin_to_tri_data), .output_signal (dac_data) ); b_rescue b_rescue_dut( .clk (sys_clk), .sys_rst_n (sys_rst_n), .rv_signal (la_filter_data), .input_signalA (dacP_data), .dds_input (adc_dds_data), .dds_shift (dacPMM_data) ); endmodule