ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用STM32F407自制带FFT频谱分析的便携示波器

用STM32F407自制带FFT频谱分析的便携示波器 简介一套基于STM32F407的示波器与FFT频谱分析完整工程面向嵌入式开发者、电子爱好者和参加MCU竞赛的学生解决便携式设备中波形显示与频谱分析需求。项目充分利用Cortex-M4内核的FPU结合多通道DMA、ADC与定时器联动在无CPU干预下搬运采样数据最高可实现2.8MHz采样率定时器预分频与计数值可调便于灵活设置采样周期。采样完成后调用FFT算法将时域信号转换为频域谱线并通过EMWIN图形库在屏幕上同步绘制波形与频谱适合学习实时数据采集、外设驱动和GUI开发。压缩包共496个文件以179个头文件和104个C源文件为主同时包含o目标文件、lib库、hex/axf固件、png界面素材及uvprojx工程配置等整体约20.32MB目录结构清晰。资源已有9335人学习下载适合作为从初始化配置、ADC采样、DMA传输、FFT计算到EMWIN界面绘制的完整参考案例可直接阅读、移植或二次开发。 做嵌入式这些年工位上最缺的一样东西就是示波器。尤其是调电机驱动、看传感器输出、抓串口毛刺的时候哪台示波器在手谁就是全组的焦点。后来我干脆用STM32F407自己做了一台带FFT频谱分析的便携示波器TFT屏上同时画时域波形和频域谱线除了常规测量还能顺手看看信号有没有周期性干扰、电源纹波在什么频率段最明显。这篇文章把硬件链路、采样策略、FFT参数设计和我实际踩过的坑一次讲清楚适合想用单片机做测量仪器、或者对数字信号处理入门的嵌入式工程师参考。1. 方案选型为什么是STM32F4071.1 硬件资源盘点先算一笔账一台简单的数字示波器到底需要什么资源第一足够快的实时采样能力第二把大量采样数据搬到屏幕上的带宽第三做FFT时足够强的算力。STM32F407这几个条件恰好都卡得很合适——168MHz主频带单精度FPUARM官方CMSIS-DSP库里现成的arm_cfft_f32函数可以直接调用内置3个独立ADC在合理时钟配置下单通道采样率能到1.4Msps左右DMA几乎不占CPUFSMC接口又能像读写外部SRAM一样驱动带并行总线的LCD屏。为什么不是F103F103没有FPU软件上跑单精度浮点FFT很吃力做128点还行做1024点就会明显感觉界面卡顿。而F407做1024点单精度FFT实测耗时不到0.5ms大量CPU时间还能留给显示刷新、按键扫描和串口协议处理。这就是我选F407而不是F1、F4低端型号的直接原因。1.2 系统架构整体链路是被测信号 → 前端衰减/阻抗变换 → ADC采样 → DMA搬运 → 内存缓冲区 → 时域波形绘制 FFT计算 → 频域谱线绘制。中间再加上按键和旋转编码器调节量程、采样率串口引出来做上位机联调。模块化拆开看大致是这几块模拟前端分压电阻、运放跟随、钳位保护、偏置电路采样链路定时器触发ADCDMA循环搬运到RAM显示链路FSMC驱动ILI9341屏幕双区域绘制时域和频域计算核心CMSIS-DSP库做窗函数、FFT、幅值修正ADC采样率用定时器触发控制这样采样率完全由定时器决定不受ADC内部时钟分频公式的约束想设成100kHz就设成100kHz想设成10kHz也方便后面调FFT分辨率会非常灵活。2. 信号调理与ADC采样链路2.1 输入前端电路信号进来不能直接怼到ADC引脚。F407的ADC输入范围是0到3.3V而我们要测的可能是正负5V、甚至更高的信号所以前端必须做三件事分压、电平搬移、钳位保护。我的做法是用1MΩ和100kΩ电阻分压把信号衰减到原来的约1/11再接运放做电压跟随输出经过BAT54S双二极管钳位把电压限制在-0.3V到3.6V左右。这样即使输入接错线也不至于把ADC打坏。分压电阻取值不能太小否则会加载被测电路但也不能太大否则运放输入偏置电流会引入误差。1MΩ级别的分压网络配合轨到轨运放跟随在100kHz以内表现够用。这里有个新手特别容易忽略的问题F407的VCAP引脚必须接2.2μF电容而且VCAP1和VCAP2都要接。这个电容是内部稳压器的关键器件漏接或容值不对轻则上电启动不正常重则芯片直接不工作。2.2 采样时钟与DMA传输固定采样率的最优方案是用定时器触发ADC。我的配置思路是TIM3产生更新事件通过TRGO输出触发ADC启动一次转换ADC完成转换后由DMA把结果搬运到RAM缓冲区DMA开启循环模式这样一整块缓冲区的数据会不断刷新不需要CPU参与逐点搬运。核心代码大致是这样/* TIM3配置84MHz主频 */ TIM_TimeBaseStructure.TIM_Prescaler 84 - 1; /* 1MHz */ TIM_TimeBaseStructure.TIM_Period 10 - 1; /* 100kHz采样率 */ TIM_SelectOutputTrigger(TIM3, TIM_TRGO_UPDATE); /* ADC配置 */ ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_T3_TRGO; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);100kHz采样率下采1024个点正好是10.24ms的时间窗对于通用信号观察已经足够实时。要是看音频信号可以降到20kHz配合FFT能得到更细的频谱分辨率看高频开关波形就提到500kHz以上。ADC采样时间这个参数也值得多说一句。采样时间越长内部采样电容充电越充分结果的精度越高但对快速变化信号的捕捉能力会下降。如果前端加了RC滤波或者源阻抗较高一定要把采样时间调大否则幅度会偏小波形看起来像被削过一样。3. FFT频谱分析3.1 FFT为什么要这样做FFT本质上是把一段时域信号分解成一系列不同频率的正弦分量的叠加。每秒钟采样FS个点连续取N点做FFT得到的结果从直流到FS/2相邻谱线的间隔就是频率分辨率等于FS/N。这个公式决定了采样率和FFT点数之间的取舍。N1024、FS100kHz时每条谱线的间隔约97.7Hz两根频率只差50Hz的信号是分不开的如果想把50Hz工频干扰和100Hz信号分开就得把采样率降到10kHz以下此时分辨率约9.8Hz。所以我的程序里留了多档采样率切换低频分析用低采样率高频观察用高采样率这是这类DIY示波器最实用的设计。用CMSIS-DSP库做FFT代码其实很简洁arm_cfft_f32(arm_cfft_sR_f32_len1024, fft_input, 0, 1); arm_cmplx_mag_f32(fft_input, fft_mag, 1024);但这里有个关键细节fft_input是复数缓冲区偶数下标存实部奇数下标存虚部。输入时虚部必须清零否则会算出莫名其妙的谱线。我就在这上面浪费了一个晚上。3.2 加窗与幅值修正FFT隐含了一个假设输入信号是周期延拓的。如果截取的不是整数个周期频谱就会发生泄漏一根干净的谱线会拖出很宽的尾巴。解决办法是加窗。通用示波器最常用的是汉宁窗它对多数信号都有不错的旁瓣抑制效果。加窗后还需要考虑幅值修正。FFT本身不是归一化变换一个幅值为A的正弦信号N点FFT后对应谱线的模值大约是A×N/2。所以要恢复真实幅值要乘2/N。汉宁窗会让波形在窗口内平均衰减一半也就是相干增益约0.5所以最终修正系数是4/N。具体实现时是先把每个采样点减掉直流分量再乘窗函数保证频谱底部干净。3.3 相位测量只算幅值其实浪费了FFT的另一半信息——相位。利用arm_cfft_f32算完后的复数结果取某一根谱线对应的实部和虚部用atan2f(imag, real)就能算出该频率分量的初始相位。这个功能在测已知频率信号的相位差、做振动分析时很有用。不过要提醒的是加窗会影响相位计算所以实际工程中如果只关心某个固定频率的相位用单频点的DFT算法会更准而不是做完整的FFT。这个可以作为后续进阶方向。4. 显示与交互4.1 波形绘制LCD用的是FSMC并口驱动的ILI9341屏幕被映射到外部SRAM的一段地址空间CPU写一个像素就像写一个内存地址速度比SPI屏快一个量级。画波形时不能把1024个点全画到320像素宽的屏幕里需要抽点比如每4个点取一个然后相邻点之间连线。另一个要点是局部刷屏。如果每秒25帧整屏清空再重绘屏幕一定闪得厉害。我的做法是只在波形区域做裁剪只重绘变化的内容波形区和FFT谱线区分开刷新率能稳定在30帧以上。4.2 量程、探头与触发软件量程按V/div设置需要根据前端分压网络算好衰减系数。探头比率是容易被忽略的点如果探头打到10x但程序还按1x换算显示的幅值就会差10倍。我在菜单里加了一个探头比率选项切换x1/x10后显示幅值自动乘10这样配合标准示波器探头就能直接读刻度。触发模式我做的是软件触发在采集缓冲区里检测电平穿越点找到边沿位置后把整帧数据对齐再显示。这种方式实现简单但做不到台式示波器的预触发效果想要硬触发需要加片外比较器属于后续升级方向。对于大多数信号观察场景软件触发已经够用。5. 常见问题排查与避坑实录5.1 问题速查表在调这个项目的过程中我遇到并解决的典型问题基本都在这张表里了现象可能原因处理方式波形布满毛刺FFT底部抬高电源与地处理不当ADC参考不稳模拟地数字地单点连接VDDA加磁珠和钽电容FFT第一根谱线特别高基线整体抬高直流分量没有去除先减均值再做加窗和FFT低频信号幅度不对电阻分压未校准或采样时间太短用信号发生器输出已知幅度校准刻度LCD刷新闪烁整屏清空没有局部裁剪按波形区域裁剪只重绘变化部分上电程序跑飞VCAP电容漏接BOOT0电平不对检查最小系统确认VCAP接2.2μF5.2 硬件避坑实测下来最值得强调的两点一个是ADC输入引脚上的RC滤波不能乱加。ADC采样本质是电容充电前级源阻抗一旦太高保持电容来不及充满采出来的值就会偏小。如果前端必须加滤波要么把采样时间调得很长要么让运放直接低阻驱动。另一个是电源参考。F407的VREF引脚在内部和VDDA直接相连如果板子上VDDA没有做好滤波FFT频谱底部会出现明显的电源频率相关成分而且用电池供电和用USB供电时测出来的底噪完全不同。我最终在VDDA上加了磁珠加10μF钽电容的组合频谱才干净下来。6. 功能扩展与使用体会6.1 功能扩展这个平台再往下走空间还很大。比如接ESP-12F模块把波形数据上传到PC或者手机做一个简单的远程波形查看界面把采样数据存到SD卡做长时间连续记录在现有FFT基础上加包络解调就能当低成本振动信号采集器用配合预测性维护场景做轴承故障的初步诊断。6.2 个人体会最后说几句实在话。做这个项目最大的收获并不是我有了一台属于自己的示波器而是把采样时间、源阻抗、DMA循环、FFT泄漏这些平时在文档里读起来很抽象的概念全部亲手验证了一遍。比如我一直记不住ADC采样时间和前级阻抗的关系直到有一次因为多加了一颗RC滤波电阻导致波形幅度明显偏小才真正理解这两个参数之间的牵制关系。如果你也在学嵌入式我建议不要只买现成的示波器模块从最小系统开始自己搭遇到问题越多越能学到东西。等这台自己做的小仪器调通了再回头用台式示波器你会发现面板上的垂直缩放、触发电平这些参数你终于看得懂它们到底在干什么了。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进