
1. 项目概述为什么在STM32上同时用输入捕获和FFT测频不是“炫技”而是解决真实痛点你手头有个电机转速传感器输出的是方波信号频率范围从50Hz到8kHz旁边还接了个振动加速度计输出的是微弱的正弦叠加噪声信号主频可能在200Hz3kHz之间波动。老板说“要实时显示这两个信号的频率精度得优于±0.5%还要能看频谱——别只给个数字我要知道有没有谐波、有没有边带、有没有异常峰值。”这时候你翻遍HAL库文档发现HAL_TIM_IC_Start_IT()能测周期但只适用于干净、稳定的方波而arm_rfft_fast_f32()能算频谱可它吃的是连续采样数组对单点跳变毫无感知。——这就是本项目存在的全部理由输入捕获负责“抓起点”FFT负责“看全貌”二者不是替代关系而是时间域与频率域的协同搭档。我做过三类实际项目工业PLC的伺服反馈校验、车载ECU的曲轴位置信号诊断、还有智能水泵的叶轮空化监测。它们共同的硬约束是——信号不可预测可能突变、可能中断、可能夹杂毛刺。单纯靠定时器计数遇到50ms的信号丢失就直接归零只做固定窗长FFT又会因截断效应把一个1250Hz的真实峰拆成1240Hz和1260Hz两个假峰。本项目标题里“STM32单片机-输入捕获、FFT测频”这12个字本质是在资源受限的嵌入式环境里用确定性硬件逻辑输入捕获锚定不确定信号的起始时刻再用软件算法FFT在该时刻之后的稳定片段中提取完整频谱特征。关键词“STM32”意味着我们不谈FPGA时序约束“输入捕获”强调硬件级边沿触发能力“FFT”指向可落地的定点/浮点实现方案“测频”则框定了输出目标——不是炫酷的瀑布图而是带置信度标记的基频前三阶谐波数值。适合正在做毕业设计的本科生、调试电机驱动的FAE工程师、或是需要快速验证信号质量的嵌入式初学者。只要你手上有块STM32F407或F103开发板配个信号发生器今天就能跑通第一版。2. 整体架构设计为什么必须分两层处理而不是让FFT“一口吞下”2.1 信号特性倒逼架构分层方波与正弦的本质差异先说个实测案例用函数发生器输出1kHz方波接入STM32F407的PA0引脚开启TIM2_CH1输入捕获。示波器显示上升沿抖动±2ns但单片机测得的周期值在998μs1003μs间跳变。再换同一台发生器输出1kHz正弦波幅度1Vpp接入同一引脚——输入捕获直接失效没有明确边沿捕获寄存器值乱跳。这揭示了根本矛盾输入捕获依赖信号的跳变沿dv/dt极大FFT依赖信号的连续采样dt恒定。方波适合前者正弦适合后者而现实中传感器输出往往是混合态霍尔传感器输出近似方波但受EMI干扰后边沿拖尾压电加速度计输出纯正弦但叠加了开关电源噪声形成的宽带干扰。若强行用FFT处理方波需先过零检测再插值计算量暴增若用输入捕获处理正弦需加施密特触发器整形引入额外相位延迟。因此本项目采用双轨并行架构轨道A确定性路径输入捕获模块专责处理含明确边沿的信号如编码器A/B相、霍尔U/V/W相、PWM反馈。利用STM32高级定时器的滤波预分频功能硬件消抖直接读取CCR寄存器获得高精度周期值。此路径延迟固定典型值1μs结果可直接用于闭环控制。轨道B分析性路径FFT频谱分析模块专责处理连续模拟信号如麦克风、振动传感器、电流采样。通过ADC以固定速率如10ksps采集N点如1024点送入FFT引擎。关键在于——采样窗口的起始时刻必须由轨道A同步触发而非软件延时启动。否则当信号频率为1001Hz而采样率为10kHz时每次FFT窗都会相对信号滑动0.1个周期导致频谱泄露加剧。提示很多初学者误以为“FFT精度采样率/N”其实真正决定频率分辨率的是有效分析时长TN×Ts。若N1024Ts100μs则T102.4ms理论分辨率Δf1/T≈9.77Hz。但若信号本身不稳定这个“理论值”毫无意义。本项目用输入捕获锁定信号相位确保每次FFT窗都从同一相位点如上升沿开始使T成为真实相干时间。2.2 硬件资源分配为什么选F4而非F1以及定时器与ADC的绑定逻辑STM32F103C8T6俗称“蓝 pill”虽便宜但其ADC最大采样率仅1Msps12位模式且无硬件过采样。而F407VE拥有3个独立ADC支持12位/16位可调最高采样率2.4Msps并内置硬件过采样滤波器Oversampling。更重要的是F4系列支持定时器触发ADC采样——这是实现“输入捕获同步FFT”的物理基础。具体绑定关系如下TIM2_CH1配置为输入捕获捕获PA0上的上升沿TIM2的更新事件UEV作为触发源连接至ADC1的注入通道触发当捕获到有效边沿时TIM2立即产生UEVADC1随即启动一次注入转换ADC转换完成中断中将结果存入环形缓冲区当缓冲区满N点如1024时触发FFT计算。这种硬件联动避免了软件查询的不确定性。实测数据F407在168MHz主频下从边沿触发到第1024个ADC采样完成总延迟稳定在103.2±0.3μs抖动远小于1个采样周期100μs。而若用F103需靠GPIO中断软件延时启动ADC实测抖动达±15μs直接导致频谱主瓣展宽。注意不要用“TIMx_CCx触发ADC规则通道”因为规则通道转换完成后会自动触发DMA传输但DMA无法精确控制何时填满N点缓冲区。必须用“TIMx_UP触发ADC注入通道”再由ADC注入转换完成中断JEOC手动搬运数据——这样你才能完全掌控每个采样点的来源和时序。2.3 软件流程图解从边沿捕获到频谱输出的7个关键节点整个流程不是线性的“捕获→采样→FFT→显示”而是带状态机的闭环系统。以下是实际代码中运行的7个核心节点已去除HAL库冗余封装直击寄存器操作初始化阶段配置TIM2为输入捕获模式CC1P1下降沿触发IC1F0b10008倍采样4次滤波预分频PSC0ADC1配置为注入模式JEXTSEL0b0010选择TIM2_UP触发JSQR0x00000001仅启用JSQ1即通道1。空闲等待CPU处于WFI低功耗模式TIM2_CC1IF标志位为0。边沿捕获中断PA0出现下降沿 → TIM2_CC1IF1 → 进入TIM2_IRQHandler → 清除CC1IF → 读取CCR1值 → 计算当前周期T_cur CCR1 × (PSC1) / TIM2_CLK84MHz→ 判断T_cur是否在有效范围如100μs20ms→ 若有效设置全局标志capture_valid 1。ADC触发与采样在清除CC1IF后立即写TIM2-EGR TIM_EGR_UG强制更新事件→ TIM2_UP中断被触发 → 在TIM2_UP_IRQHandler中检查capture_valid→ 若为1则启动ADC注入转换ADC1-CR2 | ADC_CR2_JSWSTART。数据搬运ADC注入转换完成 → JEOC1 → 进入ADC1_2_IRQHandler → 读取ADC1-JDR1 → 存入adc_buffer[buf_index]→ 若buf_index N则置位fft_ready 1buf_index复位为0。FFT执行主循环检测到fft_ready 1→ 调用arm_rfft_fast_f32(S, adc_buffer, fft_output, 0)→ 计算结果存于fft_output复数数组→ 遍历前N/2个点计算幅值mag[i] sqrtf(real[i]^2 imag[i]^2)。频率识别与输出在mag[]中找最大值索引i_max→ 基频f0 i_max × Fs / NFs10kHz, N1024 → f0 i_max × 9.77Hz→ 检查i_max±3范围内是否存在次大峰谐波→ 将f0及前三阶谐波频率打包为结构体通过串口发送。这个流程中节点3、4、5构成硬件级确定性链路延迟可控节点6、7为软件计算耗时取决于N和主频。F407上N1024的浮点FFT耗时约8.2msCMSIS-DSP库实测完全满足100Hz刷新率需求。3. 核心细节解析输入捕获的消抖策略与FFT的窗函数选择3.1 输入捕获的四级抗干扰设计从硬件滤波到软件校验很多教程只教“设置IC1F0b1000”却不说为什么选这个值。STM32的输入捕获滤波器本质是数字脉冲宽度鉴别器它要求在连续4个内部时钟周期tCK_INT内输入引脚电平保持一致才认为是有效边沿。tCK_INT (PSC1) / TIMx_CLK。以TIM2为例若TIM2_CLK84MHzPSC0则tCK_INT≈11.9ns。此时IC1F0b1000对应最小滤波时间4×tCK_INT≈47.6ns——这刚好能滤除PCB走线反射引起的50ns毛刺但不会抹掉正常信号边沿典型MCU信号边沿时间100ns。但仅靠硬件滤波不够。我在某汽车电子项目中遇到过更恶劣场景CAN总线辐射干扰导致PA0引脚出现200ns宽、3V幅度的尖峰。此时需叠加软件级防护一级硬件滤波已述二级捕获值合理性校验记录上一次有效周期T_prev本次捕获T_cur。若|T_cur - T_prev| 0.3×T_prev则丢弃本次捕获防EMI瞬态干扰。实测中电机负载突变时周期变化率15%设0.3阈值足够安全。三级连续有效性确认设立计数器valid_count每次校验通过则valid_count失败则清零。仅当valid_count ≥ 3时才置位capture_valid 1。这避免单次误触发导致整段FFT数据失效。四级ADC采样时段屏蔽在ADC采样期间即从TIM2_UP触发到第1024次JEOC禁止响应新的TIM2_CC1IF中断。方法是在进入TIM2_UP_IRQHandler时临时关闭捕获中断__HAL_TIM_DISABLE_IT(htim2, TIM_IT_CC1)在fft_ready 1后再重新使能。否则若在采样中途又来个边沿会导致ADC触发紊乱。实操心得曾有同事在F103上未做第四级屏蔽结果电机高速旋转时每秒产生上千次捕获中断ADC DMA缓冲区被反复覆盖FFT输出全是噪声。加上这行__HAL_TIM_DISABLE_IT后问题消失。这不是过度设计而是嵌入式实时系统的生存法则。3.2 FFT的窗函数实战对比矩形窗、汉宁窗、Flat Top谁更适合测频FFT的“频谱泄露”问题常被泛泛而谈。我们用实测数据说话对1000Hz纯正弦信号ADC采样率10kHzN1024分别应用三种窗函数观察频谱主瓣和旁瓣表现窗函数主瓣宽度bin最大旁瓣衰减dB频率估计误差Hz适用场景矩形窗1.0-13±4.89仅当信号频率严格等于k×Fs/N即整周期截断时可用现实中几乎不存在汉宁窗2.0-31±2.44平衡型选择主瓣稍宽但旁瓣抑制强适合检测谐波Flat Top3.0-93±0.12幅值精度最高但主瓣最宽频率分辨率最低关键结论测频精度不取决于主瓣宽度而取决于幅值估计的稳定性。Flat Top窗虽主瓣宽但其频谱峰值位置对信号相位不敏感——无论正弦波在FFT窗内起始相位如何峰值bin索引几乎不变。而矩形窗下相位偏移10°就可能导致峰值bin在999Hz和1001Hz间跳变。实测步骤生成1000Hz正弦波相位从0°扫到360°步进1°对每个相位采集1024点加Flat Top窗后FFT记录峰值bin索引i_max统计i_max分布矩形窗下i_max在102~104间跳变对应998~1002HzFlat Top窗下i_max稳定在1031001.2Hz。因此本项目默认采用Flat Top窗。其系数可通过MATLAB生成w flattopwin(1024); % 生成1024点Flat Top窗 % 导出为C数组 fprintf(const float32_t flat_top_win[1024] {\n); for i1:1024 fprintf( %.6ff,\n, w(i)); end fprintf(};\n);在FFT前对adc_buffer[i] * flat_top_win[i]即可。注意Flat Top窗会衰减信号幅值约4.6倍需在幅值计算后补偿mag_compensated mag[i] × 4.6。这点常被忽略导致谐波幅值误判。3.3 频率精修算法为什么峰值bin索引不能直接当结果用即使用了Flat Top窗峰值bin索引i_max仍存在量化误差。因Fs10kHzN1024故频率分辨率Δf9.77Hz。若真实频率为1005Hz则i_max round(1005/9.77) 103对应1004.3Hz误差0.7Hz。对电机转速测量要求±0.1%1000Hz时允许误差仅1Hz此误差已超标。解决方案抛物线插值法Parabolic Interpolation。原理是FFT频谱主瓣在峰值附近近似抛物线取峰值点i_max及其左右邻点i_max-1、i_max1的幅值y0,y1,y2拟合抛物线y a(x-x0)^2 b(x-x0) c求导得极值点x_peak x0 - b/(2a)。计算公式简化版delta 0.5 × (y0 - y2) / (y0 - 2×y1 y2) f_real (i_max delta) × Fs / N其中y0 mag[i_max-1],y1 mag[i_max],y2 mag[i_max1]。实测效果对1005.3Hz信号i_max103对应1004.3Hz经插值后f_real1005.28Hz误差仅0.02Hz提升35倍精度。实操心得插值前务必确保i_max不在频谱边缘即i_max 1 and i_max N/2-1否则邻点越界。我在调试振动分析仪时因未加此判断导致i_max0时访问mag[-1]引发HardFault。后来加了保护if (i_max 0 || i_max N/2-1) { f_real i_max * Fs / N; // 退化为粗略估计 } else { // 执行插值计算 }4. 实操过程详解从CubeMX配置到Keil工程编译的完整链路4.1 CubeMX关键配置截图级说明文字还原由于无法贴图以下用文字精准还原CubeMX中必须勾选/设置的每一项按模块顺序排列System Core → RCCHigh Speed Clock (HSE): Crystal/Ceramic Resonator 外接8MHz晶振PLL Source Mux: HSEPLLM: 8 输入分频8MHz/81MHzPLLN: 336 倍频1MHz×336336MHzPLLP: 2 系统时钟分频336MHz/2168MHzSystem Clock Mux: PLLCLKAHB Prescaler: /1 168MHzAPB1 Prescaler: /4 42MHzAPB2 Prescaler: /2 84MHzSystem Core → SYSDebug: Serial Wire 保留SWD调试Timers → TIM2Mode: Input CaptureChannel 1: Input Capture DirectInput Capture Prescaler: CK_INT/1 不分频Input Filter: 8 CK_INT 即IC1F0b1000Input Polarity: Falling EdgeMaster Mode: Reset 为触发ADC需设为Update EventTrigger Output (TRGO): Update EventAnalog → ADC1Mode: Independent modeResolution: 12 bitsData Alignment: RightScan Conversion Mode: Disabled 注入通道不需扫描Continuous Conversion Mode: DisabledExternal Triggers Conversion: EnabledExternal Trigger Source: TIM2 TRGOInjected Conversion: EnabledInjected Channels: Channel 1Injected Rank 1: Channel 1Injected Sampling Time: 15 Cycles 保证12位精度Pinout → PA0GPIO Mode: Alternate Function Push-PullGPIO Pull-up/Pull-down: No Pull-up and No Pull-downGPIO Speed: Very HighGPIO Output Type: Push-PullProject Manager → ToolchainToolchain / IDE: MDK-ARM v5Code Generation: Generate peripheral initialization as middleware 启用HAL提示很多人卡在“TIM2 TRGO无法触发ADC”。关键在两点① TIM2的Master Mode必须设为Reset非Disable否则TRGO无输出② ADC1的External Trigger Source必须选TIM2 TRGO不是TIM2 CC1因我们用更新事件而非捕获事件触发。4.2 Keil工程关键文件修改与优化CubeMX生成的工程需手动修改3个文件否则FFT无法运行1. main.c 中添加全局缓冲区与状态变量/* 在main()函数上方添加 */ #define ADC_BUF_SIZE 1024 uint16_t adc_buffer[ADC_BUF_SIZE]; volatile uint16_t buf_index 0; volatile uint8_t capture_valid 0; volatile uint8_t fft_ready 0; /* 在HAL_TIM_IC_CaptureCallback()中修改 */ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2 htim-Channel HAL_TIM_ACTIVE_CHANNEL_1) { uint32_t cap_val HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); uint32_t period_us (cap_val * 1000000ULL) / (HAL_RCC_GetPCLK1Freq() / (htim-Instance-PSC 1)); // 周期校验100us ~ 20ms if (period_us 100 period_us 20000) { capture_valid 1; } } }2. stm32f4xx_it.c 中重写TIM2_UP_IRQHandler/* 替换原有函数 */ void TIM2_UP_IRQHandler(void) { HAL_NVIC_ClearPendingIRQ(TIM2_IRQn); if (__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE) ! RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim2, TIM_IT_UPDATE) ! RESET) { __HAL_TIM_CLEAR_IT(htim2, TIM_IT_UPDATE); if (capture_valid) { // 启动ADC注入转换 HAL_ADCEx_InjectedStart_IT(hadc1); capture_valid 0; // 清标志 } } } }3. adc.c 中修改注入转换完成回调/* 在HAL_ADCEx_InjectedConvCpltCallback()中 */ void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc-Instance ADC1) { uint16_t val HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1); adc_buffer[buf_index] val; if (buf_index ADC_BUF_SIZE) { buf_index 0; fft_ready 1; } } }4. 添加FFT计算函数main.c中#include arm_math.h #define FFT_SIZE 1024 float32_t fft_input[FFT_SIZE]; float32_t fft_output[FFT_SIZE * 2]; // 复数输出实部虚部 arm_rfft_fast_instance_f32 S; void fft_init(void) { arm_rfft_fast_init_f32(S, FFT_SIZE); } void do_fft_analysis(void) { // 1. 数据搬移ADC值转float for (int i 0; i FFT_SIZE; i) { fft_input[i] (float32_t)adc_buffer[i] - 2048.0f; // 12位ADC中心化 } // 2. 加窗 for (int i 0; i FFT_SIZE; i) { fft_input[i] * flat_top_win[i]; } // 3. 执行FFT arm_rfft_fast_f32(S, fft_input, fft_output, 0); // 4. 计算幅值谱 float32_t mag[FFT_SIZE/2]; for (int i 0; i FFT_SIZE/2; i) { float32_t real fft_output[2*i]; float32_t imag fft_output[2*i 1]; mag[i] arm_sqrt_f32(real*real imag*imag); } // 5. 抛物线插值找峰值 uint16_t i_max 1; for (uint16_t i 1; i FFT_SIZE/2-1; i) { if (mag[i] mag[i_max]) i_max i; } if (i_max 1 i_max FFT_SIZE/2-1) { float32_t y0 mag[i_max-1], y1 mag[i_max], y2 mag[i_max1]; float32_t delta 0.5f * (y0 - y2) / (y0 - 2.0f*y1 y2); float32_t f_real (i_max delta) * 10000.0f / FFT_SIZE; // Fs10kHz printf(Freq: %.2f Hz\n, f_real); } }5. Keil编译优化设置Options for Target → C/C → Optimization Level: Level 3 -O3Options for Target → C/C → Define: ARM_MATH_CM4, __FPU_PRESENT1, __FPU_USED1Options for Target → Linker → Use Memory Layout from Target Dialog: UncheckOptions for Target → Linker → Scatter File: 添加STM32F407VETx_FLASH.sct确保堆栈足够FFT需约4KB RAM注意若编译报错undefined reference to arm_rfft_fast_f32说明未添加CMSIS-DSP库。需在Project → Manage → Run-Time Environment → CMSIS → DSP → Core → Float32勾选并在C/C中添加#include arm_math.h。4.3 实测波形与数据验证用示波器和逻辑分析仪交叉验证光看串口打印的“Freq: 1005.28 Hz”没说服力。我用泰克MSO5系逻辑分析仪抓取三路信号通道1黄色PA0输入信号1000Hz方波通道2蓝色TIM2_UP触发脉冲宽度10ns周期102.4ms通道3绿色USART1_TX串口输出频谱结果关键观测点TIM2_UP脉冲前沿与PA0下降沿的延迟实测92.3ns符合TIM2时钟树计算84MHz → 11.9ns/cycle8个cycle95.2ns余下为布线延迟。从TIM2_UP触发到第一个ADC采样完成逻辑分析仪标出ADC1_EOC信号延迟1.23μs与数据手册中“注入转换时间1512229个ADC时钟”吻合ADC时钟42MHz → 29×23.8ns≈690ns加上触发延迟共1.23μs。串口输出间隔两次“Freq: xxx”之间时间差102.4ms证明FFT窗长严格锁定。再用Keysight 3000T示波器做频谱对比函数发生器输出1000Hz1500Hz双频正弦幅值比3:1STM32测得f11000.12Hz幅值-10.2dBf21500.08Hz幅值-15.3dB示波器FFTf11000.05Hz-10.1dBf21499.97Hz-15.4dB误差均在0.1%以内验证方案可靠性。5. 常见问题与排查技巧实录那些官方文档不会写的坑5.1 典型问题速查表问题现象可能原因排查步骤解决方案串口无输出或输出乱码① USART波特率计算错误② 时钟树配置错误导致APBx时钟非预期值用示波器测USART_TX引脚看实际波形周期查RCC_CFGR寄存器中PCLK1/PCLK2值在CubeMX中重新生成时钟树或手动计算USARTDIV (PCLKx × 100) / (16 × BaudRate)取整后反推实际波特率FFT输出全为0或NaN①fft_input数组未初始化②arm_rfft_fast_f32()参数顺序错误③ 浮点单元未使能在调试模式下单步执行检查fft_input[0]值查看S结构体中ifftFlag是否为0确保arm_rfft_fast_init_f32(S, N)在arm_rfft_fast_f32()前调用检查__FPU_USED1是否定义输入捕获值跳变剧烈无规律① 硬件滤波未生效IC1F0② 引脚未接上拉/下拉悬空干扰③ 电源噪声大用示波器看PA0波形确认是否有高频毛刺测PA0对地电压在CubeMX中确认IC1F设置PA0外接10kΩ下拉电阻在STM32电源引脚加0.1μF陶瓷电容ADC采样值始终为0或4095① ADC参考电压未接稳② 通道未使能③ 注入序列配置错误用万用表测VREF引脚电压应为3.3V查ADC_JSQR寄存器中JSQ1字段确保VREF接3.3V在CubeMX中勾选“Injected Channels”并设置Rank1为Ch1FFT结果主频偏移10Hz① 采样率Fs不准确② 窗函数未归一化③ 信号非平稳如电机启停过程用示波器测ADC采样时钟由TIM2_UP触发周期应为100μs检查flat_top_win数组和是否乘了校准TIM2时钟源外接晶振精度±20ppm窗函数系数和应为1否则幅值失真5.2 独家避坑技巧来自产线调试的血泪经验技巧1用“虚拟信号发生器”快速定位FFT问题不用外接设备直接在代码中生成测试信号// 在adc_buffer填充前插入 for (int i 0; i ADC_BUF_SIZE; i) { float t i * 0.0001f; // Ts100us adc_buffer[i] (uint16_t)(2048 1000 * sinf(2.0f * PI * 1000.0f * t)); }这样可排除硬件信号链干扰专注验证FFT算法逻辑。我曾用此法30分钟定位到arm_sqrt_f32()未链接math库的问题。技巧2捕获中断优先级必须高于ADC中断在CubeMX中TIM2_IRQ优先级设为1ADC1_2_IRQ设为2。否则若ADC中断正在处理数据搬运TIM2捕获中断到来时会被挂起导致capture_valid标志延迟置位下次FFT窗就不同步了。实测中将TIM2优先级从3降到1后频谱稳定性提升5倍。技巧3FFT输出前必做“直流分量切除”ADC采样常含直流偏置如传感器零点漂移。若不处理直流分量会占据FFT bin0其能量可能淹没真实信号。简单方法float32_t mean 0; for (int i 0; i FFT_SIZE; i) mean fft_input[i]; mean / FFT_SIZE; for (int i 0; i FFT_SIZE; i) fft_input[i] - mean;此操作耗时100μs但可使信噪比提升20dB以上。技巧4内存对齐陷阱——FFT加速的关键CMSIS-DSP库的arm_rfft_fast_f32()要求输入数组地址4字节对齐。若adc_buffer定义为uint16_t则地址天然对齐但若转为float32_t fft_input[1024]需确保其地址%40。Keil中可在定义前加__align(4)__align(4) float32_t fft_input[FFT_SIZE];否则FFT结果随机错误且难以复现。最后分享个小技巧在量产测试中我用此系统检测电机霍尔传感器。将待测电机接入运行程序串口输出“Freq: 120.33 Hz, Harmonics: [240.11, 360.45]”。若谐波比基频高10dB即判定传感器磁钢脱落——这比示波器