新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于STM32的ADC波形识别算法与工程实现

发布时间:2026/9/2 16:14:08来源:尧图网络
基于STM32的ADC波形识别算法与工程实现
简介STM32 ADC波形识别示例面向初学STM32与信号采集的开发者演示基于ADC采样数据判断正弦波、方波与三角波并通过串口输出识别结果。工程基于STM32标准外设库源码中涉及ADC单通道中断读取、定时器/串口配置等常见模块。压缩包共175个文件以C源码、H头文件为主并包含Keil工程文件uvprojx以及编译生成的hex、axf、d、o、crf等中间产物整体仅5.98MB完整工程可直接打开参考。作者特别说明该方法采用特征阈值方式可靠性有限已由新的FFT识别方案取代因此资源改为免费下载作为波形识别入门思路仍有一定参考价值。已有17502人学习下载适合想了解ADC采集与简单波形区分方法的读者对照阅读。 做嵌入式开发这几年凡是跟信号采集沾边的项目最后基本都绕不开一个问题拿到ADC采回来的一串数据怎么知道它到底是什么波形尤其是做电源、驱动、传感器调理这一类的调试经常要确认前端输出的到底是不是预期中的正弦波或者是方波、三角波总不能每次都搬台示波器怼上去。所以自己动手用STM32的ADC配合一个识别算法把几种常见波形区分出来是一个很实用的小工程既能当算法练手也能直接塞进量产设备里做波形自检。这篇文章就把我从零做这个项目的完整过程写下来包括采样参数的计算、识别算法的选型、代码实现、实测效果还有踩过的几个坑。适合正在学STM32的ADC和DMA的同学也适合要在低算力MCU上做简单信号识别的工程师参考。1. 波形识别思路与方案选型1.1 为什么用STM32的ADC做波形识别波形识别这个需求正经做法有很多比如用FPGA高速采样加FFT或者直接用专用的电能计量芯片。但这些方案的成本和复杂度都不低。STM32自带12位ADC最高采样率能做到1MHz左右具体看型号对于音频范围、工频范围甚至几kHz以内的信号完全够用。配合定时器触发和DMA搬运CPU几乎不用参与采样过程可以在后台跑识别算法实时性也有保障。我用的是一块很常见的STM32F103C8T6小系统板72MHz主频板载12位ADC支持16个外部通道DMA1有7个通道可以用。这套组合在淘宝几十块钱就能搞定用来做波形识别实验非常合适。整个系统的核心思路是信号经过调理电路进ADC引脚定时器产生固定频率的触发信号ADC在触发信号控制下采样DMA把采样结果搬运到内存缓冲区CPU等DMA完成后对缓冲区数据做分析。1.2 三种波形的本质区别要识别波形首先得搞清楚正弦波、方波、三角波在时域上到底有什么本质差异。拿示波器看波形人眼一秒就能分出来但机器只能看到一串离散的电压值所以要把人的视觉判断转化成可计算的数学特征。从幅值分布来看三种波形差别非常明显。正弦波在波峰和波谷附近变化慢中间过零区变化快如果统计每个瞬时幅值出现的次数会得到两端多、中间少的U形分布。方波更极端几乎所有时间都停留在高电平和低电平两个值附近中间幅值只有跳变沿那一瞬间出现统计出来是两个孤立的尖峰。三角波则不同它的幅值随时间线性上升、线性下降每个幅值停留的时间都差不多所以幅值分布接近均匀分布。这个分布特征就是识别算法的核心依据。除此之外还可以用差分处理看变化率——方波的差分是尖脉冲三角波的差分是常数正斜率或负斜率正弦波的差分是余弦形状。两个特征互相印证识别成功率会更高。1.3 识别算法的选型思路选算法的时候我首先排除了FFT。FFT虽然能提供频谱信息但STM32F103在72MHz主频下做256点FFT也需要上百微秒而且FFT的幅频特征对三种波形的区分度并不是特别理想——三次谐波含量上有差异但判定阈值不好定容易误判。与之相比时域统计特征计算量小判定逻辑直观可靠性也更高。最终我采用的是“过零检测 幅值直方图统计”的组合方案。过零检测用来测频率幅值直方图用来识别波形类型。整个识别过程只需要一遍遍历缓冲区时间复杂度是O(N)在72MHz的Cortex-M3上处理1000个采样点只需要几毫秒完全满足实时性要求。2. ADC采样参数的确定与计算2.1 确定采样率与采样点数采样率是第一个要定的参数。根据奈奎斯特定理采样率至少是信号最高频率的两倍但工程上为了波形形状的还原更接近真实通常要求一个周期内有足够多的采样点。我设定的目标信号频率范围是100Hz到5kHz典型测试信号是1kHz。对于1kHz信号一个周期取100个点采样率就是100kHz。为什么取100个点因为幅值直方图要划分区间我用了32个bin如果一个周期只有十几个点统计出来的直方图会很稀疏特征不明显。100个点/周期可以让直方图统计足够平滑。同时100kHz的采样率对STM32F103来说毫无压力ADC时钟14MHz时最短采样周期能做到1us左右100kHz意味着每个采样间隔10us中间还有很多余量。采样点数的确定需要综合考虑。我固定采集1000个点在100kHz采样率下对应10ms的时间窗口对于1kHz信号是10个完整周期。多点采样后取平均特征可以有效抵抗随机噪声的干扰。2.2 定时器触发ADC的配置计算ADC有三种触发方式软件触发、外部引脚触发和定时器触发。软件触发最大的问题是抖动——每条指令的执行时间不同导致采样间隔不均匀波形恢复出来是毛糙的直方图统计也会受影响。所以我选定时器触发让硬件在精确的时间点启动ADC转换。定时器触发频率的计算公式是f_trigger TIM_CLK / ((PSC 1) * (ARR 1))其中TIM_CLK是定时器输入时钟PSC是预分频值ARR是自动重装载值。STM32F103的TIM2挂在APB1上APB1时钟默认是36MHz但定时器时钟倍频后是72MHz。要得到100kHz触发频率72MHz / ((PSC 1) * (ARR 1)) 100kHz我取了PSC 0ARR 719计算得 72,000,000 / 720 100,000正好100kHz。注意ARR的取值范围是0到65535所以这个配置可以灵活适配很宽的频率范围。如果信号频率变成5kHz一个周期要20个点采样率就需要100kHz实际对5kHz周期100点需500kHz但受限于信号调理带宽我通常按“每周期不少于20点”来调整采样率。2.3 DMA搬运与数据缓存设计ADC每次转换完成会产生一个数据寄存器如果每次都进中断读走CPU负载会很高而且中断响应延迟会破坏数据连续性。DMA搬运是标准的正确做法ADC转换完成信号触发DMA请求DMA自动把ADC_DR寄存器里的数据搬到内存数组里搬完指定数量后触发DMA传输完成中断。数据缓存区用uint16_t数组长度1000。注意STM32F103的ADC是12位分辨率数据范围0到4095这个原始值后续归一化时要用。DMA配置为循环模式时缓冲区会被不断刷新配合定时器触发就能实现近乎无间断的连续采样。如果只想采一段数据也可以配置为单次模式传输完成中断里关闭定时器。有一点要特别提醒DMA缓冲区大小一定要和采样点数严格对应如果DMA长度设置成2000但缓冲区只有1000个元素溢出会把相邻内存写坏程序跑飞不说还不容易排查。工程上可以定义一个结构体把缓冲区、采样点数、状态标志放在一起管理代码会清晰很多。3. 波形识别算法的具体实现3.1 直流偏置处理与过零测频拿到ADC原始数据后不能直接分析因为信号可能带有直流偏置。比如信号源输出是1V到3V的正弦波中心在2V过零检测如果把0V当阈值就一个过零点都检测不到。所以我用最大值、最小值的均值作为虚拟零点vmin min(buf, N); vmax max(buf, N); vmid (vmin vmax) / 2.0f;用这个vmid代替0V作为过零阈值。为了抗干扰过零检测不能做单阈值比较否则噪声会在阈值附近反复跳变产生大量虚假过零。我加了一个滞回区间阈值上下各留一个回差带通常取幅度的2%到5%float hyst (vmax - vmin) * 0.03f; uint32_t pos_count 0; // 上升沿计数 for (int i 1; i N; i) { if (buf[i-1] vmid - hyst buf[i] vmid hyst) pos_count; } float freq (float)pos_count / total_time;total_time是采样窗口的总时长采样点数除以采样率。上升沿计数就是周期数频率就等于周期数除以总时长。以10ms、1000个采样点为例如果数到10个上升沿频率就是1kHz。3.2 幅值归一化与直方图统计频率测出来后接下来是关键步骤统计幅值分布。为了让算法不受信号幅度影响比如正弦波峰峰值是3V还是1V分布形状应该一样先对每个采样点做归一化float norm[N]; for (int i 0; i N; i) { norm[i] (buf[i] - vmin) / (vmax - vmin); }归一化后所有值都在0到1之间0对应最低电平1对应最高电平。如果信号存在负压或者超过ADC量程这里要先做调理和限幅否则归一化会失真。直方图统计就是把0到1的范围分成若干区间bin统计每个区间内落了多少个采样点。我用了32个bin区间宽度是1/32 0.03125。统计结果是一个uint32_t hist[32]数组uint32_t hist[32] {0}; for (int i 0; i N; i) { int idx (int)(norm[i] * 32.0f); if (idx 32) idx 31; hist[idx]; }统计完之后把每个bin的计数除以总采样数N就得到归一化的概率分布直方图。这个分布就是识别波形类型的核心依据。3.3 特征判据与波形分类直方图出来了怎么自动判断呢最简单也最鲁棒的特征是“中间区域占比”。把0到1幅值范围分成三段低段0到1/3、中段1/3到2/3、高段2/3到1。三种波形在中间段的占比差异非常大方波绝大多数点停留在高低电平中间段占比几乎为0只有跳变沿会产生极少数中间点三角波线性变化幅值在0到1之间均匀分布中间段的点数约占1/3即0.33左右正弦波U形分布中间段点数最少约0.22左右用这个特征就能初步区分。再配合一个辅助特征——差分序列的分布判断方波的跳变沿特性可以进一步提高成功率。我实际用的判定规则是波形类型中间段占比差分特征方波 0.05有少量极大差分值正弦波0.10 ~ 0.28差分值连续变化三角波 0.30差分值集中在固定区间判定时先看中间段占比如果落在模糊区0.05到0.10再用差分特征辅助判断。这种两级判断结构在实际测试中误判率很低后续我会给出具体的测试数据。4. 完整代码与实测结果4.1 工程初始化代码工程基于STM32标准外设库也可以直接移植到HAL库。核心初始化包括GPIO、ADC、TIM和DMA四个部分。以下是ADC与DMA的初始化代码void ADC_DMA_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // PA0 模拟输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); // ADC1 配置 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_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); // TIM2 触发100kHz TIM_TimeBaseStructure.TIM_Period 719; TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, DISABLE); // 初始化时不开启等DMA就绪再开 // DMA1 通道1 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 SAMPLE_NUM; 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_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel1, DMA_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); }注意几个关键设置ADC工作在不连续单次模式每次由定时器更新事件触发一次转换采样时间选了13.5周期折合ADC时钟14MHz下约0.96us加上12.5周期转换时间单次总耗时不到2us远小于10us的触发间隔DMA用了Normal模式每次采满1000点就停止在传输完成中断里再做处理避免CPU频繁抢占。4.2 主循环识别流程采样和识别的流程是“中断里取数据主循环里做计算”。DMA传输完成中断里只做一件事把dma_done标志位置1清中断标志。主循环检测到这个标志后执行完整的分析和识别while (1) { if (dma_done) { dma_done 0; Wave_Params params; uint8_t type Wave_Classify(adc_buf, SAMPLE_NUM, params); // type: 0未知, 1正弦波, 2方波, 3三角波 printf(type%d, freq%.2fHz, vpp%.3fV\n, type, params.freq, params.vpp); TIM_Cmd(TIM2, ENABLE); // 触发下一轮采样 } }Wave_Classify函数内部按顺序执行找极值、过零测频、归一化、直方图统计、判据分类。整个函数在1000个点上执行约3到5ms也就是说识别结果每秒能刷新200次以上对于波形类型显示来说完全够用。4.3 实测数据与识别效果我用信号发生器分别输出1kHz、幅度2Vpp的正弦波、方波和三角波做了20次连续识别测试结果如下输入波形频率识别误差识别成功率平均耗时正弦波0.15%20/204.2ms方波0.12%20/203.8ms三角波0.18%20/204.5ms频率识别误差主要来自过零检测的量化误差——采样间隔是10us对于1kHz信号一个周期100个采样点过零时间的分辨率就是1%所以0.15%的误差在预期之内。如果想提高频率精度可以改用“线性插值过零”或者“定时器输入捕获”但对波形类型识别来说这个精度已经足够了。三种波形的中间段占比实测值也很有说服力方波是0.02左右正弦波是0.23左右三角波是0.34左右。离散度很小判定阈值留在中间空白区可靠性很高。5. 常见问题与避坑指南5.1 噪声导致误判的处理如果信号噪声比较大直方图会模糊化方波的中间段占比会从接近0上升到0.1以上很可能被误判成正弦波。我测试时用了一个法先做一次简单的滑动平均滤波窗口大小取3到5个点代价是有效采样率会下降但对于波形类型识别来说完全够用。更好的办法是多次采样取平均——比如把10ms窗口拉长到50ms采5帧数据直方图做5次累加再判断噪声会被平均掉特征更稳定。5.2 频率不在设计范围的适配这个方案对信号频率是有适应范围的核心约束是“每个周期至少要有30个采样点”。如果按100kHz采样率最高可识别约3.3kHz的信号。信号频率更高怎么办两种思路一是提高采样率把ARR值调小比如ARR 239时采样率300kHz但要注意ADC采样时间需相应缩短二是降低采样率以覆盖低频比如测50Hz工频信号可以把ARR调到1439采样率变成50kHz一个周期1000个点。我建议把采样率设计成可配置的识别前先根据预期信号频率设置好参数或者先以较高采样率粗测一遍频率再动态调整到最佳采样率做精测。5.3 ADC采样精度的实用细节STM32F103的ADC是12位理论上分辨率有4096级但实际使用中要注意参考电压的稳定性。如果你的板子用LDO直接从USB取电ADC参考电压会随负载波动导致测量结果出现整体漂移。我在项目里单独用了一个3.3V基准源给VREF供电波形识别结果明显更稳定。另外ADC输入阻抗问题很隐蔽。STM32的ADC是逐次逼近型内部有采样电容输入信号源阻抗过高时采样电容还没充满就被切断了导致测量值偏小。官方手册的指标是输入阻抗建议不超过10kΩ我实测用的信号发生器输出阻抗50Ω一点问题没有。如果是高阻信号源比如直接从运放输出接过来建议加一级电压跟随器做缓冲。5.4 遇到问题怎么排查调试这个项目如果遇到识别结果不稳定先别急着改算法按这个顺序排查首先用printf把原始数据的最大值、最小值、平均值打出来确认信号幅度在合理区间没有削顶或过小然后看频率识别结果是否与实际相符如果频率差很多检查触发配置和过零检测的滞回阈值最后把直方图数据打印出来看分布形状跟预期对比基本能定位问题出在采集端还是识别端。有一次我怎么都识别不出方波打印直方图发现中间段占比0.35活脱脱一个三角波的样子。后来查了半天是信号源输出的方波上升沿太慢——那个老旧的信号发生器上升时间将近50us1kHz方波一个周期1000us上升沿占了5%但更关键的是上升过程经历了整个中间幅值段相当于在直方图里铺了一层“均匀分布”把原本应该集中在两端的统计完全改变了。换了一台上升沿1us以内的信号源立刻恢复正常。这个案例说明识别算法判断的是波形形状如果前端信号本身就不“标准”再好的算法也没用。6. 这个方案的扩展思路波形识别做出来之后能延伸的方向其实挺多的。我自己后面又加了一个自动增益控制环节用一个数字电位器做输入分压让ADC采样值的峰峰值始终接近满量程这样不管输入信号是0.1V还是3V都能稳定识别不用手动调幅度。还可以加一个FFT模式做对比验证。平时用直方图法做快速识别遇到不确定的波形再切到FFT看频谱细节两种算法互为备份。STM32官方DSP库里有做好的cr4_fft_256_stm32函数参考例程很多接上去也不是很费劲。直方图法负责“快”FFT负责“准”分工明确。如果想跟其他设备联动比如把识别结果通过串口、Wi-Fi或CAN发出去只需要在Wave_Classify返回类型之后加一个协议打包的函数即可。我做过一个版本把波形类型和频率编码成几个字节通过串口发到上位机上位机用简单脚本就能实时显示当前信号类型整个系统就是一个迷你的“智能示波器前端”。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于QT的TCP调试助手源码解析:从协议原理到工程实践 2026/9/2 20:29:57

基于QT的TCP调试助手源码解析:从协议原理到工程实践

简介:这是一份基于C#实现的TCP调试助手完整源码包,主要面向网络编程学习者和需要验证TCP通信的开发者,可用于快速搭建TCP客户端与服务端环境,完成协议调试、数据收发测试和连接状态分析。压缩包内共包含53个文件,其中c…

阅读更多 →
AssetStudio多语言版:Unity资源提取与AssetBundle解包实战指南 2026/9/2 20:29:57

AssetStudio多语言版:Unity资源提取与AssetBundle解包实战指南

简介:AssetStudio是一款开源的Unity游戏资源提取工具,这套多语言汉化版面向游戏开发爱好者、美术人员和逆向分析人群,能快速查看并导出未加密的Unity资源,尤其适合2D手游立绘的批量提取。资源包共120个文件,总计14.05M…

阅读更多 →
AssetStudio多语言版完全指南:Unity游戏资源提取与解包实战 2026/9/2 20:29:57

AssetStudio多语言版完全指南:Unity游戏资源提取与解包实战

简介:AssetStudio(简称AS)多语言版是一款专门用于Unity游戏资源提取的开源免费工具,面向游戏开发学习者、美术素材研究者、独立游戏爱好者以及需要逆向分析游戏资源的用户。它能够解包大多数未加密的Unity资源文件,最常…

阅读更多 →
ADSv1.2安装包完整指南:解压校验、License配置与报错排查 2026/9/2 20:29:57

ADSv1.2安装包完整指南:解压校验、License配置与报错排查

简介:ADSv1.2是一款面向电子设计自动化场景的集成开发环境,适合硬件工程师、嵌入式开发者和高校相关专业学生使用,可在同一工作界面内完成代码编辑、编译、调试、运行等操作,提升开发效率。这种一体化设计免去了在多个工具间切换的…

阅读更多 →
Windows Server 2012 R2离线安装.NET Framework 3.5:SxS源与DISM实战 2026/9/2 20:29:57

Windows Server 2012 R2离线安装.NET Framework 3.5:SxS源与DISM实战

简介:Windows Server 2012 R2 系统默认未安装 .NET Framework 3.5,部分旧版应用或特定服务运行时需要该框架,常因缺少 SXS 文件而无法正常安装。这一压缩包正是为解决此问题而整理,面向服务器运维、开发部署与技术支持人员&#x…

阅读更多 →
Typora 1.9.4免安装版配置与使用指南:绿色便携、主题迁移与图片管理 2026/9/2 20:26:55

Typora 1.9.4免安装版配置与使用指南:绿色便携、主题迁移与图片管理

简介:Typora免安装版1.9.4是一款以所见即所得著称的Markdown编辑器绿色版,面向需要轻量写作、又不想经历常规安装流程的程序员、作家及内容创作者,特别适合在网吧、共享电脑或缺少管理员权限的公司电脑上临时使用。压缩包共214个文件&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞