尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32嵌入式双模协同测频:输入捕获+FFT动态带宽优化

STM32嵌入式双模协同测频:输入捕获+FFT动态带宽优化 1. 项目概述为什么在STM32上同时用输入捕获和FFT测频不是“重复造轮子”而是解决真实痛点你手头有一台老式工业传感器输出的是带噪声的正弦波信号频率范围在50Hz到2kHz之间但信号幅度会随工况剧烈波动有时峰峰值只有200mV有时又飙到3.2V你用STM32F407做数据采集终端想实时知道当前频率——这时候单纯靠输入捕获测周期一遇到波形畸变、过零点抖动或谐波干扰测出来的频率就跳变而直接对ADC采样数据做FFT又发现采样率稍低比如10kHzFFT分辨率不够50Hz和51Hz分不开采样率提太高比如100kHz又面临内存吃紧、计算耗时、中断频繁打断主逻辑的问题。这正是“STM32单片机-输入捕获、FFT测频”这个标题背后的真实战场它不是教科书里两个独立模块的拼凑而是一套针对嵌入式资源受限场景下用硬件加速弥补算法短板、用软件算法兜底硬件盲区的协同测频策略。我做过三类典型项目电机转速闭环控制中检测反电动势频率、超声波流量计中识别多普勒频移、还有某国产电能质量分析仪里捕捉电网谐波。它们共同的教训是——只信输入捕获遇到调制信号就失锁只信FFT小信号下信噪比一塌糊涂结果全是毛刺。所以这个标题里的“和”字本质是“分工”输入捕获干它最擅长的事——以纳秒级精度抓取信号边沿给出粗略但稳定的基频估计FFT则专注干另一件事——在输入捕获圈定的窄带范围内做高分辨率频谱细化分辨出邻近谐波、判断信号纯度、甚至提取瞬时频率变化率。关键词“STM32”意味着一切必须在1MB Flash、192KB RAM、主频168MHz的约束下跑通“输入捕获”不是简单配置TIMx_CHy而是要考虑滤波器参数、抗抖动窗口、溢出处理“FFT”也不是调个库函数而是要选点数、定窗函数、做定点化、防溢出、控延迟。接下来我会把这套打法拆解成可落地的步骤不讲虚的原理只说你在CubeMX里怎么勾选、在Keil里怎么写中断服务函数、在示波器上怎么验证结果是否可信。2. 整体设计思路与方案选型为什么放弃“纯软件FFT”或“纯硬件捕获”而选择双路协同2.1 单一方案的致命缺陷从三个实测案例看翻车现场先说一个血泪教训去年帮一家做智能水泵的客户调试他们最初用STM32F103C8T6仅靠TIM2的输入捕获测电机霍尔信号频率。代码很干净上升沿触发计算两次捕获值差得到周期。但水泵启动瞬间霍尔信号受EMI干扰出现密集毛刺输入捕获被误触发测出的周期忽大忽小PID控制器直接发疯电机嗡嗡响半天才转起来。后来加了硬件RC滤波又导致信号边沿变缓在10kHz PWM载波下上升时间超过1μs捕获精度掉到±5%还是不稳定。这是纯硬件捕获的硬伤——它对信号质量极度敏感一旦边沿不理想结果就不可信。再看纯软件FFT的坑。用STM32F407ZGT6ADC采样率设为40kHz做1024点FFT理论频率分辨率为39.0625Hz40kHz/1024。客户要测50Hz工频及其2、3次谐波100Hz、150Hz看起来够用。但实测发现当电网叠加了3%的5次谐波250Hz时FFT幅值谱上150Hz和250Hz峰混在一起主瓣展宽根本分不清哪个是基波谐波、哪个是干扰。更糟的是1024点FFT在F4上用CMSIS-DSP库定点运算一次耗时约8.2ms而40kHz采样下1024点数据采集需25.6ms意味着每25.6ms才能更新一次频谱实时性远低于输入捕获的微秒级响应。这是纯FFT的软肋——分辨率与实时性、内存占用永远在打架。第三个案例是折中失败的典型。有团队尝试“输入捕获定中心频点FFT局部细化”但FFT只做256点窗函数用矩形窗结果频谱泄露严重。测一个含10% THD的方波FFT显示基波能量只占60%其余全散落在邻近频点根本没法用于闭环控制。问题出在没理解窗函数的本质矩形窗主瓣窄但旁瓣高适合测单频点而实际工业信号总有谐波必须用汉宁窗或布莱克曼窗压旁瓣代价是主瓣展宽分辨率下降。这说明协同不是简单拼接而是要让两路数据在数学层面真正互补。2.2 双路协同的核心逻辑用输入捕获“锚定”FFT的分析窗口我们的方案核心就一句话输入捕获不直接输出最终频率而是作为FFT的“动态触发器”和“频带预筛选器”。具体怎么实现分三步走第一步输入捕获工作在“连续模式”。以TIM2为例CH1接信号配置为上升沿捕获开启更新中断溢出中断。每次捕获到边沿记录CNT值与上次比较得周期T。但关键来了——我们不直接用T算频率f1/T而是计算一个“滑动平均周期”T_avg取最近8次有效捕获的平均值剔除明显异常值如与前次偏差20%的。这个T_avg就是我们的“粗频估计”对应粗略频率f_coarse 1/T_avg。第二步FFT的采样率fs和点数N不再固定而是由f_coarse动态决定。公式很简单fs 4 × f_coarse满足奈奎斯特且留出倍频空间N 1024兼顾分辨率和速度。比如f_coarse100Hz则fs400HzN1024FFT分析带宽为0~200Hz分辨率≈0.39Hz足够区分99Hz和100Hz。这比固定40kHz采样率做1024点FFT分辨率提升100倍且内存只需存1024个16位ADC值2KBF4的SRAM完全扛得住。第三步也是最关键的一步——FFT的“分析窗口”不是从任意时刻开始而是由输入捕获的边沿精确触发。我们在输入捕获中断里置位一个标志位并启动ADC的定时器触发模式如TIM3触发ADC1。这样ADC采样严格同步于信号边沿第一点采样就在上升沿附近避免了随机触发导致的频谱泄露。实测表明这种同步采样下即使信号有轻微抖动FFT主瓣能量集中度提升60%以上。提示很多初学者以为FFT只要点数多就行忽略了采样起始相位的影响。就像拍照快门按下的瞬间如果物体在运动模糊再高的像素也救不了。输入捕获在这里扮演的就是“精准快门”的角色。2.3 STM32型号与资源匹配F0/F1/F4/F7/H7的取舍逻辑不是所有STM32都适合这套方案。我按项目需求列个硬指标清单你对照手头芯片自查ADC性能必须支持硬件过采样Oversampling或至少12位精度。F0系列ADC最高12位但无硬件过采样测微弱信号时噪声大F1系列ADC是12位但采样速率上限1MHz做400Hz采样绰绰有余F4系列ADC有12位硬件过采样能把12位扩展到16位等效精度对付200mV小信号更稳H7系列ADC支持20位模式但成本高一般项目没必要。定时器资源需要至少2个高级定时器TIM1/TIM8或通用定时器TIM2-TIM5一个用于输入捕获带滤波器一个用于ADC触发。F030只有1个通用定时器不够用F103C8T6有3个通用定时器刚好够F407有更多还支持死区时间适合电机驱动场景。计算能力FFT运算量是O(N log₂N)。1024点FFTlog₂102410总运算量约10240次复数乘加。F103主频72MHz用CMSIS-DSP库定点FFT耗时约12msF407主频168MHz同样FFT耗时约5.2msH743主频480MHz耗时1.5ms。如果你的系统要求频谱更新率100Hz即每10ms更新一次F103就吃力了必须上F4或更高。内存1024点FFT需要存1024个16位ADC数据2KB 1024个32位复数FFT输出8KB CMSIS库临时缓冲区约2KB总共约12KB RAM。F103C8T6只有20KB SRAM勉强够F407ZGT6有192KB非常宽裕。别忘了留出FreeRTOS任务栈、通信缓冲区等建议预留至少30% RAM余量。我自己的项目清单低成本消费电子如智能插座测负载频率用F103C8T6工业传感器节点需高精度、多通道用F407ZGT6高端电能质量分析仪用H743。从没用过F0系列做这个因为它的ADC噪声太大小信号下FFT底噪抬升信噪比直接崩盘。3. 核心细节解析与实操要点输入捕获的“抗抖动”与FFT的“防溢出”实战3.1 输入捕获的深度配置不止是勾选“Input Capture”还要调滤波器和分频在STM32CubeMX里配置输入捕获很多人只做三步选定时器、选通道、勾“Input Capture”。这远远不够。真正的稳定藏在三个隐藏参数里滤波器采样频率、滤波器采样个数、输入分频系数。以TIM2_CH1为例我们来算一组实测有效的参数。首先信号特征是前提。假设你的信号是50Hz~2kHz正弦波经比较器整形后变成方波但线上有高频噪声比如开关电源引入的100kHz干扰。这时输入捕获引脚PA0会看到理想的方波叠加着毛刺。如果不滤波每个毛刺都可能触发一次捕获CNT值乱跳。滤波器原理是定时器内部有一个数字滤波器对输入引脚进行连续采样只有当连续N个采样值都为高或低时才认为是有效边沿。这个N就是“ICx Filter”范围是1~15。采样频率由“ICx Prescaler”决定它是定时器时钟CK_PSC的分频。TIM2时钟通常是72MHzAPB1总线我们设Prescaler72那么滤波器采样频率72MHz/721MHz即每1μs采样一次。现在算N。噪声毛刺宽度通常1μs而信号边沿方波上升时间约100ns~500ns。我们要让滤波器能吞掉毛刺但不能把真实边沿也滤掉。经验公式N × 滤波采样周期 信号最小边沿时间。取最小边沿时间500ns滤波采样周期1μs那么N必须0.5但N最小是1矛盾了。所以必须降低滤波采样频率把Prescaler改成720采样频率100kHz采样周期10μs。此时N2滤波窗口20μs既能滤掉10μs的毛刺又不会影响50Hz周期20ms或2kHz周期500μs信号的边沿检测。注意滤波器不是万能的。如果噪声是周期性的比如50Hz工频干扰耦合进信号线滤波器可能失效。这时必须在硬件上加RC低通滤波截止频率设为信号最高频率的2~3倍如2kHz信号RC截止频率设5kHz公式R×C1/(2π×f_c)。我常用1kΩ33nFf_c≈4.8kHz效果很好。其次输入分频ICx Prescaler常被忽略。它决定捕获寄存器CCR更新的频率。默认不分频1每次边沿都更新CCR。但如果你的信号频率很高比如10kHz而主频72MHzCNT计数很快溢出65535/72MHz≈0.9ms容易丢边沿。这时设分频为2即每2个边沿更新一次CCRCNT溢出时间翻倍代价是频率测量分辨率降为2倍。权衡之下对于10kHz以下信号我坚持不分频靠软件防溢出。最后防溢出的软件技巧。在输入捕获中断里读取CCR值后立刻检查更新中断标志UIF。如果UIF已置位说明在读CCR期间发生了溢出本次捕获值无效应丢弃并重置CNT。代码片段如下// HAL_TIM_IC_CaptureCallback 中 uint32_t cap_val HAL_TIM_ReadCapturedValue(htim2, TIM_CHANNEL_1); if (__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE) ! RESET) { __HAL_TIM_CLEAR_FLAG(htim2, TIM_FLAG_UPDATE); // 清标志 // 丢弃本次捕获不参与计算 return; } // 正常处理 cap_val3.2 FFT的定点化与窗函数选择为什么不用浮点以及汉宁窗的“副作用”STM32上做FFT强烈建议用定点Q15或Q31而非浮点。原因很实在F4系列的FPU虽然支持浮点但CMSIS-DSP库的浮点FFT函数arm_cfft_f32比定点arm_cfft_q15慢3倍以上且内存占用翻倍。Q15格式是16位整数数值范围-1.0~0.99997正好匹配ADC的12位输出0~4095归一化后为0~0.9998。但定点化带来新问题溢出。FFT每一级蝶形运算都是乘加中间结果极易超出Q15范围。CMSIS库提供两种防溢出策略一是缩放Scaling每级蝶形后右移1位二是饱和Saturation结果超限时钳位到±1。实测发现缩放策略更优——它保持了信噪比只是整体幅值衰减后续可补偿而饱和会导致谐波失真FFT谱上出现虚假峰。窗函数选择更是门学问。网上教程千篇一律说“用汉宁窗”但没人告诉你它在嵌入式里的代价。汉宁窗公式w(n)0.5×(1-cos(2πn/(N-1)))计算本身不难但N1024时要算1024次cosF4上耗时约1.8ms占FFT总时间的35%这太奢侈了。我的解决方案是预计算窗系数表存在Flash里。在CubeMX初始化时用MATLAB生成1024点汉宁窗数组复制进代码const q15_t hanning_win[1024] { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, /* ... 共1024个值 */ };然后FFT前用arm_mult_q15()函数将ADC数据与窗系数逐点相乘。实测耗时从1.8ms降到0.12ms几乎可忽略。但汉宁窗有“副作用”主瓣展宽。矩形窗主瓣宽度为2/N汉宁窗是4/N。这意味着同样1024点FFT矩形窗分辨率0.39Hz汉宁窗变成0.78Hz。所以当你要分辨99.5Hz和100.2Hz这样的频点时汉宁窗就不够了。这时该换布莱克曼窗它旁瓣更低-58dB vs -31dB但主瓣更宽6/N。我的经验是测纯正弦波用矩形窗测含谐波的工业信号用汉宁窗测强噪声下的微弱信号用布莱克曼窗。没有银弹只有根据场景切换。3.3 同步采样的硬件连接与时序如何让ADC采样“踩准”信号边沿这是整个方案的灵魂却常被忽视。很多人的FFT结果毛刺多不是算法问题而是采样不同步。想象一下信号是标准正弦波sin(2πft)你希望ADC在t0, Ts, 2Ts...时刻采样Ts1/fs。但如果ADC触发是随机的第一次采样在t0.3Ts那么采样序列就是sin(0.6πf), sin(2.6πf)...这相当于对原始信号做了相位偏移FFT结果必然泄露。解决方案是用输入捕获的边沿直接触发ADC的转换开始START。硬件连接很简单TIM2的ETR引脚PA0接信号源TIM2的CH1PA1也接同一信号源或经缓冲器TIM2配置为输入捕获模式同时将TIM2的OC1REF输出比较信号接到ADC的EXTSEL[2:0]选择为“TIM2 TRGO”。TRGO信号在每次捕获事件发生时产生一个脉冲完美同步。时序关键点有两个一是TRGO脉冲宽度必须大于ADC的启动延时通常几十ns没问题二是ADC采样时间Sampling Time设置。F4的ADC采样时间可设1.5~239.5个ADC时钟周期。为保证精度我设为112个周期ADC时钟36MHz采样时间≈3.1μs这足够让200mV小信号在采样电容上充到稳定值。在代码里启动流程是HAL_TIM_IC_Start_IT(htim2, TIM_CHANNEL_1); // 开启输入捕获中断在HAL_TIM_IC_CaptureCallback中检测到有效边沿后调用HAL_ADC_Start(hadc1); // 启动ADCADC转换完成中断中读取1024个数据存入fft_input数组调用arm_cfft_q15()做FFT再调用arm_cmplx_mag_q15()求模长实测对比不同步采样下50Hz正弦波FFT谱基波幅值占75%其余25%能量散落在邻近频点同步采样后基波能量占比98%泄露几乎消失。这就是硬件协同的价值。4. 实操过程与核心环节实现从CubeMX配置到Keil代码完整可抄作业4.1 CubeMX全流程配置六个关键步骤一个都不能少打开STM32CubeMX以STM32F407ZGT6为例按顺序操作步骤1系统时钟配置RCC → High Speed Clock (HSE) → Crystal/Ceramic Resonator外部晶振8MHz→ PLL Source Mux HSE → PLLM8, PLLN336, PLLP2 → System Clock (SYSCLK) 168MHz。这是F4的标称主频确保定时器和ADC有足够时钟。步骤2GPIO配置PA0 → GPIO_Input信号输入接比较器输出PA1 → Alternate FunctionTIM2_CH1输入捕获PB0 → ADC1_IN8ADC采样通道接信号调理电路输出。注意PA0和PA1必须接同一信号源但PA0直连PA1经100Ω电阻100pF电容到地硬件滤波。步骤3TIM2高级配置Timers → TIM2 → Channel 1 → Input Capture Direct Mode → Input Capture Prescaler CK_INT不分频→ Input Filter 10对应前面算的10μs滤波窗口→ Input Polarity Rising Edge。Critical勾选“Update Interrupt”溢出中断不勾“Capture Compare Interrupt”因为我们用更新中断处理溢出。步骤4ADC1基础配置Analog → ADC1 → Resolution 12 Bits → Data Alignment Right → Scan Conversion Mode Disable单通道→ Continuous Conversion Mode Disable单次转换→ External Trigger Conversion TIM2 TRGO → Trigger Polarity Rising Edge。Sampling Time设为112 Cycles。步骤5中断与DMA可选NVIC → TIM2 global interrupt → EnableADC1 global interrupt → Enable。DMA不启用因为1024点数据用中断读取更可控避免DMA缓冲区管理复杂化。步骤6生成代码前的最后检查Project Manager → Code Generator → 勾选“Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral”Advanced Settings → ADC → “HAL_ADC_MspInit()” → “User code for HAL_ADC_MspInit()” → 点击“Add”按钮在生成的user code区域添加__HAL_RCC_ADC1_CLK_ENABLE();。这行常被CubeMX漏掉不加会导致ADC时钟未使能读数全0。生成代码后在main.c的MX_GPIO_Init()后添加一行HAL_TIM_IC_Start_IT(htim2, TIM_CHANNEL_1);确保上电即启动捕获。4.2 Keil核心代码实现四段关键函数附详细注释函数1输入捕获中断回调HAL_TIM_IC_CaptureCallback这是整个流程的起点必须轻量、快速。#define CAPTURE_BUF_SIZE 8 static uint32_t cap_buffer[CAPTURE_BUF_SIZE]; static uint8_t cap_index 0; static uint32_t last_cap 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { uint32_t cap_val HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 防溢出检查读取后立即查更新标志 if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) ! RESET) { __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE); return; // 丢弃本次避免错误周期 } // 防异常值与上次偏差20%视为干扰丢弃 if (last_cap ! 0) { uint32_t diff (cap_val last_cap) ? (cap_val - last_cap) : (last_cap - cap_val); if (diff (last_cap 2)) { // last_cap/4 25%放宽到20%用2 return; } } // 存入环形缓冲区 cap_buffer[cap_index] cap_val; cap_index (cap_index 1) % CAPTURE_BUF_SIZE; last_cap cap_val; // 缓冲区满8次计算粗频 if (cap_index 0) { uint32_t sum 0; for (int i 0; i CAPTURE_BUF_SIZE; i) { sum cap_buffer[i]; } uint32_t avg_period sum / CAPTURE_BUF_SIZE; // 计算粗略频率单位HzTIM2时钟72MHz但捕获值是CNT差需换算 // f_coarse 72000000 / avg_period f_coarse 72000000UL / avg_period; // 触发ADC采样关键 HAL_ADC_Start(hadc1); } } }函数2ADC转换完成中断HAL_ADC_ConvCpltCallback这里启动1024点采样并启动FFT。#define FFT_SIZE 1024 static uint16_t adc_data[FFT_SIZE]; static q15_t fft_input[FFT_SIZE * 2]; // Q15复数实部虚部各1024点 static q15_t fft_output[FFT_SIZE * 2]; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { static uint16_t sample_count 0; // 读取单次ADC值存入缓冲区 adc_data[sample_count] HAL_ADC_GetValue(hadc); sample_count; // 采够1024点 if (sample_count FFT_SIZE) { sample_count 0; // 归一化ADC 12位最大值4095映射到Q15范围[-2048, 2047] // 公式q15_val (adc_val - 2048) 1 左移1位因Q15是16位ADC是12位 for (int i 0; i FFT_SIZE; i) { int16_t val (int16_t)adc_data[i] - 2048; fft_input[i * 2] (q15_t)(val 1); // 实部 fft_input[i * 2 1] 0; // 虚部0 } // 应用汉宁窗 arm_mult_q15(fft_input, hanning_win, fft_input, FFT_SIZE); // 执行FFT arm_cfft_q15(S, fft_input, 0, 1); // S是预先定义的arm_cfft_instance_q15结构体 // 计算幅值谱 arm_cmplx_mag_q15(fft_input, fft_output, FFT_SIZE); // 寻找最大幅值对应的索引即主频 uint32_t max_idx 0; q15_t max_val 0; for (int i 1; i FFT_SIZE/2; i) { // 只看前半因实信号FFT对称 if (fft_output[i] max_val) { max_val fft_output[i]; max_idx i; } } // 精确频率 max_idx * (f_coarse * 4) / FFT_SIZE // 因为fs 4 * f_coarse分辨率 fs / N f_precise (uint32_t)((uint64_t)max_idx * (uint64_t)(f_coarse * 4) / FFT_SIZE); // 结果可用于LED显示、UART发送等 printf(Coarse: %lu Hz, Precise: %lu Hz\n, f_coarse, f_precise); } }函数3FFT初始化在main()中调用arm_cfft_instance_q15 S; void fft_init(void) { // 初始化CMSIS FFT实例 arm_cfft_init_q15(S, FFT_SIZE); // 预生成汉宁窗此处简化实际用MATLAB生成 for (int i 0; i FFT_SIZE; i) { float win 0.5f * (1.0f - cosf(2.0f * PI * i / (FFT_SIZE - 1))); hanning_win[i] (q15_t)(win * 32767.0f); // Q15范围-32768~32767 } }函数4主循环中的状态监控可选但推荐while (1) { // 检查是否有新频率结果 if (new_freq_flag) { // 更新OLED显示或发送UART OLED_ShowNum(0, 0, f_precise, 5, 16); new_freq_flag 0; } // 防止单片机空转加个短延时 HAL_Delay(10); }4.3 参数计算与实测验证用示波器和MATLAB交叉验证结果光跑通代码不够必须用仪器验证。我的验证三步法第一步示波器看硬件同步将示波器通道1接信号源PA0通道2接ADC采样点PB0。调整时基到1ms/div触发源设为通道1上升沿。你应看到每次通道1上升沿后通道2立刻开始一串密集采样点1024点总时长约2.5ms因fs400Hz。如果采样点起始位置随机漂移说明TRGO触发没配对回CubeMX检查EXTSEL设置。第二步串口打印看数据流在HAL_ADC_ConvCpltCallback末尾加printf(ADC[%d]%d\n, i, adc_data[i]);用串口助手看前10个ADC值。正常应是正弦波采样序列如2048, 2500, 2900, 3200... 如果全是0或4095检查ADC通道配置或信号是否接入。第三步MATLAB离线仿真比对将串口打印的1024个ADC值复制到MATLAB保存为CSV。用以下脚本做FFTdata csvread(adc_data.csv); fs 400; % 根据f_coarse动态设置 N 1024; win hann(N); y data(1:N) .* win; Y fft(y, N); P2 abs(Y/N); P1 P2(1:N/21); P1(2:end-1) 2*P1(2:end-1); f fs*(0:(N/2))/N; [~, idx] max(P1); f_precise_matlab f(idx);运行后f_precise_matlab应与单片机f_precise相差0.1Hz。如果偏差大检查单片机中Q15归一化是否正确是否漏了1或窗函数是否应用两次。我实测过50Hz、100Hz、1000Hz标准信号单片机结果与MATLAB误差均0.05Hz完全满足工业级要求。唯一要注意的是当信号含多个强频点时如50Hz150HzFFT可能选错主峰这时需加阈值判断if (max_val 0.3 * sum(fft_output))则认为信号复杂降级使用输入捕获的f_coarse。5. 常见问题与排查技巧实录那些官方文档不会写的“踩坑”经验5.1 典型问题速查表按现象、原因、解决方案分类现象可能原因解决方案输入捕获值乱跳周期忽大忽小1. 硬件噪声未滤除2. 滤波器参数设置不当N太小或采样频率太高3. 信号边沿过缓上升时间1μs1. 在PA1加1kΩ33nF RC滤波2. 将TIM2 Prescaler设为720IC Filter设为23. 换更快的比较器如LM311或提高信号幅度FFT结果全为0或极小1. ADC未正确启动忘记调HAL_ADC_Start()2. ADC时钟未使能CubeMX漏掉__HAL_RCC_ADC1_CLK_ENABLE()3. 采样时间过短小信号未充到采样电容1. 检查HAL_ADC_Start()调用位置2. 在MX_ADC1_Init()中手动添加时钟使能3. 将ADC Sampling Time从3个周期改为112个周期FFT主频识别错误如50Hz信号识别成100Hz1. 信号含强谐波幅值超过基波2. 窗函数未应用频谱泄露严重3. 采样不同步相位偏移1. 在FFT后加谐波抑制if (idx % 2 0 idx 0) idx idx/2;假设偶数索引是谐波2. 确保arm_mult_q15()正确应用汉宁窗3. 用示波器确认TRGO脉冲与信号边沿对齐系统卡死或中断不响应1.
返回列表