简介:这是一份2023年电赛H题的STM32F4完整代码工程,适合正在备赛电赛、学习信号分离与波形同步输出的电子类学生和项目开发人员。题目要求输入信号A、B经加法器叠加成C,由单片机对C做FFT频域分析提取两路正弦波频率,再通过AD9833 DDS模块输出A'、B';针对输出与原信号相差0.1Hz即发生相对漂移的同频稳定难题,给出了将晶振由25M改为1M的调频精度提升方案。下载包内共136个文件,约11.07MB;其中69个.h头文件和56个.c源文件组成核心代码,覆盖外设驱动、液晶显示、FFT处理、DDS控制等模块,另有工程配置文件、启动文件、固件及说明文档,可直接编译或烧录。已有685人学习下载。除完整源码外,还包含实际调试中应对晶振温漂的参数调整思路,工程模块划分清晰,便于复现“采样—FFT分离—DDS同步输出”的完整赛题流程,适合电赛备赛与信号处理方向学习时参考。
1. 2023年电赛H题代码:难点不在FFT,而在让FFT输出稳定可信的数字
2023年电赛H题代码,表面上是写一套能跑的FFT测量程序,实际上不少队伍把FFT一调通就以为完事了,接上信号发生器一测,读数不是跳来跳去,就是和实验室失真度仪对不上。这道题的对象是1kHz到100kHz范围内的周期信号,要求测出基波、2到5次谐波的幅度,再给出总谐波失真和信号失真度,真正卡的从来不是“会不会调用FFT库”,而是采样率怎么定、采样点数怎么选、加什么窗、谱峰怎么找、两个失真度定义用哪一个。
写代码不难,难的是一帧FFT算出来的数字能稳定、可重复、经得起外部仪器比对。我按做这类测量题的惯用思路,把整套代码从ADC采样配置到THD计算完整拆一遍,讲清楚每个参数怎么定、每一步在做什么、哪些位置最容易翻车。这篇文章适合正在备赛的本科生,也适合做嵌入式信号测量但被频谱分析折腾过的人。
2. 测控链路怎么搭:自动量程、抗混叠与采样策略
2.1 前端信号调理:自动量程和滤波一个都不能省
H题输入信号的幅度范围往往跨了两个数量级,小信号只有几十毫伏,大信号接近满量程。如果只用一个固定增益,小信号时ADC量化噪声直接吃掉谐波,大信号时又削顶产生一堆假谐波。常见做法是两级程控增益:前级用模拟开关或数字电位器切换反馈电阻,把信号放大到ADC量程的八成左右,后级再做二阶有源低通。以常见M4内核单片机配12位ADC、0到3.3V输入为例,我会把信号先抬一个1.65V的直流偏置,这样单极性ADC也能采双极性信号。
抗混叠滤波器的截止频率要按“分析最高频率”来定。题目最高基波100kHz,要看到5次谐波就是500kHz,所以低通截止频率取600kHz到800kHz比较稳妥,低于奈奎斯特频率但高于最高分析频率。注意这里有个取舍:低频档采样率如果降到128kHz,500kHz处的噪声是会折返到低频段的,但被测信号的2到5次谐波此时最高只有50kHz,折返来的高频噪声能量通常不大,配合后面加窗和多次平均,对读数影响有限。真要较真,就在低频档测量时只分析5次谐波以内,别去看高频谱线。
增益切换后的建立时间也容易被忽视。运放反馈网络里只要有电容,切换瞬间就会有一个毛刺,这个毛刺会被当成信号采进去。我一般会在量程切换后加一个1到5毫秒的延时,再清一次DMA缓冲重新采集,宁肯慢一点也不要第一帧数据带毛刺。下表是常用的参数组合,实际以你手里前级运放的增益带宽积为准:
| 参数 | 低频档 | 高频档 |
|---|---|---|
| 信号频率范围 | 1kHz ~ 10kHz | 10kHz ~ 100kHz |
| ADC采样率 | 128kHz | 2MHz |
| FFT点数 | 2048 | 2048 |
| 抗混叠低通截止 | 600kHz(共用) | 600kHz(共用) |
| 单帧采集时长 | 16ms | 1.024ms |
2.2 采样率、FT点数和周期数:先把公式摆清楚再写代码
采样率选择的第一原则是保证5次谐波在奈奎斯特频率以内。100kHz信号的5次谐波是500kHz,所以采样率必须大于1MHz,我取2MHz是留了大约两倍裕量。低频段则相反,如果始终用2MHz采1kHz信号,2048点只有大约1.3个信号周期,FFT分辨率太粗,谐波谱线全糊在一起。所以采样率必须分档,这是H题测量精度的第一步。
接下来是FFT点数N和信号周期数M的关系。设信号频率f0、采样率fs,如果采样N个点正好覆盖M个完整信号周期,则满足N / fs = M / f0,也就是N = M * fs / f0。当f0和fs满足这个关系时,FFT的频谱泄漏最小,基波和各次谐波正好落在整数谱线上,幅度测量最干净。实际中f0是未知的,所以流程要先粗测频率,再决定采样参数。
我先用固定采样率和固定点数采一帧,做一次粗略的基波频率估计,然后按两档采样率重新采样。低频档fs=128kHz、N=2048时频率分辨率只有62.5Hz,1kHz信号落在第16条谱线附近,5kHz信号落在第80条谱线附近,都在很靠前的位置,不会被低频噪声干扰。高频档fs=2MHz、N=2048时分辨率约976.6Hz,100kHz信号大约在第102条谱线,5次谐波在第512条谱线处。这个分辨率对单根谱线的谐波测量够用,但前提是基波频率不能落在两条谱线正中间,否则要用窗函数和插值兜底。
很多人一上来就用4096点甚至8192点,觉得点数越大越准。实际在12位ADC、信号本身就有噪声的前提下,2048点已经能把谱线看得比较干净,点数翻倍只会让FFT时间变长,对精度的提升远不如把采样率档位和窗函数选好。我的习惯是先固定N=2048,把精力放在频率粗测和谱峰提取上。
2.3 主控与DSP库:M4内核加CMSIS-DSP是稳妥组合
做FFT测量,主控要满足三个条件:有硬件浮点、主频够高、内存能装下复数缓冲。常见的M4内核单片机主频跑到168MHz甚至240MHz,2048点复数FFT单次大约1到2毫秒,完全够用;内存方面,2048点复数float缓冲占16KB,再加窗函数表和幅度谱数组,总共50KB以内,大部分M4芯片都能扛住。
我建议直接用CMSIS-DSP里的arm_cfft_f32,不要自己写基2或基4FFT。比赛现场时间紧张,手写FFT的位反转和旋转因子表一旦出错,排查起来非常痛苦,而库函数经过大量验证,性能也足够。唯一要注意的是arm_cfft_f32需要传入交织的复数数组,也就是实部、虚部交替存放,实数信号要把虚部全部填0。另一个容易踩的坑是N变化后要重新调用初始化函数arm_cfft_init_f32,不然实例里的旋转因子表还是旧点数的,算出来的频谱完全是乱的。
3. 从采样配置到谱峰提取:可复现的代码主线
3.1 定时器触发ADC与DMA缓冲:先让数据稳定进内存
这一步的目标是让ADC在定时器的驱动下匀速采样,采完的数据通过DMA搬进内存,不占用CPU。下面这段初始化代码按常见HAL风格写,以低频档fs=128kHz为例:
// 定时器触发ADC采样,目标采样率128kHz // 假设定时器时钟80MHz,PSC=79得到1MHz计数频率 TIM_HandleTypeDef htim2; htim2.Instance = TIM2; htim2.Init.Prescaler = 79; // 80MHz / 80 = 1MHz htim2.Init.Period = 7; // 1MHz / (7+1) = 125kHz,接近目标 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim2); // 让定时器的TRGO输出更新事件,作为ADC的外部触发源 TIM_MasterConfigTypeDef sMasterConfig = {0}; sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig); // ADC1配置为外部触发,单通道规则组 ADC_HandleTypeDef hadc1; hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.DMAContinuousRequests = DISABLE; HAL_ADC_Init(&hadc1); // 启动定时器和ADC的DMA采集,N_ADC是当前档位的采样点数 HAL_TIM_PWM_Start(&htim2); HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, N_ADC);这里的关键参数是Prescaler和Period,两者共同决定采样率,精确值等于定时器时钟除以(PSC+1)*(ARR+1)。算出来是125kHz而不是128kHz没关系,只要代码里所有用到采样率的常量跟着改就行,我习惯用一个宏统一管理。DMAContinuousRequests设置为DISABLE,这样每采满一帧就停一次,方便在DMA中断里做频率粗测和FFT。
DMA中断里要做什么?先判断传输完成标志,再翻转一个全局标志通知主循环处理数据。注意不要直接在中断里做FFT,2048点FFT耗时超过1毫秒,会拉长中断时间,还是放到主循环里处理更稳。
3.2 过零检测粗测基波频率:整周期采样的前提
在重新配置采样参数之前,必须先知道信号的大致频率。常见做法是过零检测:先减掉直流分量,再统计一帧内上升沿过零的次数和间隔,平均出信号周期。直流分量不除掉的话,信号整体偏移会导致过零误判,所以第一步先算整帧均值。
// 粗测基波频率:上升沿过零检测,多周期取平均 float dc = 0.0f; for (int i = 0; i < N_ADC; i++) { dc += adc_buf_f[i]; } dc /= N_ADC; int zc_count = 0; // 上升过零次数 uint32_t last_zc = 0; // 上一次过零位置 uint32_t sum_period = 0; // 过零间隔累加 for (int i = 1; i < N_ADC; i++) { if ((adc_buf_f[i-1] - dc) <= 0.0f && (adc_buf_f[i] - dc) > 0.0f) { zc_count++; if (zc_count > 1) { sum_period += (i - last_zc); } last_zc = i; } } if (zc_count > 1) { float avg_period = (float)sum_period / (zc_count - 1); // 每周期平均采样数 float f0 = fs_current / avg_period; // 粗测频率 }过零检测的精度受噪声影响很大,信号本身如果有毛刺,可能在一个周期内触发多次过零。所以代码里只统计“相邻两个采样点从负到正”的跳变,这样对窄毛刺有一定免疫力。如果信号噪声实在大,可以在过零检测前先做一个简单的滑动平均滤波,代价是相位会偏移一点,对频率测量影响不大。
avg_period是浮点数,它的小数部分反映了真实频率和FFT网格之间的偏差。比如低频档1kHz信号,平均每个周期128个采样点,则f0为125kHz/128约976.6Hz,和真实1kHz差约2%,这个粗测值已经足够用来确定采样档位,但远不足以直接当精确频率用。
3.3 重新采样加窗,再做FFT:把时域信号变成频谱
拿到粗测频率f0后,先判断落在哪个档位,然后重算采样点数并重新启动DMA。这里我不追求严格整周期采样,而是固定N=2048,先算当前f0对应的周期数M = N * f0 / fs,然后加汉宁窗压制频谱泄漏。汉宁窗的主瓣比矩形窗宽,但旁瓣衰减快得多,对谐波这种幅度差异很大的信号来说,选汉宁窗是稳妥的。
// 根据粗测频率决定采样档位 if (f0 < 10000.0f) { set_sampling_rate(LOW_FREQ_FS); // 125kHz } else { set_sampling_rate(HIGH_FREQ_FS); // 2MHz } // 重新启动DMA,N_ADC固定为2048 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, N_ADC); // 等待DMA传输完成标志置位后继续 // 对采样数据加汉宁窗 float fft_in[4096]; // 2*N_ADC,交织存放实部和虚部 for (int i = 0; i < N_ADC; i++) { float win = 0.5f - 0.5f * cosf(2.0f * PI * i / (N_ADC - 1)); fft_in[2*i] = adc_buf_f[i] * win; fft_in[2*i + 1] = 0.0f; } // 执行复数FFT变换 arm_cfft_instance_f32 fft_inst; arm_cfft_init_f32(&fft_inst, N_ADC); arm_cfft_f32(&fft_inst, fft_in, 0, 1); // 计算幅度谱 float fft_mag[2048]; arm_cmplx_mag_f32(fft_in, fft_mag, N_ADC);注意arm_cmplx_mag_f32输出的幅度谱是每条谱线的模值,不是有效值。单频正弦信号如果正好落在某条谱线上,该谱线的幅值近似等于信号振幅乘以N/2,加汉宁窗后还要再乘一个约2.0的恢复系数,这些系数我在下一节统一处理。
一个关键点是:arm_cfft_f32的实例必须与当前点数匹配。如果上一帧用了512点,这一帧改成2048点却忘记重新arm_cfft_init_f32,旋转因子表还是512点的,FFT结果会完全不可用。我习惯把初始化放在函数入口,每次计算前都重新执行,虽然多花一点时间,但能避免这种隐蔽错误。
3.4 谱峰搜索与谐波幅度提取:搜对范围比全谱扫描重要
谱峰搜索不能满图谱乱找,而要在已知频率附近搜。基波的粗测频率是f0,就在f0附近一个小区间内找最大幅度谱线;2到5次谐波分别在2f0、3f0、4f0、5f0附近搜索。这样做既能减少噪声误判,也能明显加快速度。
// 频谱分辨率 float df = fs_current / N_ADC; // 基波搜索范围:f0的0.9到1.1倍 int k_min = (int)(f0 * 0.9f / df); int k_max = (int)(f0 * 1.1f / df); if (k_min < 1) k_min = 1; if (k_max > N_ADC/2 - 1) k_max = N_ADC/2 - 1; int k_peak = k_min; for (int k = k_min; k <= k_max; k++) { if (fft_mag[k] > fft_mag[k_peak]) { k_peak = k; } } // 抛物线插值,修正谱峰位置到小数精度 float kx = k_peak; if (k_peak > 0 && k_peak < N_ADC/2 - 1) { float y1 = fft_mag[k_peak - 1]; float y2 = fft_mag[k_peak]; float y3 = fft_mag[k_peak + 1]; float denom = y1 - 2.0f*y2 + y3; if (fabsf(denom) > 1e-12f) { kx = k_peak + 0.5f * (y1 - y3) / denom; } } // 基波峰值幅度,加窗后的幅度恢复系数取2.0 float amp_peak = fft_mag[k_peak] * 2.0f / N_ADC * 2.0f; float amp_rms = amp_peak / 1.41421356f;为什么幅度恢复系数取2.0?不加窗时,整周期采样的单频正弦在FFT谱线上的幅度等于AN/2,所以A = 2X/N。加上汉宁窗后,信号能量被主瓣摊到相邻三条谱线,峰值谱线幅度大约降到原来的二分之一,所以要再乘2。严格来说这个系数会随窗函数定义有所不同,但汉宁窗下取2.0是工程上够用的近似,配合插值修正,幅度误差可以压到1%以内。
2到5次谐波的提取方法和基波一致,只是把搜索中心换成nf0,幅度恢复系数不变。要注意的是,如果基波频率粗测误差较大,谐波的搜索范围也要相应放宽,否则可能把谐波漏掉。我一般把谐波搜索范围设在nf0的0.98到1.02倍。
4. THD与失真度的定义差别:读数对不上的根源往往在这
4.1 两个公式长得像,结果差很多
很多队伍把THD和失真度当成同一个东西,这是H题里最常见的认知坑。总谐波失真THD定义为各次谐波有效值的平方和开方,再除以基波有效值,即THD = sqrt(U2²+U3²+U4²+U5²)/U1。而信号失真度r的常见定义是各次谐波有效值平方和开方,除以基波加谐波的总有效值,即r = sqrt(U2²+U3²+U4²+U5²)/sqrt(U1²+U2²+U3²+U4²+U5²)。
这两个公式在小失真时结果几乎一样,比如THD=1%时,失真度r约等于0.99995倍的THD,肉眼看不出差别。但当信号严重失真、THD达到30%以上时,两个读数的差距能到几个百分点。2023年H题要求测的是“信号失真度”,题目里通常会给出明确的定义公式,有的年份还会明确说失真度是指谐波含量占总有效值的比例。写代码前必须把这两个公式都实现,再用一个宏或常量来切换,方便现场根据题目要求调整。
// 计算THD与失真度,u1~u5为基波和各次谐波有效值 float thd = sqrtf(u2*u2 + u3*u3 + u4*u4 + u5*u5) / u1; // 失真度r的定义可能随题目表述不同,用宏开关切换 float r; #ifdef USE_DISTORTION_DEF_TOTAL float u_total = sqrtf(u1*u1 + u2*u2 + u3*u3 + u4*u4 + u5*u5); r = sqrtf(u2*u2 + u3*u3 + u4*u4 + u5*u5) / u_total; #else r = thd; // 如果题目就把失真度定义为THD,直接复用 #endif除了公式,还要注意是否包含直流分量。如果信号源本身带直流偏移,而ADC前端又抬了1.65V偏置,FFT的第0条谱线会很大。H题测的是周期信号的失真度,直流分量一般既不计入信号有效值,也不计入谐波含量。所以计算前先把均值减掉再算FFT,或者计算时直接忽略第0条谱线。
4.2 幅度校正:滤波器幅频特性和ADC增益误差不能装看不见
FFT算出来的是数字域幅度,从模拟输入到FFT输出,中间隔着一堆增益误差:前级运放的增益误差、抗混叠滤波器在500kHz附近的衰减、ADC的增益误差和参考电压误差。如果这些都不校正,高频段谐波幅度会整体偏小,失真度读数自然不对。
我惯用的校正方法是建立一个频点补偿表。用一台函数信号发生器输出已知幅度的正弦波,分别在1kHz、2kHz、5kHz、10kHz、20kHz、50kHz、100kHz、200kHz、500kHz这几个点上,实测FFT读数和信号发生器设定值的比值,把每个频点的补偿系数存到一张表里。实际测量时,根据各次谐波的实际频率,在表里做线性插值,拿插值结果乘上FFT读出的幅度。
// 频响补偿表:frq[]为校准频点,gain_k[]为对应补偿系数 // 实际使用时分段线性插值 float gain_comp(float freq) { if (freq <= cal_frq[0]) return cal_gain[0]; for (int i = 0; i < CAL_N - 1; i++) { if (freq >= cal_frq[i] && freq < cal_frq[i+1]) { float t = (freq - cal_frq[i]) / (cal_frq[i+1] - cal_frq[i]); return cal_gain[i] + t * (cal_gain[i+1] - cal_gain[i]); } } return cal_gain[CAL_N - 1]; }这个补偿表每个量程档都要做一遍,因为不同增益档的运放带宽和寄生电容不一样。工作量大,但这是高频测量精度能到1%以内的关键。如果现场时间不够,至少要校100kHz和500kHz两个点,因为这两处是滤波器和运放衰减最明显的地方。
还有一点容易被忽略:ADC的参考电压如果是单片机内部的浮动参考,温度变化读数就会飘。追求稳定的话,外部基准或至少用单片机内部的VREFINT通道做一次校准,把ADC的满量程系数修正到实测值。
5. 避坑指南:采样、混叠、量程切换和显示的五条血泪经验
5.1 低频信号读数乱跳,失真度越测越脏
现象:输入一个干净的1kHz正弦波,基波幅度还算稳定,但2次和3次谐波幅度忽大忽小,失真度读数在0.5%到3%之间乱跳。
原因:最常见的两种情况,一是采样率档位没切对,用高频档2MHz采1kHz信号,2048点只有大约1个信号周期,FFT分辨率太粗,谐波谱线被频谱泄漏污染;二是过零检测受噪声干扰,导致每次进入FFT的数据起点不固定,加窗后泄漏程度也在变。
解决:先把采样档位逻辑检查一遍,确认低频信号走低频档。然后在过零检测前加一个简单的滑动平均滤波器,或者把过零检测的阈值从过零改成“超过正负小阈值”,比如信号幅度的5%。最后保证加窗函数不是矩形窗。按这个顺序排查,九成以上的乱跳都能解决。
5.2 FFT点数改动后,频谱突然全乱
现象:程序里把FFT点数从512改成2048,结果频谱上到处是随机峰值,基波谱线都找不到。
原因:arm_cfft_f32的实例里保存着旋转因子表,点数改变后没有重新调用arm_cfft_init_f32,导致FFT运算用了旧点数的旋转因子。
解决:把arm_cfft_init_f32放到每次FFT之前无条件执行,不要只在初始化时调用一次。代价是每次计算多花一点时间,但和“频谱全乱”比起来完全值得。
5.3 信号源很干净,但2次谐波始终偏大
现象:函数信号发生器输出的正弦波,面板上显示THD小于0.1%,但装置测出2次谐波达到0.5%甚至1%。
原因:前级运放的非线性失真是主要来源,尤其增益档位切到高增益时,反馈电阻阻值大,运放输出级在小信号下更容易产生二次谐波。另外电源纹波如果耦合进信号通路,也会在频谱上表现为谐波附近的杂散。
解决:把前级运放换成低失真型号,反馈电阻阻值尽量减小。电源端加LC滤波,让模拟供电尽量干净。还有一个判断技巧:把输入信号幅度降到一半,如果2次谐波幅度也下降一半,说明是前级非线性;如果2次谐波幅度下降得更快,说明是信号源自身的问题。
5.4 高频段谐波幅度整体偏小
现象:100kHz信号输入,基波幅度准确,但4次、5次谐波幅度比外部仪器测出的值低10%以上。
原因:抗混叠滤波器在500kHz附近已经有明显衰减,运放的增益带宽积也让高频增益下降。这是模拟链路的固有问题,不是FFT算错了。
解决:用2.2节的频响补偿表。校准点在500kHz附近要加密,至少每隔50kHz校一个点。注意补偿系数要针对当前增益档位重新测量,不能一个系数通吃所有档位。
5.5 自动量程切换后,第一个显示值明显异常
现象:输入信号幅度突然变大,触发量程切换,切换后第一次显示的THD高达百分之十几,刷新一次后恢复正常。
原因:增益切换瞬间,前级滤波电容充放电产生毛刺,这帧数据里混入了开关瞬态,FFT把毛刺当成宽带噪声谱,THD自然爆表。
解决:切换量程后,放弃切换后短时间内的第一帧数据。做法是切换后延时至少5个时间常数,再清一次DMA计数,重新开始采样。或者简单点,切换后的第一帧只用于显示但不参与最终读数,第二帧再上报。现场用第二种方案最省事。
6. 现场快速验证与一个提升频率精度的插值技巧
频率插值这个技巧,在H题里性价比很高。基波频率是后续所有谐波搜索的基础,如果频率估偏了,5次谐波的搜索范围就得放宽,容易把噪声峰误判成谐波。抛物线插值可以把频率精度从FFT分辨率提升到接近千分之一。
// k_peak为整数谱峰序号,kx为修正后的谱峰位置 // 利用峰值谱线和左右两条谱线做抛物线插值 float kx = (float)k_peak; if (k_peak > 0 && k_peak < N_ADC/2 - 1) { float y1 = fft_mag[k_peak - 1]; float y2 = fft_mag[k_peak]; float y3 = fft_mag[k_peak + 1]; float denom = y1 - 2.0f*y2 + y3; if (fabsf(denom) > 1e-12f) { kx = k_peak + 0.5f * (y1 - y3) / denom; } } float f_precise = kx * df; // 精确基波频率用这个方法修正后再去搜索2到5次谐波,搜索范围可以收窄到0.995到1.005倍,误判概率明显下降。你还会发现修正后的基波频率在不同帧之间几乎不跳,这对连续测量稳定性的帮助是立竿见影的。
现场调试时我还有一个习惯:把2048点幅度谱按CSV格式从串口发到PC端绘图,不用等屏幕逐点刷新,直接在电脑上看整条频谱曲线。这样能一眼看出是频谱泄漏、电源纹波还是混叠,比只看最终读数高效太多。
有一年调试,我因为在50kHz以上没做频响补偿,100kHz信号的5次谐波幅度偏了12%,当时对着读数折腾了一晚上,最后用信号发生器逐点扫频才定位到问题。后来我形成的习惯是:每次上电先接一个已知失真的信号源自检,基波读数和信号发生器面板对不上,先怀疑采样配置和补偿表,而不是怀疑算法。希望这篇拆解能帮你把H题代码里最容易翻车的环节提前堵住,赛场上少走弯路。
本文还有配套的精品资源,点击获取