拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32G4 ADC高精度采集实战:硬件过采样与软件滤波优化指南

1. 为什么STM32 G4的ADC值得单独拿出来聊如果你用过STM32F1或者F4系列的ADC再换到G4第一感觉可能是“寄存器怎么又变了”。但真正跑过一轮高精度采集之后你会发现G4的ADC不是小修小补它在模拟前端和数字后处理上都做了实质性增强。STM32G4系列定位就是混合信号MCUADC采样率最高可以到4Msps12位模式下硬件过采样器支持最高256倍累加配合16位分辨率输出这在电机控制、音频采集、精密传感器读取这些场景里非常实用。我最近做了一个项目用STM32G474采集一个桥式传感器的微弱差分信号满量程大概±20mV噪声要求控制在等效输入10uV以内。一开始我直接用12位单次采样结果跳得没法看。后来把硬件过采样开到64倍、配合DMA循环搬运和软件端滑动平均才把有效位数ENOB从9位多拉到接近13位。这个过程踩了不少坑也积累了一些在官方手册里不会明说的经验。这篇内容适合两类人一是刚接触G4系列、想搞清楚ADC过采样到底怎么配的嵌入式新手二是已经在用G4但采集精度卡在某个瓶颈、想从硬件和软件两个层面系统优化的工程师。我会从过采样的原理讲起把配置参数的计算过程拆开再讲软件滤波的选型和实测对比最后给出一套可以直接复用的代码框架。全程不堆术语尽量用“为什么这么设”来解释每一步。2. 硬件过采样在G4上到底是怎么工作的2.1 过采样的本质用速度换分辨率过采样的核心逻辑其实很朴素如果你对同一个信号连续采样N次再求平均白噪声会按照√N的规律被压低而信号本身不变。理论上每4倍过采样可以多获得1位有效分辨率这就是常说的“4倍过采样换1位”。STM32G4的硬件过采样器把这个过程做进了ADC内部不需要CPU干预也不占用额外的采样时间窗口。具体来说G4的ADC过采样单元支持两种模式累加模式和平均模式。累加模式是把N次转换结果直接相加输出位宽会扩展平均模式是相加后右移保持输出位宽不变。举个例子12位ADC开16倍过采样累加模式下输出是16位124平均模式下输出还是12位但数值更平滑。很多人一开始搞不清这两个模式的区别其实记住一点就行累加模式保留全部精度适合后续还要做数字处理的场景平均模式直接给你一个滤波后的结果适合对实时性要求高、不想在软件里再算的场景。2.2 过采样倍率和移位配置的计算G4的过采样配置涉及三个关键参数Oversampling ratio倍率、Oversampling shift右移位数、Oversampling mode模式。这三个参数不是随便设的它们之间有一个硬性约束右移位数必须小于等于log2(倍率)否则输出会溢出或者丢失有效位。我整理了一个常用配置的对照表方便你直接查过采样倍率累加后位宽推荐右移位数最终输出位宽理论增益2x13位0或113位或12位0.5位4x14位0~214~12位1位16x16位0~416~12位2位64x18位0~618~12位3位256x20位0~820~12位4位这里有个容易踩的坑右移位数不是越大越好。右移确实能去掉低位噪声但如果你右移太多有效信号也被截掉了。我的经验是右移位数取log2(倍率)的一半左右比较稳妥。比如64倍过采样右移3位既压了噪声又保留了足够的动态范围。另外要注意G4的过采样器在累加模式下如果倍率超过16倍累加结果会超过16位这时候你必须用32位变量来接收DMA搬运的数据否则高位会被截断。我第一次配64倍过采样时就是用了uint16_t结果数据一直不对查了半天才发现是数据类型的问题。2.3 触发源和采样时间的配合过采样不是独立工作的它依赖ADC的常规转换触发。G4的ADC支持多种触发源软件触发、定时器触发、外部中断触发等。在高精度采集场景里我强烈建议用定时器触发DMA循环的方式。原因很简单定时器触发能保证采样间隔严格均匀这对过采样的噪声抑制效果至关重要。如果采样间隔抖动过采样对低频噪声的抑制能力会大打折扣。采样时间Sampling Time的设置也直接影响精度。G4的ADC采样时间可以配到2.5到640.5个ADC时钟周期。采样时间太短采样保持电容充不满读数偏低采样时间太长整体吞吐率下降。对于高阻抗信号源比如传感器输出阻抗大于10kΩ采样时间至少要设到247.5个周期以上。我一般会先用一个电位器模拟信号源从最短采样时间开始逐步加长观察读数什么时候稳定下来那个点就是你的最小安全采样时间。提示G4的ADC时钟最高可以到60MHz但过采样开启后实际转换时间会乘以倍率。比如12位转换本身需要12.5个周期开64倍过采样后一次完整转换需要12.5×64800个周期。如果你用60MHz时钟单次转换就要13.3微秒采样率降到75ksps左右。这个换算在做系统时序规划时一定要提前算清楚。3. 从寄存器到CubeMX过采样配置的完整落地3.1 CubeMX里的关键选项和隐藏陷阱用CubeMX配置G4的ADC过采样界面看起来挺直观但有几个选项藏得比较深。在ADC配置页的“Oversampling”标签下你需要依次设置Mode、Ratio、Shift和Triggered Mode。这里有个细节Triggered Mode这个选项控制的是过采样是否在每次触发后重新开始累加。如果你用的是定时器周期触发这个选项必须使能否则过采样器会在第一次触发后一直累加永远不输出结果。还有一个容易忽略的地方是DMA配置。过采样开启后DMA的数据宽度必须和ADC输出位宽匹配。12位过采样到16位DMA要配成Half Word如果累加到18位或20位DMA必须配成Word。CubeMX有时候不会自动帮你改这个设置需要手动去DMA Settings里调整。我在实际项目里还遇到过一个情况开了过采样之后ADC的EOCEnd of Conversion中断触发频率变了。原本每次转换都触发现在变成每N次转换才触发一次。如果你在中断里做数据处理要记得把中断优先级和DMA传输完成中断配合好不然会出现数据覆盖或者丢帧。3.2 手写寄存器的核心代码框架虽然CubeMX能生成初始化代码但过采样的核心配置我建议还是自己写一遍这样出了问题知道去哪查。下面是一个基于G474的ADC过采样初始化片段用LL库写的逻辑比较清晰// 使能ADC时钟和过采样 LL_ADC_Enable(ADC1); LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_GRP_REGULAR_CONTINUED); LL_ADC_ConfigOverSamplingRatioShift(ADC1, LL_ADC_OVS_RATIO_64, LL_ADC_OVS_SHIFT_RIGHT_3); LL_ADC_SetOverSamplingDiscont(ADC1, LL_ADC_OVS_REG_CONT); // 配置触发源为TIM2 TRGO LL_ADC_REG_SetTriggerSource(ADC1, LL_ADC_REG_TRIG_EXT_TIM2_TRGO); LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING); // 设置采样时间 LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_247CYCLES_5); // 使能DMA循环模式 LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);这段代码里LL_ADC_OVS_GRP_REGULAR_CONTINUED表示过采样作用于常规通道组且连续进行LL_ADC_OVS_REG_CONT表示过采样结果连续输出不中断。这两个参数配合使用才能让过采样器在每次触发后自动完成N次累加并输出一个结果。3.3 校准和偏移处理不能省G4的ADC自带校准功能但很多人初始化完就直接开始采样跳过了校准步骤。校准能消除内部电容失配带来的固定偏移对高精度采集来说是必须的。校准的流程是先使能ADC电压调节器等待稳定然后启动校准轮询等待校准完成标志。校准完成后你还可以用LL_ADC_SetOffset设置一个偏移量把零点附近的读数平移到一个合适的范围。比如你的信号是双极性的经过差分放大后变成0.5V到2.5V的单极性信号你可以设一个偏移让零点对应到1.5V这样正负半周都有足够的动态范围。注意校准必须在ADC使能之前完成而且校准期间不能有触发信号进来。我一般会把校准放在所有外设初始化之后、主循环之前确保没有干扰。4. 软件滤波怎么选从滑动平均到卡尔曼的实测对比4.1 滑动平均最简单但不一定最合适硬件过采样之后数据已经平滑了不少但如果你对精度要求更高软件端还可以再加一层滤波。滑动平均是最容易实现的维护一个环形缓冲区每次新数据进来就替换最老的数据并重新求和。优点是计算量小、延迟固定缺点是它对突变信号的响应慢而且如果缓冲区长度选得不好会把有用的高频成分也滤掉。我在项目里试过窗口长度8、16、32、64的滑动平均配合64倍硬件过采样实测ENOB分别是12.1、12.6、12.9、13.0。可以看到窗口从8加到32有明显提升但32到64几乎没差别。所以窗口长度不是越大越好一般取硬件过采样倍率的1/2到1/4就够用了。4.2 中值滤波对付脉冲噪声的利器滑动平均对白噪声有效但如果信号里混了脉冲噪声比如电机换向时的尖峰滑动平均反而会把尖峰扩散到多个采样点上。这时候中值滤波更合适。中值滤波的做法是取最近N个采样值排序后取中间那个。N一般取奇数3、5、7比较常见。中值滤波的缺点是计算量比滑动平均大尤其是N比较大的时候。不过在G4上Cortex-M4内核跑120MHz做7点中值滤波的排序完全不是瓶颈。我一般会把中值滤波和滑动平均串起来用先中值去脉冲再滑动平均去白噪声。这个组合在电机电流采集场景里效果非常好。4.3 一阶低通滤波参数怎么算一阶低通滤波的公式是y[n] α·x[n] (1-α)·y[n-1]其中α是滤波系数。α越小滤波效果越强但相位滞后也越大。α的取值和截止频率的关系是α 1 - exp(-2π·fc/fs)其中fc是截止频率fs是采样率。举个例子采样率10ksps想要截止频率100Hz算出来α≈0.062。这个滤波器在代码里就是一行乘加效率极高。但一阶低通的缺点是对低频噪声抑制不够。如果你的噪声主要集中在低频段比如50Hz工频干扰一阶低通需要把截止频率设得很低这样有用信号的带宽也被压缩了。这时候可以考虑二阶或更高阶的滤波器但计算量会上去。4.4 实测数据对比和选型建议我把几种滤波方案在同一个信号源上跑了一遍输入是1kHz正弦波叠加白噪声硬件过采样统一开64倍结果如下滤波方案ENOB延迟CPU占用适用场景无软件滤波11.8位无0%对实时性要求极高滑动平均(16)12.6位16个采样周期1%通用场景中值(5)滑动平均(16)12.9位21个采样周期2%有脉冲噪声一阶低通(α0.1)12.3位约10个采样周期1%低频信号中值(5)一阶低通(α0.05)12.7位约25个采样周期2%工频干扰严重从数据看中值滑动平均的组合在精度和延迟之间取得了最好的平衡。如果你的系统对延迟不敏感可以再加一级一阶低通进一步压噪声。但要注意每加一级滤波都会引入额外的相位滞后在闭环控制场景里要谨慎。5. 那些手册不会告诉你的实操细节5.1 电源和参考电压的噪声控制ADC精度再高如果参考电压不干净一切都是白搭。G4的VREF引脚可以外接参考源也可以用内部的VREFBUF。内部VREFBUF的初始精度大概±0.5%温漂也不小适合对绝对精度要求不高的场景。如果你要做精密测量建议外接一个低噪声的基准源比如ADR4525之类的。电源去耦也很关键。G4的VDDA和VREF引脚旁边一定要放100nF和1uF的电容而且尽量靠近引脚。我见过一个案例有人把去耦电容放在板子另一头结果ADC读数跳了十几个LSB把电容挪到引脚旁边立刻就稳了。5.2 PCB布局对ADC的影响模拟信号走线要远离数字信号尤其是时钟线和PWM线。如果实在避不开中间加一条地线做隔离。ADC输入引脚前面可以加一个RC低通截止频率根据信号带宽来定一般取信号最高频率的5到10倍。电阻不要太大否则会影响采样保持电容的充电一般100Ω到1kΩ比较合适。还有一点G4的ADC输入阻抗不是无限的。在高速采样时输入阻抗会下降对信号源的内阻有要求。如果你直接接一个高阻抗传感器读数会偏低。这时候要么加一个运放做缓冲要么把采样时间设到最长。5.3 温度漂移和长期稳定性高精度采集不是一次校准就完事的。ADC的偏移和增益会随温度变化如果你在户外或者温度变化大的环境里用最好定期做零点校准。我的做法是在系统里加一个“校准模式”上电时短接输入端到地采一组数据算偏移然后存到Flash里。运行过程中每隔一段时间比如每小时再校准一次。另外G4的ADC内部有一个温度传感器虽然精度不高但可以用来监测芯片温度。如果温度变化超过一定阈值就触发一次重新校准。这个策略在工业现场设备里很实用。6. 一套可以直接复用的采集框架6.1 整体架构设计我把整个采集链路分成四层硬件过采样层、DMA搬运层、软件滤波层、应用接口层。硬件过采样在ADC内部完成DMA负责把结果搬到内存软件滤波在定时器中断或者主循环里做应用接口层提供阻塞式和非阻塞式两种读取方式。这个架构的好处是每一层都可以独立调整。比如你想换滤波算法只需要改软件滤波层不影响底层配置。你想改采样率只需要调定时器周期其他层不用动。6.2 关键代码实现DMA缓冲区我一般开两个一个用于ADC搬运一个用于滤波处理用乒乓模式切换。这样能避免数据处理和DMA搬运之间的竞争。代码框架大概是这样#define OVS_RATIO 64 #define BUF_SIZE 32 uint32_t adc_dma_buf[BUF_SIZE]; uint16_t filter_buf[BUF_SIZE]; volatile uint8_t buf_ready 0; void ADC_DMA_Complete_Callback(void) { // DMA搬运完成切换缓冲区 for (int i 0; i BUF_SIZE; i) { filter_buf[i] (uint16_t)(adc_dma_buf[i] 3); } buf_ready 1; } uint16_t get_filtered_value(void) { static uint16_t window[16] {0}; static uint8_t idx 0; uint32_t sum 0; if (!buf_ready) return window[(idx - 1) 0x0F]; // 中值滤波取中间值 uint16_t temp[5]; for (int i 0; i 5; i) { temp[i] filter_buf[(idx i) % BUF_SIZE]; } // 简单冒泡排序取中值 for (int i 0; i 4; i) { for (int j i 1; j 5; j) { if (temp[i] temp[j]) { uint16_t t temp[i]; temp[i] temp[j]; temp[j] t; } } } uint16_t median temp[2]; // 滑动平均 window[idx 0x0F] median; idx; for (int i 0; i 16; i) sum window[i]; buf_ready 0; return (uint16_t)(sum 4); }这段代码里adc_dma_buf是32位数组因为64倍过采样累加后是18位必须用32位存。右移3位后变成15位用16位变量接没问题。中值滤波取5个点滑动平均窗口16这两个参数可以根据实际噪声情况调整。6.3 性能评估和调优建议这套框架在G474上跑ADC时钟60MHz定时器触发频率10kHz64倍过采样实际输出数据率是156Hz。CPU占用率不到5%RAM占用大概200字节。如果你需要更高的数据率可以降低过采样倍率或者提高ADC时钟但精度会有所下降。调优的时候建议先用信号发生器输入一个已知幅度的正弦波用DMA把原始数据传到上位机用Excel或者Python画个FFT看看噪声主要分布在哪个频段。如果是白噪声加滑动平均就行如果是特定频率的干扰用陷波滤波器更有效。不要盲目堆滤波级数每加一级都要问自己“这一级解决了什么问题”。提示调试ADC的时候建议把VREF和VDDA用示波器交流耦合看一下纹波。如果纹波超过几个毫伏先解决电源问题再调软件否则事倍功半。7. 常见问题排查和我的踩坑记录7.1 读数跳动大先查这三项ADC读数跳动是最常见的问题我一般按这个顺序排查第一看参考电压稳不稳用万用表或者示波器测VREF引脚纹波应该在1mV以内第二看采样时间够不够把采样时间设到最大再试如果跳动明显减小说明信号源内阻太高第三看地线回路模拟地和数字地是不是单点连接有没有形成地环路。有一次我调一个压力传感器读数一直跳换了三个传感器都一样。后来发现是传感器和MCU之间的地线太长形成了天线效应把周围的环境噪声耦合进来了。把地线缩短并加屏蔽之后问题解决。7.2 过采样开了但精度没提升这种情况通常是过采样配置有问题。检查这几个点过采样模式是不是设成了“Triggered Mode”右移位数是不是超过了log2(倍率)DMA数据宽度是不是匹配。还有一个隐蔽的问题过采样和ADC的连续转换模式不能同时用。如果你开了连续转换又开过采样过采样器会一直累加不输出。必须用触发模式每次触发完成一轮过采样。7.3 DMA传输丢数据DMA丢数据一般是缓冲区太小或者优先级冲突。G4的DMA有多个通道如果ADC的DMA通道和别的外设共用可能会被抢占。建议把ADC的DMA通道设成最高优先级缓冲区大小至少是过采样倍率的2倍。另外DMA的循环模式和普通模式要选对循环模式适合连续采集普通模式适合单次采集。7.4 校准后读数仍然有固定偏移校准只能消除ADC内部的偏移如果外部电路有偏移比如运放的输入失调电压校准是消不掉的。这时候需要在软件里做零点校正短接输入端采一组数据求平均把这个平均值作为零点偏移存起来后续所有读数都减去这个偏移。如果偏移随温度变化还需要做温度补偿。8. 写在最后的几点个人体会做高精度ADC采集硬件和软件是两条腿缺一条都走不远。我见过太多人把精力全花在滤波算法上结果硬件底子没打好怎么滤都达不到指标。也见过硬件做得很漂亮但软件配置一塌糊涂过采样开了等于没开。我的建议是先把硬件过采样和DMA跑通确认原始数据质量再考虑软件滤波。原始数据如果信噪比太低软件滤波是救不回来的。另外不要迷信高倍率过采样64倍和256倍的精度提升并没有想象中那么大但转换时间长了4倍对系统实时性的影响是实实在在的。最后分享一个我常用的调试技巧把ADC的原始数据和滤波后的数据同时通过串口传到上位机用Python画个对比图。这样你能直观地看到每一级滤波到底贡献了多少。工具不复杂但能帮你省下大量盲目试参数的时间。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门