资讯动态

STM32 ADC波形识别实战:正弦波方波三角波的特征提取

发布时间:2026/9/2 3:16:51 来源:尧图企业网站定制
简介面向STM32入门与进阶学习者的ADC波形识别工程用于对采集到的正弦波、方波、三角波数据进行分类并将识别结果经串口输出。作者说明该版本为早期实现采用多条件判断堆叠完成识别可靠性有限目前已淘汰但程序整体思路直观适合理解ADC采样、基础特征判断与串口上报流程。压缩包共175个文件包含29个c源码、30个h头文件、30个d及30个o等编译中间文件还有uvprojx工程文件、hex烧录文件、sct链接脚本及标准外设库相关源文件整体约5.98MB可直接用Keil打开工程查看ADC单通道中断读取、定时器配置和串口发送等模块代码。目前已有17502人学习下载作为后续FFT波形识别方案的早期对比样本仍然可以帮助学习者梳理基础识别逻辑并避开作者踩过的坑。 搞嵌入式这几年波形识别这种需求隔三差五就能碰到设备自检要判断自己的驱动信号是正弦还是方波传感器模块要区分采集回来的是脉冲时钟还是数据波形甚至有些简易信号发生器标定也要先认出当前输出的是不是三角波。最近我正好用STM32的ADC做了一套波形采集识别方案专门对付正弦波、方波、三角波这三种最基础的周期信号整体流程不算复杂但ADC采样的底层配置、特征参数的选择和调试时的那些坑足够写一篇实战记录。这篇文章会把完整思路、关键代码和踩坑经验都写出来适合在做信号采集、仪器仪表相关项目的朋友参考。1. 项目拆解到底要识别什么1.1 三种波形的本质差异正弦波、方波、三角波是最常见的周期信号识别它们本质是找能稳定区分三者的“数字指纹”。在时域里正弦波光滑、方波只有高低电平、三角波是线性上升下降。但从ADC采样到的离散点看光靠肉眼看形状不现实需要提取统计特征。先记住三组值峰值因数峰值/有效值方面方波是1正弦波约1.414三角波约1.732整流平均值占比整流平均值/峰值方面方波是1正弦波约0.637三角波是0.5。这两组值对输入信号的幅度不敏感、对频率不敏感是最适合放进单片机判断的依据。1.2 为什么不用FFT可能有人上来就想做FFT通过谐波含量区分。正弦波只有基波方波有1、3、5...次谐波三角波也有这些谐波但幅值衰减更快。理论上可行但FFT在STM32上至少需要做256点或512点占RAM大、耗时相对较长而且频谱泄漏、加窗都会引入麻烦。对于只需要识别三种已知波形、不关心具体谐波含量的场景统计特征法更合适几十行代码一次遍历同时算出全部特征在72MHz的F103上跑512点只要几百微秒。我这次选型STMF103C8T6片内资源足够重点是把采集链路做稳。1.3 整体设计流程我的实现流程是定时器触发ADC采样DMA自动搬运到内存缓冲区对缓冲区做整周期特征计算最后根据特征值范围输出波形类型。整个流程不依赖外部信号源只要采样窗口覆盖一个完整周期就能判断。这个结构也方便后续扩展比如把特征值换成FFT或者增加锯齿波识别都是在这个框架里改不需要推翻重来。2. ADC采集链路先把原始波形采准2.1 硬件输入端的几个细节STM32的ADC输入范围是0到VREF一般就是0到3.3V。测量负半周信号必须先做电平抬升信号幅值超过3.3V必须分压。我这次用信号发生器输出2Vpp0.5V到2.5V直接进PA1刚好落在ADC量程中间不用额外处理直流偏置。如果信号源输出阻抗比较高ADC内部采样电容会导致电压建立不足波形会比实际偏斜这种情况要加一级运放跟随器。建议在ADC引脚前串一个几百欧电阻再对地接一个100nF电容构成简单低通能滤掉大部分高频噪声。VREF要接干净的3.3V最好再用104电容滤波否则采集值会随负载波动。2.2 采样率与触发方式判断波形至少要让一个周期有足够多的点工程经验是每个周期20个点以上。我用TIM2产生20kHz触发频率测1kHz信号一个周期采20个点测500Hz信号就采40个点特征值更稳。ADC时钟用12MHz采样周期设为71.5个周期一次转换约6.9us远小于50us的触发周期采样电容有足够时间建立。触发方式选定时器更新事件触发ADC配合DMA循环模式代码只负责读数组采集完全不占CPU。如果用普通连续采样CPU要不停进中断搬数据采样率稍微提上去就跑不起来了。2.3 ADC与DMA配置要点先给出标准外设库的初始化代码。这里主要看三个地方TIM2重载值决定采样率、ADC外部触发源选择TIM2_TRGO、DMA设成循环模式。20kHz采样率的计算很简单TIM2挂在72MHz总线上预分频设为71重载值设为4972M / (711) / (491) 20kHz。// 采样率计算TIM2时钟72MHz预分频71重载49 - 72M/72/50 20kHz TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_Period 49; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_SelectOutputTrigger(TIM2, TIM_TRGO_Update); TIM_Cmd(TIM2, ENABLE); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_T2_TRGO; ADC_InitStructure.ADC_ExternalTrigConvCmd ENABLE; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_71Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)adc_buf; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel1, DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE);这里最容易漏的是ADC_DMACmd(ADC1, ENABLE)。很多新手配了DMA但忘了让ADC产生DMA请求结果数组一直是0。另外DMA数据宽度必须配成半字16位因为ADC数据寄存器是16位配成字宽度会把相邻缓冲区数据打乱。我最初就吃过这个亏波形识别出来完全是乱的。3. 从原始数据里提取数字指纹3.1 预处理去直流和滤波ADC直接采到的值是带直流分量的比如0.5V到2.5V信号数字量约在552到2500之间均值约1526。如果不把均值去掉后面算峰值因数一定偏大。第一步就是求均值然后让每个采样点减均值得到交流分量。滤波方面我用一个简单的5点滑动平均去毛刺。要注意如果滑动窗口太大波形棱角会被磨平三角波可能被识别成正弦波所以窗口点数不要超过一个周期点数的十分之一或者干脆不做滤波而靠统计特征本身抗噪。3.2 三个特征值的物理意义峰值因数等于峰值除以交流有效值能反映波形“尖不尖”。正弦波峰值是有效值的1.414倍三角波更瘦长系数到了1.732方波顶是平的峰值因数就是1。整流平均值占比等于绝对值的平均除以峰值反映波形“填充程度”。方波绝对值是一条水平线占比1正弦波平均幅度相对较小占比约0.637三角波线性爬坡平均下来只有0.5。这两个特征组合起来三种波形在二维平面上离得很远判断非常稳。为了不依赖ADC满量程特别是信号有直流偏置时峰值统一用max_abs表示max_abs max(|ADC值 - 均值|)。不要用(max-min)/2后者对偏置和毛刺非常敏感。整流平均值占比也直接用max_abs归一化这样就算信号发生器偶尔飘一点直流偏置结果也不会跑偏。3.3 特征计算代码以下是遍历512点缓冲区计算特征值的函数一次循环就能算完void CalcFeature(uint16_t *buf, uint16_t n, WaveFeature_t *f) { uint32_t sum 0; for (uint16_t i 0; i n; i) sum buf[i]; float mean (float)sum / n; double sum_ac_sq 0; float sum_abs_ac 0, max_abs 0; for (uint16_t i 0; i n; i) { float v (float)buf[i] - mean; sum_ac_sq (double)v * v; sum_abs_ac fabsf(v); if (fabsf(v) max_abs) max_abs fabsf(v); } float rms_ac sqrtf((float)(sum_ac_sq / n)); float rect_avg sum_abs_ac / n; f-max_abs max_abs; f-rms_ac rms_ac; f-peak_factor max_abs / rms_ac; f-rect_ratio rect_avg / max_abs; }有一点要提醒如果输入信号幅度太小max_abs会很小除法会产生离谱值。所以在调用前要先判断max_abs大于50个LSB以上才做识别否则直接认为无信号。对于12位ADC50个LSB大概对应40mV基本能满足大多数信号源需求。4. 识别算法与工程实现4.1 阈值怎么定理想情况下三种波形的两组特征值正好落在三个固定点。实际因为有噪声、信号发生器的上升沿、采样量化误差实测值会在理想值附近抖动。我给出的判定区间波形类型峰值因数整流平均值占比方波0.90 ~ 1.100.90 ~ 1.10正弦波1.30 ~ 1.550.60 ~ 0.70三角波1.60 ~ 1.850.45 ~ 0.55这个区间是按我自己实测的1kHz、2Vpp信号调的不同幅度和频率下会略有偏差。信号源上升沿慢的时候方波峰值因数会偏离1可以把方波区间放宽到0.85~1.2。如果只是自己玩建议先用串口把特征值打印出来再对照波形微调区间不要一上来就套固定值。4.2 判决函数实现按照上面的区间判决函数写出来非常直白。我用的是“与理想值做差”的方式阈值写在条件里后续加锯齿波时只需要多抄一个分支WaveType JudgeWave(WaveFeature_t *f) { if (f-max_abs 50.0f) return WAVE_NONE; if (fabsf(f-peak_factor - 1.0f) 0.12f fabsf(f-rect_ratio - 1.0f) 0.12f) { return WAVE_SQUARE; } if (fabsf(f-peak_factor - 1.414f) 0.15f fabsf(f-rect_ratio - 0.637f) 0.10f) { return WAVE_SINE; } if (fabsf(f-peak_factor - 1.732f) 0.15f fabsf(f-rect_ratio - 0.50f) 0.08f) { return WAVE_TRIANGLE; } return WAVE_UNKNOWN; }这里用绝对差值而不是硬编码区间好处是后续想加两种新波形时直接套理想值就行。比如锯齿波的峰值因数和整流比大体在三角波和方波之间但你真要做识别还得单独标定不能想当然。4.3 串口打印和现场标定调试阶段我会每隔500ms把峰值因数、整流比、max_abs和识别结果通过串口发出来同时用示波器看输入波形。比如把信号发生器设成300Hz三角波正常应看到peak_factor约1.73、rect_ratio约0.5。如果看到数值偏了先检查是不是触发了ADC倍频或采样率不对不要先去改阈值。实测一组数据20kHz采样1kHz信号2Vpp输入波形峰值因数整流平均值占比识别结果正弦波1.420.65正弦波方波1.031.02方波三角波1.760.52三角波整体识别稳定三种波形没有误判。我在这个阶段还顺手把信号频率算出来通过过零计数方便后续显示。5. 调试中踩过的坑和解决办法5.1 采样率不够正弦被识别成三角波第一次调试时我把20kHz采样率设成了7kHz1kHz正弦一个周期只有7个点波形被采成近似线性整流平均值占比从0.637偏到了0.51直接识别成三角波。解决方法是先算好采样率至少20倍信号频率。如果信号频率不确定可以用过零检测先测频率再动态调TIM重载值。不过动态调会造成缓冲区内波形不完整简单的做法是按预期最高频率把采样率固定下来。5.2 直流偏置导致特征值偏离刚开始我用(max-min)/2作为峰值信号发生器输出0.5V到2.5V时如果均值本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价