
简介面向FPGA数字信号处理学习者的DDS与ADDA自环测试工程围绕DDS IP数字波形合成、DAC输出与ADC回采验证展开适合已具备Verilog/VHDL基础、希望打通从波形生成到频谱分析链路的开发者。工程采用50MHz时钟驱动DAC输出正弦波将模拟输出自环至ADC输入并以MMCM分频得到25MHz采样时钟配合ILA采集不少于2048个样点再由Matlab完成频谱分析同时可通过VIO修改频率字生成1MHz与3MHz信号验证频谱结果。压缩包共613个文件、约46.11MB包含Verilog/VHDL源码与仿真文件、XDC约束、Tcl与批处理脚本、比特流、ILA/CSV采样数据及工程配置等兼顾综合实现与数据分析。已有321人学习可用于复现实验、核对DDS配置与ADC采样时序并参考AN108 34针插头安装位置等注意事项减少排错成本。1. DDS 驱动 ADDA 的闭环测试这套工程文件省掉的不是写代码的时间把 DAC 的输出用一根短线直接接回 ADC 的输入中间只留一级重建滤波器、一级抗混叠和必要的衰减网络就能用一套 DDS 把整条模拟链路的指标量出来。原理上只有三句话DDS 发正弦、DAC 打出去、ADC 收回来做 FFT。真正动手时多数人卡在“发的”和“收的”对不齐DAC 侧时钟怎么转发、ADC 侧数据位序怎么拼、抓到的 8192 点到底是不是完整周期。这份 DDS ADDA 测试工程文件价值就在把这条闭环固化成可复现的 RTL 加脚本。它适合两类人正在调 DDS 波形相位累加器、查找表、频率控制字的和要给一块自制采集板做首轮 SNR、ENOB 摸底的。下面按“先读懂 RTL再定参数最后排坑”的顺序拆。2. 拆工程目录DDS 相位累加器到 DAC 打拍的三级流水拿到这类工程最忌讳从第一个 .v 文件顺着往下读。DDS 加 ADDA 的工程文件通常按功能切得很干净波形生成、发送接口、接收缓存、上位机脚本四块剩下的是约束和测试记录。先把 top 层的连线看清楚再把信号从“数字域”到“模拟域”再到“数字域”的整条路径画在纸上后面所有参数才有落点。2.1 目录骨架先找 top再看四个子模块这类工程的文件组织大同小异常见结构如下表。你手里的工程文件如果目录名不同按用途对号入座即可不要被命名带偏。目录通常放什么阅读优先级rtl/top、dds_core、dac_if、adc_cap 四个模块第一优先先看 top 的端口rtl/lut/ 或 sim/正弦查找表 .hex/.coe 与生成脚本第二优先确认表点数和位宽constr/时钟约束、输出延迟约束、引脚分配第三优先出问题时回来看sw/上位机取数与 FFT 分析脚本第四优先决定你怎么看数据doc/测试记录表、频点与码值对照有空再看别当说明书读阅读顺序我一般这么走先看 top 里clk、rst_n、fcw这几个端口谁驱动谁确认 DDS 和 DAC 是不是同一个时钟源再看 dds_core 的位宽参数这决定了你的频率分辨率然后看 dac_if 里时钟是怎么送到管脚的这里是第一个翻车高发区最后看 adc_cap 的触发条件它决定了你抓到的是不是一段“干净”的数据。提示工程里如果同时存在仿真用的 .hex 和综合用的 .coe务必确认综合实际读的是哪一个。改了一个忘了另一个是最常见的“波形怎么都不对”的源头。2.2 相位累加器与查找表DDS 的数学怎么落成 RTLDDS 的核心就一个公式输出频率f_out FCW × f_clk / 2^N其中 N 是相位累加器位宽FCW 是频率控制字。它的直接推论是频率分辨率f_clk / 2^N——125 MHz 时钟配 32 位累加器分辨率约 0.029 Hz这个数字决定了你能不能“卡”到严格相干的频点上后面第 4 章会反复用到。工程里的 dds_core 一般长这样我按可综合的写法整理过module dds_core #( parameter PHASE_W 32, // 相位累加器位宽决定频率分辨率 parameter LUT_AW 12, // 查找表地址位宽决定相位截断杂散 parameter DATA_W 14 // 幅度位宽对齐 DAC 位宽 )( input wire clk, input wire rst_n, input wire [PHASE_W-1:0] fcw, // 频率控制字运行时可变 input wire [PHASE_W-1:0] pinc, // 相位偏移双通道正交/双音时用 output wire [DATA_W-1:0] dac_data, output wire dac_valid ); reg [PHASE_W-1:0] phase_acc; wire [PHASE_W-1:0] phase_sum phase_acc pinc; wire [LUT_AW-1:0] lut_addr phase_sum[PHASE_W-1 -: LUT_AW]; // 取高位做截断 reg [DATA_W-1:0] sine_lut [0:(1LUT_AW)-1]; reg [DATA_W-1:0] lut_q; reg [1:0] vld_sr; initial $readmemh(sine_lut.hex, sine_lut); // 综合前务必确认路径有效 always (posedge clk or negedge rst_n) begin if (!rst_n) phase_acc {PHASE_W{1b0}}; else phase_acc phase_sum; // 每拍累加一次溢出即一个周期 end always (posedge clk or negedge rst_n) begin if (!rst_n) begin lut_q {DATA_W{1b0}}; vld_sr 2b00; end else begin lut_q sine_lut[lut_addr]; // BRAM 读固定 1 拍延迟 vld_sr {vld_sr[0], 1b1}; // valid 跟着数据一起打拍 end end assign dac_data lut_q; assign dac_valid vld_sr[1]; // 与相位→地址→数据的总延迟对齐 endmodule这段代码里有三个必须理解的点。第一phase_sum[PHASE_W-1 -: LUT_AW]是取相位的高 LUT_AW 位做地址低位被丢掉这就是“相位截断”。经验上地址每多 1 bit截断引入的最坏杂散改善约 6 dB但同时 BRAM 用量翻倍12 bit 地址配 14 bit 数据在很多板子上是资源和指标折中的甜点。第二vld_sr打了两拍是因为从相位到 BRAM 输出一共两拍延迟如果你的 LUT 用组合读分布式 RAM实现valid 只打一拍就够多打一拍反而会让第一个数据点错位一格看上去就是“波形整体挪了一点”。第三pinc不是摆设做双音互调测试时用两路 DDS 各给一个 FCW把两路数据相加再送 DAC做 IQ 正交时让第二路pinc 2^(N-2)就是 90 度相移。参数怎么算举个例子f_clk 125 MHz、N 32、目标f_out 1 MHz则FCW 1e6 × 2^32 / 125e6 34359738.368取整 34359738实际输出 999999.98 Hz偏差 0.02 Hz。这个偏差看起来微不足道但当你要求严格相干采样时它就是频谱泄漏的根源。2.3 DAC 侧发送时钟转发、数据对齐和 valid 数拍DDS 算得再对DAC 接口接错了也是白搭。并行高速 DAC 的标准接法是“源同步”把内部时钟经 ODDR 转发到管脚作为 DAC 的采样时钟数据用另一组输出寄存器同时送出靠 PCB 上等长布线保证建立/保持。FPGA 内部千万不要让工具把时钟和数据走两条不同的路径出去否则偏斜随温度漂。// 时钟转发用 ODDR 把系统时钟送到管脚避免时钟走全局缓冲后偏斜失控 ODDR #( .DDR_CLK_EDGE(SAME_EDGE), .INIT(1b0), .SRTYPE(SYNC) ) u_dac_clk_fwd ( .Q (dac_clk_p), // 直接接 OBUFDS 或单端输出 .C (clk_dac), .CE(1b1), .D1(1b1), .D2(1b0), // 双沿都翻转得到与 clk_dac 同频的方波 .R (1b0), .S(1b0) );数据侧一般再打一拍输出寄存器让数据和转发时钟从同一个 IOB 出发# 输出方向按源同步写具体数值必须按 DAC 手册的建立/保持时间换算不能照抄 set_output_delay -clock [get_clocks dac_clk_tx] -max 1.2 [get_ports dac_data*] set_output_delay -clock [get_clocks dac_clk_tx] -min -0.8 [get_ports dac_data*]还有两个容易忽略的细节。一个是数据格式多数高速 DAC 吃的是偏移二进制offset binary而 DDS 里累加器自然产生的是二进制补码中间要么加半量程偏置要么翻转最高位少做这一步输出就是一个单极性半波看上去“频率对了但只有半个正弦”。另一个是更新率串行接口的 DAC 建立时间通常在微秒量级用它做 1 MHz 以上的正弦幅度会被压得很惨这不是 DDS 的问题是接口带宽的问题。3. 把波形收回来ADC 采样、触发抓数与上位机取数发送端通了接着是接收端。这一段是整个闭环里最容易被低估的部分很多人以为 ADC 只要时钟给上、数据接上就能出数实际上位序、眼图中心、跨时钟域这三件事里任何一件没处理你看到的频谱都会是一团糊。判断自己有没有做对有个简单标准——输入一个干净的正弦频谱上除了基波和已知谐波底噪应该是平的如果底噪有明显鼓包或者成对出现的小峰八成是采样侧的问题。3.1 ADC 接口与采样时钟先算抖动再谈位宽采样时钟的质量直接决定 SNR 上限公式是SNR_jitter -20log10(2π × f_in × t_jitter)。代入两个数感受一下输入 1 MHz、时钟抖动 1 ps理论上限约 104 dB输入 100 MHz、同样 1 ps上限掉到 64 dB。这条曲线说明一件事——评估一块 14 位 ADC理论 SNR 约 86 dB时如果你在 100 MHz 输入下测出 64 dB那不一定是板子坏了可能是时钟或信号源的抖动已经把你限死了。选测试频点时必须先做这个估算。并行 CMOS 输出的 ADC 一般随路送一个 DCO 时钟接收侧用 IDDR 在双沿采样再把两拍数据拼成 2×N 位// DCO 反相后作为采样时钟保证采在数据眼图中心 IDDR #( .DDR_CLK_EDGE(SAME_EDGE_PIPELINED), .INIT_Q1(1b0), .INIT_Q2(1b0), .SRTYPE(SYNC) ) u_adc_iddr ( .Q1(adc_d0), .Q2(adc_d1), // 同一时钟沿采到的两个相邻样本 .C (adc_dco_buf), .CE(1b1), .D (adc_data_in), .R(1b0), .S(1b0) );拼接时位序错了波形会“像被洗过一样”频谱上出现对称的镜像杂散这是血泪经验里最典型的一种。处理办法是打开工程里的 IDELAY 扫描逻辑或者手工改 tap 值把延迟从 0 扫到最大每档算一次 SNR取 SNR 最高那一档的中间值作为工作点——眼图中心找到了位序问题通常也一并暴露。串行 LVDS 输出的 ADC 则是另一套用 ISERDES 做串并转换注意 bitslip 对齐字边界帧头对不上时数据会整体循环移位表现是“波形对但幅度乱跳”。这类接口在工程里一般有独立的状态机先把 lock 信号拉高再开始采集。3.2 触发与深存储怎么保证抓到的 8192 点不是半截自由跑采集的问题在于你无法控制起点。做 FFT 虽然不要求从正弦的零点开始但要求这段数据是“连续、不丢点、不含复位毛刺”的。工程里通常的做法是内部用一块 8192×16 bit 的 BRAM 做深存储写入由 ADC 采样使能驱动触发条件可以是软件下发的一个脉冲也可以是电平跨越阈值预触发深度设 50%这样触发点前后的波形都能看到。跨时钟域是这一步的关键。ADC 数据在采样时钟域BRAM 和上传逻辑在系统时钟域中间必须过异步 FIFO。FIFO 深度不够、或者读侧在 FIFO 空的时候硬读结果就是数据里随机丢点——丢点在时域上几乎看不出来在频域上表现为底噪整体抬高 3 到 6 dB非常像“ADC 本身性能不行”。判断方法把一个已知频率的相干信号灌进去看基波峰值是否稳定如果峰值每次采集抖动超过 0.5 dB先查丢点别急着换芯片。存满之后停止写入、置一个 ready 标志等上位机把数据搬走再清标志重新开始。这个握手如果做成“边写边读”很容易读到半新半旧的两段数据拼在一起相位不连续频谱上会出现宽裙边。3.3 取数到上位机把 8192 点搬进 Python传输链路的选择取决于你的工程里已经有什么。常见的是 UART8192 点 × 2 字节 16 KB115200 波特率下 8N1 每字节占 10 bit满打满算要 1.4 秒换成 921600 就只要 0.18 秒。这个时间对调试体验影响很大——每次改参数等一秒半一天下来耐心就磨没了。有条件的话优先走 USB 或网口串口留给兜底。Python 侧收到的是裸二进制直接按 16 位小端解析然后进 FFTimport numpy as np FS 100e6 # ADC 实际采样率必须与你配置的时钟一致 N 8192 # 单次分析点数取 2 的幂 def load_samples(path): raw np.fromfile(path, dtypeu2) # 无符号 16 位小端 return raw.astype(np.float64) def tone_metrics(x, fsFS, harmonics5, half_lobe3): n len(x) x x - x.mean() # 去直流否则 DC 泄漏会污染邻近 bin w np.blackman(n) # 非严格相干时用窗压泄漏 spec np.abs(np.fft.rfft(x * w)) ** 2 k int(np.argmax(spec[1:]) 1) # 跳过 DC 找基波 lo, hi max(k - half_lobe, 1), k half_lobe psig spec[lo:hi 1].sum() # 基波主瓣功率 mask np.ones_like(spec, dtypebool) mask[0:2] False # DC 与 1 号 bin 不计入噪声 mask[lo:hi 1] False hpow 0.0 for h in range(2, harmonics 1): c int(round(k * h)) if c half_lobe len(spec): break hpow spec[c - half_lobe:c half_lobe 1].sum() mask[c - half_lobe:c half_lobe 1] False # 谐波单独统计 pnoise spec[mask].sum() snr 10 * np.log10(psig / pnoise) sinad 10 * np.log10(psig / (pnoise hpow)) sfdr 10 * np.log10(psig / spec[1:][mask[1:]].max()) enob (sinad - 1.76) / 6.02 return dict(bink, fk * fs / n, snrsnr, sinadsinad, sfdrsfdr, enobenob) if __name__ __main__: print(tone_metrics(load_samples(cap_8192.bin)))几个参数要盯住。half_lobe3表示基波主瓣按 ±3 个 bin 统计用 Blackman 窗时主瓣宽度约 6 个 bin取 3 正好覆盖一半如果你换成 Hann 窗可以收到 2。harmonics5是排除前 5 次谐波测 ADC 时一般排到 5 次足够测 DAC 的镜像时则要单独处理。mask[0:2] False把 DC 和 1 号 bin 排除掉是因为加了窗之后直流会向邻近泄漏不排的话低频噪声会被算高。注意加窗是为了在非相干条件下看趋势带窗算出来的 SNR 会偏低 1 到 2 dB。要报正式指标必须先让相干采样条件严格成立再把窗换成全 1 重算一遍。4. 参数怎么定频点、采样率、点数与窗函数的取值对照表同一块板子换一组测试参数SNR 能差出 10 dB 以上这是很多“指标对不上”争议的来源。所以动手之前先把频率控制字、相干条件、点数、窗函数这四件事一次性定死写进测试记录表后面所有数据都基于同一口径。4.1 频率控制字与实际频点先算清楚再谈指标DDS 的频率分辨率是f_clk / 2^N这意味着你想要的任何频点都会被量化到最近的格点上。做单音测试时这没什么问题但当你同时用 ADC 的采样率去卡相干条件时量化误差就变成了频谱泄漏。所以正确的顺序是先由 FCW 反算实际频点再拿这个实际频点去设计相干采样。CLK_DDS 125e6 # DDS/DAC 系统时钟 PHASE_W 32 # 相位累加器位宽 FS_ADC 100e6 # ADC 采样率 N_FFT 8192 # 分析点数 def fcw_for(f_target, clkCLK_DDS, wPHASE_W): fcw int(round(f_target * (1 w) / clk)) f_real fcw * clk / (1 w) # 量化后的真实频点 return fcw, f_real def coherent_pick(n_fftN_FFT, fsFS_ADC, m_min60): # 选一个与 n_fft 互质的 m落在合适频段减少谐波折叠到基波附近 for m in range(m_min, m_min 200): if np.gcd(m, n_fft) 1: yield m, m * fs / n_fft这里m和n_fft互质是相干采样的核心条件一个周期内采到的 N 个点刚好覆盖信号的整数个周期且采样点在相位上均匀铺开不重复。若m与n_fft有公因子采样点会在相位上“扎堆”等效于降低了有效分辨率。我一般会固定N_FFT 8192、在m60 到 260 之间扫挑一个既落在关心的频段、谐波又不会折回基波附近的。折回这件事很容易被忽略如果 3 次谐波的频率超过fs/2它会镜像回来落在某个位置万一正好压在基波或 2 次谐波上你算出来的 SINAD 就是错的。4.2 相干采样与窗函数频谱泄漏该由谁背理想情况下相干采样成立就不需要窗函数矩形窗的能量最集中SNR 也最准。但现实是 DDS 的 FCW 量化、ADC 时钟与 DDS 时钟不同源带来的微小频差、以及时钟本身的漂移都会让相干条件“差不多但不严格”。这时候泄漏就会发生基波能量向邻近 bin 扩散主瓣变宽噪声统计被污染。判断方法很直接把采集到的数据截两段各做 FFT比较基波峰值位置。如果两次的峰值落在不同 bin说明相干没锁住如果落在同一 bin 但主瓣明显比理论宽说明有频率漂移。前者换参数后者只能加窗。场景建议窗函数主瓣宽度bin代价严格相干用于报指标矩形窗2对频偏零容忍相干但有时钟漂移Hann4SNR 偏低约 1.5 dB非相干日常看趋势Blackman6SNR 偏低约 2 dB动态范围好小信号 大信号共存Blackman-Harris8主瓣最宽适合找杂散选窗的原则是你要看的是“底噪有多低”就用矩形或 Hann你要看的是“有没有小杂散”就用 Blackman 系列。两者不要混着报数更不要拿带窗的结果去和芯片手册的典型值直接对比。4.3 SNR、SINAD、ENOB、SFDR 的口径必须统一这四个指标经常被混用但它们回答的是不同问题。SNR 只算噪声把谐波也当成信号的一部分排除在外SINAD 把谐波算进“非理想成分”所以 SINAD 恒小于等于 SNR两者之差反映了谐波失真有多严重ENOB 由 SINAD 换算ENOB (SINAD - 1.76) / 6.02它是最常被引用也最容易被滥用的数字SFDR 是最差的那根杂散相对基波的高度跟噪声无关纯粹看有没有“尖峰”。实操上有个坑不同工具对“谐波排除窗口”的定义不同。有的按 ±1 bin有的按 ±5 bin同一组数据算出来 SINAD 能差 1 dB 以上。解决办法是在测试记录表里写清口径——主瓣 ±3 bin、谐波排到 5 次、DC 与 1 号 bin 排除、窗函数类型——后面所有数据都照这个来。这份工程文件里的 sw 目录通常就带这样一个配置头别嫌它啰嗦它是唯一能让三个月后的你复现出同样数字的东西。还有一点DAC 侧的指标和 ADC 侧要分开测。DAC 的关键是 SFDR 和通带平坦度因为重建滤波器和 sin(x)/x 滚降ADC 的关键是 SNR 和 ENOB。把两者混在一个闭环里测出来的数字既不是 DAC 的也不是 ADC 的只能叫“系统指标”对外报的时候必须说清楚。5. 联调避坑DDS 到 ADDA 闭环上最容易翻车的五个点下面这五条是我在这类工程上反复见过的。每条都按“现象、原因、解决”写遇到问题时可以直接对号入座。5.1 波形侧出不来、出歪了、幅度不对一、输出恒定在量程中点一点波形都没有。现象示波器上看 DAC 输出是一条平直的直流测出来正好是半量程电压。 原因查找表没被正确初始化。$readmemh的相对路径在仿真时有效、综合时失效或者综合工具把 BRAM 优化成了全零初值。此时地址怎么变读出来的都是 0再经过偏移二进制转换就成了中点。 解决检查综合日志里$readmemh是否被识别把 hex 文件转成 .coe 或用initial块加综合属性最稳的办法是加一个自检计数器上电后顺序读 16 个地址通过串口打印出来看是不是正弦的前 16 个点。二、频率完全正确幅度只有一半波形有明显台阶。现象频谱上基波频率分毫不差但幅度比预期低 6 dB时域看波形上下不对称。 原因位宽没对齐。DDS 输出 14 位DAC 也是 14 位但中间某处只接了 13 根线或者把补码直接当偏移二进制送了出去导致负半周被截掉。 解决逐级核对位宽和格式转换点补码转偏移二进制就是最高位取反或加半量程转换点只能有一处多了会翻两次等于没翻。三、低频正常频率一上去幅度就塌。现象1 MHz 时幅度正常10 MHz 时掉了 3 dB30 MHz 时几乎看不到波形。 原因三个可能——DAC 重建滤波器截止频率太低输出变压器或运放的压摆率不够DAC 更新率本身不足高频点每个周期只有几个采样点。另外 sin(x)/x 滚降在0.4×f_clk附近已经有约 2.7 dB 衰减这是物理规律不是故障。 解决先把滤波器旁路测一次排除滤波器因素再算 DAC 的建立时间是否够最后接受 sin(x)/x 的存在在软件侧做预补偿把 LUT 值乘上 1/sinc 的倒数或者干脆把工作频段限制在0.1×f_clk以内。5.2 数据侧抓不全、算不准四、抓回来的波形“像被洗过”SNR 只有 30 dB 左右。现象时域波形毛刺多、台阶乱频谱底噪整体抬高还伴随对称的镜像杂散。 原因IDDR 拼接位序反了或者采样时钟相位不对采在了数据跳变沿上。这两种情况的表现极为相似都是“数据大体对但细节全乱”。 解决先做静态测试——让 DDS 输出一个极低频的锯齿或直接让 DAC 输出递增码这样每次采样值应该是确定的位序错了会立刻看出规律性的跳变确认位序没问题后再扫 IDELAY按 SNR 曲线找眼图中心。这个流程走一遍大概二十分钟比盲猜快得多。五、ENOB 每次算都不一样前后能差 1 bit 以上。现象同一组硬件配置连续采十次ENOB 在 10.2 到 11.5 之间跳。 原因采集不是严格相干的每次触发点落在不同相位上加窗后残余泄漏量随机变化或者分析脚本里谐波排除窗口、DC 排除范围没固定也可能是丢点导致的随机底噪抬升。 解决固定三件事——采集点数必须是 2 的幂、触发后先检查数据连续性相邻点差分不应出现异常大跳变、分析脚本的参数写死在配置文件里。做完这三步重复性通常能收到 ±0.1 bit 以内。如果还跳回去查 FIFO 有没有溢出标志被忽略。提示把每次测试的 FCW、实际频点、采样率、点数、窗函数、脚本版本号一起记进 csv出问题时按时间倒查比翻聊天记录靠谱。6. 扫频与双音把工程的可信度从“能出波形”推到“指标可复现”单点测试通过只说明这套工程在那个频点上能跑。要判断它值不值得作为长期使用的测试平台还得做两件事扫频和双音。扫频的做法是把 FCW 从一个下限步进到上限每一步采一组数据、算一次 SNR 和基波幅度最后画两条曲线幅度平坦度和 SNR 随频率的变化。这两条曲线能暴露很多单点测试看不见的东西——重建滤波器的实际截止点、运放开始压摆的频点、以及 ADC 的 SNR 随输入频率下降的斜率是否和抖动估算吻合。我一般会取 1 MHz 到 40 MHz、每 1 MHz 一个点40 个点跑完大约五分钟得到的曲线比任何手册都贴合你手上这块板子。扫频时记得把每步的 FCW 和实际频点都存下来DDS 的量化会让实际频点略低于目标值画图时用实际值否则曲线会有细小的锯齿。双音测试用来量互调。做法是例化两路 DDSFCW 分别对应 f1 和 f2两路数据相加后送 DAC注意加完后要饱和或右移一位防止溢出FFT 里重点看 2f1-f2 和 2f2-f1 两个三阶产物相对基波的高度那就是 IMD3。两个频点的间隔有讲究太近了落在同一个主瓣里分不开太远了又可能被滤波器滚降影响幅度我一般取间隔为采样率的 1/40 左右并且让 f1、f2 都满足与采样率互质的前提。双音对 DAC 的要求比单音高得多很多在单音下 SFDR 漂亮的板子一上双音三阶产物就冒出来了这不是工程错了是它本来就该暴露。最后一个我踩过的坑扫频跑完发现 20 MHz 附近 SNR 突然掉 4 dB查了半天硬件最后发现是那个频点的 FCW 量化后刚好和采样率构成了近似整数比相干条件被破坏泄漏进了噪声统计。从那以后我每次做扫频之前都强制先用第 4 章那个脚本把每个频点的实际值和互质条件过一遍不满足就跳到下一个格点宁可曲线少一个点也不留一个假坑。希望帮到你。本文还有配套的精品资源点击获取