资讯动态

ADC采样精度提升实战:从采样原理、取平均到精度分析

发布时间:2026/10/26 1:02:30 来源:尧图企业网站定制
1. 从一个“读数总在跳”的问题说起但凡做过嵌入式或者硬件相关的项目ADC 这个东西几乎绕不开。不管你是用 STM32 读个电位器、用 S32K312 采个电流还是拿专用 ADC 芯片做高精度数据采集最后都会撞上同一堵墙读出来的值为什么一直在跳明明输入是稳定的直流电压串口打印出来的数字却在最后两三位来回抖有时候甚至能跳几十个 LSB。新手第一反应往往是“芯片坏了”或者“代码写错了”但折腾一圈之后会发现问题通常出在对采样原理、取平均策略和精度边界的理解上。这篇内容就是把我这些年踩过的 ADC 相关的坑做一次系统汇总。核心围绕四件事展开采样原理到底在采什么、取平均该怎么取才有效、精度受哪些因素制约、以及工程上怎么把这些东西落地成能用的代码。适合刚接触 ADC 的嵌入式新手也适合做了几年但一直“能用就行”、没深究过噪声和精度来源的工程师。我不打算写成数据手册的复述而是按实际项目里遇到的顺序来讲把每个选择背后的理由说清楚让你看完能直接对着自己的板子改代码、调参数。先给一个最朴素的认知ADC 不是“读一次就准”的器件它是一个把连续模拟量切成离散数字量的过程这个过程天然带着量化误差、噪声和时间上的不确定性。你后面做的所有滤波、平均、校准本质上都是在跟这些不确定性做斗争。理解了这一点很多“玄学”现象就都有解释了。2. ADC 采样原理你到底在采什么2.1 采样、量化、保持三步缺一不可很多人把 ADC 理解成一个“电压转数字”的黑盒输入多少伏输出对应数字完事。但真实过程分三步采样、量化、编码中间还有一个关键的采样保持环节。采样是在某个时间点上“抓”住输入电压的瞬时值。这里有个容易被忽略的点采样不是瞬间完成的SAR ADC 内部有一个采样电容需要一定时间充电到输入电压。这个时间就是采样时间Sampling Time在 STM32 里对应寄存器里的 SMP 位。如果采样时间太短电容没充到位转换结果就会偏低而且这个偏低跟输入阻抗、信号源内阻强相关。量化是把抓到的电压映射到有限的数字刻度上。一个 N 位 ADC把参考电压 Vref 分成 2^N 份每一份就是一个LSBLSB Vref / 2^N比如 12 位 ADC、Vref 3.3V那么 LSB 3.3 / 4096 ≈ 0.806 mV。也就是说理论上你最小能分辨 0.8mV 的变化。但注意这只是“分辨力”不等于“精度”这两个概念后面会专门讲。采样保持是在转换期间把采到的电压“冻住”因为 SAR 转换需要若干个时钟周期逐次逼近如果输入在这期间变化转换结果就乱了。所以采样保持电路的质量直接决定了高速信号采样的准确性。提示采样时间不是越短越好。信号源内阻越大需要的采样时间越长。STM32 的参考手册里有一张“最大外部阻抗 vs 最短采样时间”的对应表选采样时间时一定要对照这张表而不是随手填个最小值。2.2 采样定理与混叠为什么你的波形看起来是错的奈奎斯特采样定理说采样频率必须大于信号最高频率的 2 倍才能无失真恢复原信号。但工程上这条定理经常被误用因为它只保证了“理论上可恢复”没保证“你采到的样子是对的”。举个实际例子。你用 1kHz 的采样率去采一个 900Hz 的正弦波按定理 2 倍是 2kHz1kHz 不够会混叠。但如果你采一个 600Hz 的信号1kHz 采样率满足不了 2 倍需要 1.2kHz同样混叠。混叠之后600Hz 的信号会“伪装”成一个 400Hz 的信号出现在你的数据里你根本分辨不出来。这就是为什么很多人在示波器上看波形好好的一进 ADC 就变形——采样率不够高频成分折叠回来了。解决混叠的办法有两个提高采样率或者在 ADC 前面加抗混叠滤波器低通。抗混叠滤波器的截止频率要设在采样率的一半以下把高于奈奎斯特频率的成分提前滤掉。实际项目中如果信号本身带宽不高比如温度、压力这类慢变量一个简单的 RC 低通就够了如果是音频或者振动信号就得认真设计滤波器阶数。2.3 采样周期怎么定不是越快越好采样周期或者说采样率的选择取决于你的信号变化速度和你要捕捉的细节。这里给一个实操中的经验法则采样率至少是信号最高频率成分的 5 到 10 倍而不是卡着 2 倍。2 倍只是理论下限实际工程里留足余量才能保证波形形状和幅值都准。但采样率也不是越高越好。采样率越高单位时间内的数据量越大后续处理压力越大同时高速采样对 ADC 的建立时间、时钟抖动、电源噪声都更敏感反而可能让精度下降。我见过有人为了“保险”把采样率拉到 ADC 的极限结果噪声比低速时还大就是因为高速下采样保持电路和参考电压的瞬态响应跟不上。对于 STM32 这类 MCU 内置 ADC还有一个现实约束ADC 时钟频率有上限比如 F1 系列是 14MHzF4 是 36MHz采样时间加转换时间决定了单次转换的总时长。如果你用定时器触发 ADC触发频率必须大于这个总时长的倒数否则转换还没完成就被下一次触发打断数据就废了。3. 取平均最常用也最容易用错的滤波手段3.1 为什么取平均能降噪白噪声的统计特性取平均之所以有效是因为大多数噪声是零均值的随机噪声。假设真实值是 V每次采样得到 V n_i其中 n_i 是均值为 0、方差为 σ² 的噪声。那么 N 次采样的平均值V_avg (1/N) * Σ(V n_i) V (1/N) * Σn_i噪声部分的方差变成 σ²/N标准差变成 σ/√N。也就是说取 N 次平均噪声幅度理论上降低到原来的 1/√N。取 16 次平均噪声降到 1/4取 256 次降到 1/16。这就是为什么过采样加平均能提升有效分辨率——每 4 倍过采样理论上多出 1 位有效分辨率。但这里有个前提噪声必须是随机的、零均值的。如果噪声里有周期性成分比如工频干扰、开关电源纹波取平均是压不下去的因为周期性噪声在多次采样中不会相互抵消。这时候你需要的是同步采样或者陷波滤波而不是简单平均。3.2 算术平均、滑动平均、中值平均各管什么工程上常用的平均方式有三种各有适用场景平均方式原理优点缺点适用场景算术平均连续采 N 次求平均实现简单降噪效果明确占用连续采样时间响应慢慢变量、静态测量滑动平均维护一个 N 长度窗口每次新数据进、旧数据出响应快数据连续输出需要缓存窗口大时占内存实时监控、连续采集中值平均采 N 次排序取中间值能剔除脉冲干扰计算量大对连续噪声效果一般有突发尖峰干扰的场合算术平均适合温度、电池电压这种变化很慢的量。滑动平均适合需要连续输出的场合比如实时显示。中值平均的杀手锏是剔除野值——如果信号里偶尔混进一个特别大或特别小的干扰脉冲算术平均会被它拉偏但中值平均直接把它排到边上不影响结果。实际项目中我经常把中值和算术平均组合起来用先采 N 次去掉最大和最小的几个剩下的再求平均。这样既剔除了脉冲干扰又降低了随机噪声。这个组合在电机电流采样、传感器读数处理里特别常见。3.3 取平均的陷阱什么时候平均反而有害取平均不是万能的用错了反而会引入问题。第一个陷阱是信号本身在变化。如果你对一个正在上升或下降的信号做算术平均平均结果会滞后于真实值相当于给信号加了一个低通滤波相位延迟了。对于控制系统里的反馈量这个延迟可能导致振荡。第二个陷阱是平均次数过多导致响应迟钝。我见过一个项目为了追求“稳定”对按键检测的 ADC 值取 256 次平均结果按键响应慢半拍用户体验很差。平均次数要根据信号的实际变化速度来定不是越多越好。第三个陷阱是在中断里做大量平均计算。如果平均是在 ADC 中断里完成的N 很大时中断执行时间会很长影响其他任务的实时性。正确的做法是在中断里只存数据平均放到主循环或者用 DMA 搬运后统一处理。提示取平均的次数最好是 2 的幂次4、8、16、32……这样除法可以用右移实现在 MCU 上省下除法指令的开销。这个细节在资源紧张的 8 位机上尤其重要。4. 精度分辨力、准确度、稳定度别混为一谈4.1 分辨力不等于精度这是最容易被混淆的一对概念。分辨力是 ADC 能区分的最小电压变化由位数决定12 位就是 12 位改不了。精度是测量值与真实值的接近程度受参考电压、噪声、非线性、温漂等一大堆因素影响。一个 12 位 ADC 的分辨力是 0.8mV但如果参考电压本身有 1% 的误差你的绝对精度就只有 33mV 左右远差于分辨力。所以“12 位精度”这种说法本身就是不严谨的应该说“12 位分辨力精度取决于参考和前端设计”。影响精度的主要因素有这么几个参考电压精度和温漂Vref 是测量的基准它不准后面全不准。内置参考通常只有 1% 左右精度要求高时要用外部精密基准。ADC 本身的 INL 和 DNL积分非线性和微分非线性决定了转换曲线的偏离程度。噪声包括 ADC 内部噪声和外部耦合进来的噪声。前端电路信号源内阻、运放失调、滤波器的幅频特性都会影响最终精度。4.2 有效位数 ENOB衡量真实精度的硬指标数据手册上标的都是理想位数但实际能用到几位要看ENOBEffective Number of Bits有效位数。ENOB 是通过测量 ADC 的信噪比和失真SINAD反推出来的ENOB (SINAD - 1.76) / 6.02一个标称 12 位的 ADC如果 SINAD 只有 60dB那么 ENOB (60 - 1.76) / 6.02 ≈ 9.7 位。也就是说你实际能信得过的只有不到 10 位最后两位全是噪声。这个计算在选型和评估时非常有用别只看标称位数。提升 ENOB 的手段除了前面说的过采样加平均还有抖动注入dithering。原理是人为加入一点随机噪声让量化误差随机化再通过平均把噪声压下去从而提升有效分辨率。这个方法在音频 ADC 和高精度测量里很常见但在普通 MCU 项目里用得不多因为实现起来需要额外的噪声源和更复杂的处理。4.3 参考电压精度的天花板参考电压的重要性怎么强调都不过分。ADC 的转换结果本质上是数字值 (Vin / Vref) * 2^NVref 一变同样的 Vin 算出来的数字值就变了。内置参考电压通常来自 MCU 的电源或者内部基准精度和温漂都不理想。如果项目对精度有要求一定要用外部基准芯片比如常见的 2.5V 或 4.096V 精密基准。选外部基准时关注三个参数初始精度比如 ±0.1%、温漂比如 10ppm/°C、长期稳定性。初始精度决定了常温下的绝对误差温漂决定了温度变化时的误差长期稳定性决定了用几年之后还准不准。这三个参数里温漂往往是最容易被忽略但影响最大的。还有一个实操细节参考电压的去耦。Vref 引脚旁边一定要放一个低 ESR 的电容通常 0.1uF 加 10uF 组合而且要尽量靠近引脚。参考电压上的任何纹波都会直接调制到转换结果上这个坑我踩过不止一次。5. 实操从采样到滤波的完整代码实现5.1 STM32 多通道 ADC 采样的配置要点以 STM32 为例多通道采样通常有两种方式扫描模式 DMA或者单通道轮流切换。扫描模式配 DMA 是最省心的ADC 按顺序转换多个通道DMA 自动把结果搬到数组里CPU 几乎不参与。配置时有几个关键点采样时间按通道单独设置。不同通道的信号源内阻可能不同采样时间要分别设置。STM32 的规则组每个通道都有独立的 SMP 位。DMA 缓冲区大小要匹配通道数。比如 4 个通道DMA 缓冲区至少 4 个元素循环模式下会自动覆盖。通道切换后要丢弃第一次转换结果。切换通道时采样保持电容上还残留着上一个通道的电压第一次转换结果是不准的。稳妥的做法是切换后连续转换两次取第二次的值。// STM32 HAL 库多通道扫描 DMA 配置示例 ADC_ChannelConfTypeDef sConfig {0}; // 通道0采样时间 71.5 周期 sConfig.Channel ADC_CHANNEL_0; sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLETIME_71CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); // 通道1采样时间 239.5 周期信号源内阻较大 sConfig.Channel ADC_CHANNEL_1; sConfig.Rank ADC_REGULAR_RANK_2; sConfig.SamplingTime ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); // 启动 DMA 传输缓冲区 adc_buf[2] HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, 2);5.2 一个可复用的 ADC 滤波函数下面这个函数把中值剔除和算术平均结合起来适合大多数传感器采样场景。思路是采 N 次排序去掉最大最小的各 K 个剩下的求平均。#define ADC_SAMPLE_NUM 16 #define ADC_TRIM_NUM 2 uint16_t adc_filter(uint8_t channel) { uint16_t buf[ADC_SAMPLE_NUM]; uint32_t sum 0; uint8_t i, j; uint16_t tmp; // 采集 N 次 for (i 0; i ADC_SAMPLE_NUM; i) { buf[i] adc_read_once(channel); } // 冒泡排序N 小的时候够用 for (i 0; i ADC_SAMPLE_NUM - 1; i) { for (j 0; j ADC_SAMPLE_NUM - 1 - i; j) { if (buf[j] buf[j 1]) { tmp buf[j]; buf[j] buf[j 1]; buf[j 1] tmp; } } } // 去掉最大最小各 TRIM 个剩下的求平均 for (i ADC_TRIM_NUM; i ADC_SAMPLE_NUM - ADC_TRIM_NUM; i) { sum buf[i]; } return (uint16_t)(sum / (ADC_SAMPLE_NUM - 2 * ADC_TRIM_NUM)); }这个函数有几个可以调的地方ADC_SAMPLE_NUM越大降噪越好但响应越慢ADC_TRIM_NUM越大抗脉冲干扰越强但会损失一些有效数据。一般 16 次采样、去掉 2 个最大最小是个比较平衡的默认值。5.3 滑动平均的环形缓冲实现如果需要连续输出且响应快滑动平均更合适。用环形缓冲实现每次新数据进来覆盖最旧的数据同时更新累加和。#define MOVING_AVG_WIN 8 typedef struct { uint16_t buf[MOVING_AVG_WIN]; uint32_t sum; uint8_t idx; uint8_t count; } moving_avg_t; uint16_t moving_avg_update(moving_avg_t *ma, uint16_t new_val) { // 减去最旧的值加上最新的值 ma-sum - ma-buf[ma-idx]; ma-buf[ma-idx] new_val; ma-sum new_val; ma-idx (ma-idx 1) % MOVING_AVG_WIN; if (ma-count MOVING_AVG_WIN) { ma-count; } return (uint16_t)(ma-sum / ma-count); }这个实现的好处是每次更新只做一次减法和一次加法计算量恒定跟窗口大小无关。窗口大小取 2 的幂时取模运算可以用位与代替进一步提速。提示滑动平均的初始阶段缓冲区还没填满除数要用实际数量 count 而不是窗口大小否则前几个输出会偏小。这个细节不注意的话上电瞬间的读数会明显偏低。6. 常见问题与排查实录6.1 读数跳动大怎么一步步定位读数跳动是最常见的问题排查要按顺序来别一上来就改代码。我的排查顺序是这样的先看硬件。用示波器或者万用表交流档测 ADC 输入引脚看信号本身是否稳定。如果输入本身就在跳那是前端电路的问题改代码没用。检查参考电压。测 Vref 引脚看是否有纹波。参考电压不稳所有通道都会跳。检查地线。模拟地和数字地是否单点连接ADC 的地回路是否干净。地线上的噪声是最隐蔽的干扰源。检查采样时间。信号源内阻大而采样时间短会导致采样电容充不满读数偏低且不稳定。试着把采样时间调大。最后才怀疑代码。确认没有在中断里做耗时操作DMA 配置正确通道切换后丢弃了第一次结果。这个顺序的核心逻辑是先排除硬件再怀疑软件。因为硬件问题改代码是治不好的而软件问题往往有明显的规律性比如固定偏差、固定周期跳动。6.2 常见问题速查表现象可能原因排查方法解决方向读数整体偏低采样时间不足、参考电压偏低加大采样时间、测 Vref调整 SMP、换基准读数周期性跳动工频干扰、开关电源纹波示波器看频谱加滤波、同步采样读数随机跳动噪声、地线干扰短接输入看是否还跳加去耦、改地线通道间串扰采样保持电容残留切换通道后看第一次值丢弃首次转换高温下精度变差参考温漂、运放失调温漂高低温测试对比换低温漂基准读数偶尔出现极值脉冲干扰、EMI观察极值出现规律中值滤波、加屏蔽6.3 几个容易被忽略的实操心得第一个心得ADC 输入引脚不要直接接高阻抗信号源。STM32 的 ADC 输入阻抗不是无穷大信号源内阻太大时分压效应会让读数偏低。如果信号源内阻超过几十 kΩ中间要加一个电压跟随器运放缓冲。第二个心得校准要在实际工作温度下做。常温校准好的系统到了高温环境可能偏几十个 LSB。如果产品工作温度范围宽要么用低温漂器件要么做温度补偿。第三个心得别迷信过采样。过采样加平均确实能提升有效分辨率但前提是噪声是随机的。如果噪声里有周期性成分过采样反而会把周期性噪声“平均”进结果里越采越乱。先用示波器确认噪声性质再决定用不用过采样。第四个心得ADC 的电源要单独滤波。很多 MCU 的 VDDA 和 VDD 是分开的引脚VDDA 一定要加 LC 或者 RC 滤波否则数字部分的开关噪声会通过电源耦合进 ADC。这个在高速数字电路里尤其明显。7. 精度提升的进阶思路7.1 过采样与抽取滤波过采样提升分辨率是有严格数学基础的。每 4 倍过采样理论上可以多获得 1 位有效分辨率前提是噪声足够随机。实现方式是以远高于信号带宽的频率采样然后做低通滤波和抽取降采样。比如你要测一个 10Hz 以内的慢信号正常采样率 100Hz 就够。但你可以用 25.6kHz 采样采 256 次平均得到 100Hz 的输出。这样有效分辨率能提升 4 位因为 256 4^4。一个 12 位 ADC 理论上能到 16 位当然实际受限于 ADC 本身的线性度和噪声能提升 2 到 3 位就不错了。这个技术在称重、温度测量、电池管理等慢速高精度场景里非常实用。STM32 的硬件过采样功能就是干这个的配置好之后硬件自动完成不占 CPU。7.2 前端电路对精度的影响ADC 的精度上限很多时候不是 ADC 本身决定的而是前端电路决定的。几个关键点运放的选择失调电压、失调温漂、噪声密度都要看。高精度场合要用零漂移运放或者斩波运放。滤波器的设计抗混叠滤波器的截止频率和阶数要匹配采样率。阶数越高过渡带越陡但相位延迟也越大。PCB 布局模拟走线和数字走线要分开模拟部分下面最好铺完整的地平面。ADC 输入走线尽量短远离时钟线和开关电源。我做过一个项目ADC 本身是 16 位的但前端运放失调温漂有 5uV/°C温度变化 20°C 就偏了 100uV相当于 16 位 ADC 的好几个 LSB。后来换了零漂移运放精度立刻上来了。所以选型时一定要把前端和 ADC 作为一个整体来评估。7.3 校准把系统误差消掉再好的器件也有初始误差校准是提升绝对精度的最后一道关。常用的校准方法有两种两点校准在量程的低端和高端各加一个已知标准值记录 ADC 读数算出斜率和截距之后所有读数都用这个线性关系修正。这个方法能同时消掉失调误差和增益误差。多点校准在量程内取多个标准点做分段线性或者多项式拟合。适合非线性比较明显的系统。校准的实操要点校准点要覆盖实际使用范围校准时的温度要接近工作温度校准用的标准源精度要远高于目标精度至少高一个数量级。校准系数存在 Flash 或者 EEPROM 里上电时读出来用。提示校准不是一劳永逸的。器件会老化温漂会变化高精度设备通常需要定期重新校准。如果产品生命周期长最好设计一个用户可触发的校准流程。8. 写在最后的一点个人体会ADC 这个东西入门容易精通难。刚开始做项目时我也觉得“读个数而已能有多复杂”直到被各种跳动、偏差、温漂折磨过几轮之后才慢慢理解为什么数据手册要写那么多页。采样原理决定了你能采到什么取平均决定了你能压掉多少噪声精度分析决定了你的系统到底能做到什么水平这三件事是环环相扣的。如果只让我给一条建议那就是先搞清楚你的信号和噪声长什么样再决定怎么采、怎么滤。用示波器看一眼输入信号比在代码里瞎调参数有用得多。很多所谓的“ADC 问题”本质上是信号完整性问题和地线问题跟 ADC 芯片本身没多大关系。另外别怕麻烦去读数据手册里的电气特性表。那些 INL、DNL、ENOB、温漂参数看着枯燥但都是选型和排查时的硬依据。我现在的习惯是拿到一个新 ADC先把电气特性表过一遍心里对它的能力边界有个数后面调试时就不会有不切实际的期望。这套采样、平均、精度分析的思路不只适用于 MCU 内置 ADC换成外置 ADC 芯片、专用采集卡逻辑是一样的。把底层原理吃透换什么平台都能快速上手。

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

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

免费获取报价 →
↑