做了这么多年嵌入式硬件和固件开发,我越来越觉得ADC才是整个采集链路里最值得花时间的器件。一块板子上所有信号最终都要变成数字才能被处理,而把模拟信号变数字这一下,恰好是误差进入系统最大的入口。数字化仪这个词听着唬人,本质上就是一台以ADC为核心的数据采集设备。不管是示波器里的采集前端、BMC里读取主板电压的ADC,还是STM32内部那颗逐次逼近型ADC,它们解决的是同一件事:把连续变化的模拟电压,变成处理器能计算的一串二进制数字。
如果你正在做数据采集、电源监控、传感器测量,或者也遇到过“ADC采样值总是跳”“多通道DMA数据错位”“同一颗芯片测出来的数值忽高忽低”这类问题,这篇文章应该能把原理和工程串起来。我会从数字化仪的整体链路说起,拆解SAR、Σ-Δ、Pipeline等主流架构的工作原理,再把采样周期、建立时间、量化噪声、时钟抖动这些指标讲透,最后落到前端调理电路、PCB布局、三点校准和C语言滤波这些可以直接抄作业的实践上。
1. 数字化仪到底在“读”什么:一张框图看懂完整链路
1.1 从传感器信号到寄存器里的一串数字
数字化仪就是把模拟信号变成数字信号的仪器,广义上它包含传感器、信号调理、采样保持、ADC转换、存储与处理这几个环节。很多人以为数字化仪就等于ADC,其实ADC只是里面最核心的一个部件,但恰恰是这个部件的指标决定了整台设备的性能上限。
模拟信号从物理世界出来,先要经过信号调理。比如热电偶的毫伏信号要放大,高压直流母线要分压衰减,电流信号要经过采样电阻转成电压。调理完成的电压进入ADC的采样保持电路。采样保持电路像一个高速快门,在某个瞬间把输入电压“冻”在内部电容上,随后ADC再把这个保持住的电压逐位量化成数字码。这个数字码经过对齐、校准和滤波之后,就变成了你在串口或者上位机里看到的那串十六进制数据。
很多人在这一步就忽略了采样保持的重要性。ADC标称的最高采样率是指它能连续完成“采样加转换”的速度,但前提是采样电容在切换瞬间能充分充电到当前电压。如果前级输出阻抗太大,采样电容充电时间不够,读出来的码值就会偏低,这种误差在数据手册里写得明明白白,可绝大概率是出在电路设计上,而不是ADC本身。
1.2 数字化仪系统的典型组成与真正的瓶颈
一套完整数字化仪可以抽象成四个部分:模拟前端、时钟源、ADC核心、数字后端。
- 模拟前端负责把待测信号幅度调整到ADC满量程附近,并完成抗混叠滤波。
- 时钟源给ADC提供采样时刻基准,时钟抖动直接决定高速采样时的信噪比上限。
- ADC核心执行量化。
- 数字后端负责缓存数据、触发控制、滤波标定和输出。
这四个部分里,最容易卡住系统指标的不是ADC本身,而是时钟和电源。ADC数据手册上的信噪比都是在理想时钟和理想电源条件下测出来的,实际项目里如果时钟抖动大、电源纹波高,ADC的动态性能会明显退化,这就是为什么很多工程师发现同一颗ADC在评估板上测得很好,换到自己的板子上性能就变差了。
对于普通MCU内部的ADC,时钟来源和电源质量同样关键。STM32的ADC用的是APB2总线时钟经过预分频得到的ADC时钟,这个时钟源不是专门为采样优化的,抖动属于可接受范围,但如果系统里有一堆开关电源而且布局很差,采样值跳动就非常明显。因此做数字化仪相关的设计,第一课就是明白:噪声是系统级的,不是ADC一颗芯片能扛住的。
2. 主流ADC架构逐一拆解:为什么有的ADC快、有的ADC准
2.1 SAR:逐次逼近,嵌入式系统里最常见的那个
SAR(Successive Approximation Register,逐次逼近寄存器)是嵌入式世界里占有率最高的ADC架构,STM32、GD32这些MCU内部集成的ADC基本都是SAR。它的工作原理很像猜数字游戏:主持人心里想一个0到1023之间的数,你每次猜一个数,主持人只告诉你大了还是小了,你用二分法在10次内就能猜中。
SAR ADC内部有一个比较器、一个数模转换器(DAC)和逐次逼近逻辑。转换开始时,先将最高位置1,DAC输出满量程一半的电压与采样保持的输入电压比较;如果输入高于这个值,最高位保留为1,否则清零;然后继续试探次高位。N位SAR ADC大约需要N个“置位-比较-判决”周期才能完成一次转换,所以速度取决于内部DAC建立速度和比较器响应速度。
SAR ADC的优势是功耗低、延迟小、没有流水线延迟,适合多通道轮流采样。STM32的扫描模式就是典型应用:先配置多个规则通道,启动转换后ADC自动按顺序把每个通道都采一遍。但它也有自己的脾气:对前级驱动能力敏感。因为采样瞬间需要从外部信号源抽取一个电流尖峰给采样电容充电,如果源阻抗太高,建立时间就不够,测出来的值会偏小。
在实际项目中,处理办法是给信号源加一级运放缓冲器,或者把采样时间配置得更长。STM32的采样时间可以从1.5个时钟周期一路配到239.5个时钟周期,当信号源阻抗大的时候,宁可放慢采样速度,也要保证建立充分。
2.2 Σ-Δ:用时间换精度,低频高精度测量的主力
如果SAR是一把卷尺,Σ-Δ ADC就是一台激光测距仪。它的核心思想是过采样加噪声整形。
Σ-Δ ADC内部有一个积分器、一个比较器和一个1位DAC组成的反馈环。它用远高于奈奎斯特频率的采样率对输入信号进行粗量化(每步只输出0或1),然后用数字滤波器对大量采样点进行平均。由于反馈环路的噪声整形作用,量化噪声被推到了高频端,低频段就获得了极高的信噪比。这就是为什么24位的Σ-Δ ADC能测到微伏级别信号的原因。
Σ-Δ ADC适合称重传感器、热电偶测温、应变测量这些低频高精度场景。它的代价是转换速度慢,一个完整的转换结果需要经过数字滤波器的抽取过程,数据更新率通常只有几十到几千SPS。另外Σ-Δ ADC输入端有几个特性需要注意:它是开关电容输入,会周期性抽取电流,所以前端RC滤波的取值不能随便拍脑袋。电阻太大会导致开关电容充不上电,产生增益误差;电容太小又滤不掉调制器抽取带来的纹波。一般建议根据ADC数据手册给出的源阻抗推荐值来设计,同时让运放输出能够提供瞬态电流。
热词里提到的“Σ-Δ ADC前端RC滤波设计”就是这个道理。RC不是越大越好,而是要在建立时间和噪声抑制之间找平衡。如果后端还有数字滤波,RC的主要任务是限制运放工作带宽防振荡,而不是承担全部滤波职责。
2.3 Pipeline与Flash/折叠内插:高速数字化的另一个赛道
当采样率跑到几十MSPS以上,SAR和Σ-Δ就都不够用了。示波器、高速数字化仪、射频接收机这些场景里,主角变成了Pipeline架构。
Pipeline ADC把多级低分辨率ADC串联起来,每一级先粗量化一部分位,把残差放大后再交给下一级。就像工厂流水线,每一级只完成一部分加工,各级并行工作,所以吞吐率很高,能做到几百MSPS甚至几GSPS,同时又比全并行结构省比较器。代价是存在流水线延迟,每次转换结果要经过多个时钟周期才会输出,还有级间增益误差和比较器失调误差需要用数字校准算法修正。
Flash ADC则是把所有比较器全部并联,输入电压同时和2^N-1个参考电平比较,直接产生温度计码。它可以做到极快,但比较器数量随分辨率指数增长,12位的Flash ADC需要4095个比较器,芯片面积和功耗都难以接受。折叠内插架构是针对Flash的改进,先折叠减少比较器数量,再用内插技术从较少参考点中插出更多量化级,常见于高速示波器芯片。
对我们做嵌入式系统的人而言,Pipeline和Flash一般不会直接碰到,但了解它们有益无害。因为很多高速数字化仪产品内部就用了这类架构,当你发现常规MCU的ADC速度不够时,选择外挂高速ADC芯片,看懂架构能帮你快速理解为什么高速ADC的输入阻抗那么诡异、为什么需要片内校准、为什么结果有固定的数据延迟。
2.4 架构选型:结合分辨率、采样率与应用场景的对照
选ADC架构不是越高级越好,而是匹配需求。我把四类架构的典型参数范围整理成了表格:
| 架构 | 典型分辨率 | 典型采样率 | 延迟特性 | 适合场景 |
|---|---|---|---|---|
| SAR | 8~18 bit | 0.1~10 MSPS | 单次转换延迟小 | MCU采集、多通道扫描、电池监测 |
| Σ-Δ | 16~24 bit | 几十~几千 SPS | 数字滤波引入明显延迟 | 称重、测温、高精度仪表 |
| Pipeline | 12~16 bit | 10 MSPS~GSPS | 固定流水线延迟 | 示波器、无线通信、视频采集 |
| Flash / 折叠内插 | 6~8 bit(可达12 bit) | GSPS级别 | 极低延迟 | 高速瞬态记录、雷达脉冲 |
选型的时候还需要考虑满量程范围、输入阻抗、供电噪声灵敏度、封装和成本。一个常见错误是看到24位Σ-Δ芯片就觉得一定比12位SAR测出来更准。事实上如果信号本身就是mV级且带宽有几十kHz,Σ-Δ因为采样率跟不上,反而不如SAR;如果信号是缓慢变化的直流,用SAR去堆精度则需要多次平均,还受EMI影响。先定位信号特征,再选架构,才能避免高射炮打蚊子。
3. 读懂ADC指标,才能不被数据手册误导
3.1 采样周期、建立时间与孔径抖动:时序上的三个坑
数据手册第一页通常不会讲的三个参数,恰恰是项目里最容易翻车的:采样周期(Acquisition Time)、建立时间(Settling Time)和孔径抖动(Aperture Jitter)。
采样周期指的是采样保持开关保持导通的时间,在这段时间里采样电容要充电到足够精确的电压。STM32里配置的采样时间就是这个参数。如果外部源阻抗是10kΩ,ADC内部采样电容按理论估算大概需要5个RC常数才能稳定到12位精度以内。我见过有人用STM32的ADC直接接一个几十kΩ的电位器,采样时间还配的1.5个周期,测出来的电压明显偏低,这就是建立时间不够。
孔径抖动是指采样时钟沿的不确定性,它会导致每次采样的实际时刻有微小偏差。对于一个高速正弦波,采样时刻偏差δt对应电压误差δV = 斜率 × δt。信号频率越高,斜率越大,同样的抖动引起的误差就越大。这个关系几乎完全限制了高速数字化仪能采到的输入带宽,同时解释了为什么高速ADC的时钟输入要用低抖动晶振或专用时钟芯片。
还有一个跟时序相关的经典说法:“如果单片机ADC输入口电压为1V,采样得到的值是多少?”算法其实很简单。12位ADC、参考电压3.3V时,code = round(1 / 3.3 × 4096) ≈ 1241;16位时code = round(1 / 3.3 × 65536) ≈ 19859。这个公式有两个前提:输入的1V是稳定的、参考电压精确等于3.3V。实际工程里这两个前提都容易被打破,所以才会出现“明明是1V输入,采出来却是1.03V”的怪现象。
3.2 量化噪声、SNR与ENOB:从理想ADC到实际有效位
任何ADC都存在量化误差。一个理想的N位ADC,输入满量程FS,最小量化单位LSB=FS/2^N。量化误差在±0.5 LSB之间近似均匀分布,它的均方根噪声电压是V_LSB/sqrt(12)。由这个公式可以推导出理想ADC能达到的信噪比上限:
SNR = 6.02N + 1.76 dB
12位ADC理想SNR约74 dB,16位约98 dB。但实际ADC里还有热噪声、时钟抖动、参考电压噪声、DNL/INL非线性,这些噪声叠加后导致实际信噪比低于理论值。于是就有了ENOB(Effective Number of Bits,有效位数),它的计算方式是ENOB = (SINAD - 1.76) / 6.02。SINAD是信号对噪声加失真的比值。
热词里的“adc动态参数”指的就是SFDR、THD、SINAD、ENOB这组指标。测它们需要给ADC输入一个纯净的正弦波,采集大量数据做FFT分析。从频谱上看,基波旁边的毛刺是谐波失真,底噪是噪声地板,杂散最大的一根谱线和基波的差值就是SFDR。很多人以为买到16位ADC就等于有了16位精度,被坑过的人才明白,实际ENOB往往比标称位低1~3位,这还是在信号路径设计得不错的条件下。
对于普通MCU内部集成ADC,数据手册通常不提供完整动态参数,只能靠实测。一个实用做法是把ADC输入端短路并接地,连续采几千个点求方差,这个方差换算成码值就是系统的本底噪声;再和满量程做比,大致估算出可用的有效位数。这个指标比厂商宣传的分辨率有意义得多。
3.3 DNL、INL与校准:非线性误差和三点校准怎么做
ADC的静态指标还包括DNL(微分非线性)和INL(积分非线性)。DNL是实际量化台阶宽度和理想LSB之间的偏差,INL是整个转换曲线上实际输出码和理想直线的累积偏移。INL过大会导致大信号幅度测量不准,DNL过大会导致小信号抖动异常甚至丢码。
热词里有个很有意思的话题:“如果通过斜坡法仿真ADC的DNL”。方法是给ADC输入一个缓慢线性上升的斜坡电压,然后统计每个数字码出现的次数。理想情况下每码出现次数相等;实际由于台阶宽度不一致,某些码出现的次数会多、某些码会少,多出来的比例就是DNL的直接测量结果。这个思想同样适用于标定:用精密电压源输出一系列已知电压,把实测码值和理想码值做拟合,就能得到增益误差和失调误差。
三点校准是工程上比较完整的一套标定流程,比单纯算斜率的“两点校准”多考虑了非线性段修正。通常做法是:
- 输入0V(或最小已知电压),记录输出码,得到失调码值。
- 输入接近满量程的已知电压,记录输出码,根据两个点算出增益系数。
- 再取一个中间点电压做校验,如果实际输出和直线拟合结果偏差超过阈值,用分段线性表或多项式修正。
在C语言里实现标定很简单,关键是采集时要做多次平均去抖动。我常用校准公式:y_cal = (adc_code - offset) × gain_factor,其中offset在零点标定时得到,gain_factor在满量程点计算。如果你见到BMC固件里带“adcofftrim”和“adcgaintrim”这类名词,本质上也是在做同类型校准,区别只是它把这些参数直接烧进了OTP或者寄存器,保证每次上电后采样值都是修调过的。热词里特别提到“CLA读取ADC结果寄存器时可能读到尚未应用adcofftrim的原始值”,实际工程中要注意:在一些DSP/MCU里,如果辅助处理器(比如C2000的CLA)直接在ADC转换结束中断里读取结果寄存器,而固件还没来得及把偏移修调值叠加进去,就可能拿到未校准的原始码,所以读取时机和数据同步机制必须仔细设计。
4. 数字化仪硬件设计实战:前端调理、参考源与PCB布局
4.1 输入前端设计:分压、跟随与RC滤波的计算方法
绝大多数采集系统不会直接把待测信号怼到ADC引脚上,中间要加调理电路。以BMC读取主板电压为例,服务器主板的12V、3.3V、Vcore等电压都需要被采样,但BMC的ADC输入范围一般只有0~1V或者0~2V,所以第一件事就是分压。
分压电阻计算不复杂:选R1和R2,使Vout = Vin × R2/(R1+R2)落在ADC的满量程区间内,同时留10%~20%裕量,避免电压偏高时超出量程。需要注意两点:分压电阻的精度和温漂直接影响测量精度,建议用0.1%精度、低温度系数的电阻;分压点需要并联一个小电容构成低通滤波,抑制高频耦合噪声。
一个典型例子:测12V电压,选用R1=110kΩ、R2=10kΩ,输出约1V,再并联一个100nF电容,截止频率约为145Hz,这个带宽明显不够测快速动态变化,但对电源电压监控这类慢变信号来说非常合适,还能有效滤除开关电源的纹波。
如果信号源内阻高且要测快速变化信号,则需要在分压器和ADC之间加一级运放跟随器。运放的作用是提供低输出阻抗,让采样电容在几个微秒内就能充满,同时把前级分压电阻的噪声影响也隔离掉了。∑-Δ ADC的前端RC取值需要更谨慎,因为其开关电容输入会产生周期性电流脉冲,RC过大反而会在每个采样周期里充不满导致增益误差,所以数据手册推荐值通常只给几十欧姆串联电阻加纳法级电容。
4.2 参考电压设计:被大多数人低估的精度瓶颈
ADC测量结果能有多准,最终取决于参考电压有多准。12位ADC,满量程3.3V,一个LSB约0.8mV。如果你的参考电压有1mV的纹波或温漂,那么即使ADC本身完美无缺,测出来的码值也会跳好几个位。很多人抱怨采样值不稳定,查了一圈发现不是ADC坏了,而是参考电压引脚上的去耦电容位置不对。
参考电压的设计要点包括:
- 使用低噪声专用基准源芯片(如REF50xx、ADR4520这类)代替直接从3.3V电源取参考。
- 基准源输出端到ADC参考引脚之间串联小电阻加两枚去耦电容,通常是一个10μF钽电容加一个100nF陶瓷电容,且电容必须紧贴ADC的Vref引脚放置。
- 参考电压走线要尽量短、尽量宽,不要和数字信号线平行长距离走线。
- 如果ADC支持差分参考输入,参考源的正负都要做好去耦。
很多人问“为什么我用万用表量参考电压是3.300V很干净,但采样值还是跳?”万用表测的是低频均值,示波器才能看到瞬态噪声。ADC采样电容在转换时会在参考引脚上抽取一个电流脉冲,如果去耦电容离得远,引线电感会在这个瞬间产生电压塌陷,直接表现为输出码的随机跳动。
4.3 规避时钟抖动与电源噪声的三个PCB布局要点
“adc/dac电路设计:规避时钟抖动与电源噪声的3个PCB布局要点”这个热词切中了高速采集系统的痛点。我总结自己在多个项目里验证有效的三个要点:
第一,模拟区域与数字区域物理隔离,地平面保持完整。把ADC、模拟调理电路和参考源放在板子一侧,数字电路、处理器和连接器放在另一侧,中间用一字型或十字型分区。所有模拟地引脚通过短过孔直接下到完整地平面,不要为了“隔离”把地平面抠开,否则返回电流被迫绕路,反而产生地弹噪声。
第二,时钟电路就近放置,走线远离模拟信号。采样时钟源(晶振、时钟芯片)要尽量靠近ADC的时钟引脚,时钟走线要做阻抗控制或至少保持短而直,并加入串联电阻匹配。最忌讳的是时钟走线绕了大半个板子,还和模拟输入走线并排走几厘米,时钟沿的每一次翻转都会通过耦合电容跑到模拟输入上。
第三,模拟电源独立滤波,参考电压单独去耦。ADC的电源引脚用磁珠加电容的π型滤波结构,模拟电源尽量用独立的LDO而不是直接从数字开关电源取电。参考电压去耦电容的地端要直接且单独地接入ADC芯片下方的模拟地过孔,不要和数字地混用同一枚过孔。
这三条不是死规则,而是帮你建立优先级:先保证地回流路径完整,再管时钟串扰,最后是电源滤波。我见过只做第三条、不做前两条的板子,电源纹波已经很低了,但采样频谱上还是有一堆杂散,最后发现是时钟线和输入线在顶层平行走了3厘米。
4.4 电源噪声治理:从纹波到地弹的完整思路
电源噪声对ADC的影响有两个途径:一是直接通过电源引脚进入芯片内部模拟电路,二是通过参考电压引脚改变比较阈值。数据手册里的PSRR(电源抑制比)会告诉你在某个频率下电源噪声能衰减多少,但高频下PSRR往往很差,所以不能指望ADC自己把电源干扰扛住。
处理电源噪声的思路要分层:开关电源输出先经过LC滤波或者低噪声LDO,把纹波压到 mV 级别;ADC电源引脚前再串联磁珠,把高频噪声进一步衰减;最后在引脚旁边放去耦电容。这里的磁珠要选低频阻抗低、高频阻抗高的型号,常用600Ω/100MHz规格。需要注意磁珠不能放在LDO反馈环路里,否则相位裕量不足可能振荡。
地弹问题多发生在数字电路同时翻转大量IO时,内部电流瞬变经过地线阻抗产生电压波动。如果ADC和数字逻辑共用一个地网络且布局不当,这种波动会直接叠加到采样结果上。缓解手段包括控制IO翻转速率、数字地和模拟地在芯片处单点连接、保证每个芯片都有自己的本地去耦电容。
5. 固件与算法侧:配置、DMA、校准与滤波的工程经验
5.1 STM32内部ADC配置:扫描模式、注入通道与DMA的配合
STM32的ADC配置看着简单,实际容易踩坑。一个标准的多通道连续采集方案是:ADC配置为扫描模式,规则序列里排好多个通道,开DMA循环模式,让转换结果自动搬进内存缓冲区。
CubeMX里需要注意几个关键设置:
- Clock Prescaler:ADC时钟不能超过器件规定的上限。STM32F1的ADC最大14MHz,默认APB2是72MHz,预分频至少取6。F4系列可达36MHz,但考虑到采样精度,不建议跑满,常用4~6分频。
- Resolution:可配置12位、10位、8位或6位。采样率要求高、精度要求低时可以降低分辨率换取速度。
- Scan Conversion Mode:多通道必须打开。
- Continuous Conversion:如果用DMA循环模式,建议关闭,因为DMA请求会再触发转换;如果开了反而会造成连续不断刷新的数据覆盖。
- DMA Continuous Requests:打开,否则每次转换完一轮后DMA只搬运一次。
- DMA模式选Circular,外设地址为首地址且地址不自增,内存地址自增,数据宽度Half Word(因为ADC是12位)。
之前遇到过“四通道DMA采集数据紊乱”,现象是四路数值中通道0和通道2对调了,或者第一轮采完数据顺序变了。原因通常是DMA搬运时序和ADC序列切换没对齐,第二轮开始后DMA缓冲区里的数据相对于通道序列错位。解决方案有两种:在DMA传输完成中断里读取当前ADC外设锁定通道来校正,或者把DMA缓冲区设为两倍长度,用一个指向最新完整数据帧的头指针来读取。后期我写通用驱动时干脆在DMA缓冲区开头放一个通道ID采样值,从数据本身就能看出对齐状态,调试效率高很多。
注入通道是另一个容易理解偏差的功能。注入通道可以在规则通道转换过程中被外部触发打断,适合处理高优先级事件。很多人的疑问是“什么时机读取注入通道数据?”答案很简单:在注入转换结束标志(JEOC)置位之后读取注入数据寄存器。因为注入转换是异步插入的,如果你在主循环里随时去读,很可能读到上一轮的旧结果或者正在更新中的残值。更稳妥的办法是:阻断噪声源、先读注入通道的校准因子(数据手册里称为CALFACT)再读取结果,或者配置好DMA让注入数据也走DMA搬运,避免CPU因为读寄存器时机不对而拿到脏数据。
STM32的三重模式(Triple Mode)在F1系列上也有用到。它让ADC1、ADC2、ADC3并行工作,可以把采样率提高到单ADC的三倍,或者用无相位差的规则同步采样同时抓取三相电流。配置时要格外注意多ADC之间的触发源同步和DMA数据格式,CubeMX里选择多ADC模式后,通用规则数据寄存器ADCCDR包含三个ADC的数据,读取时按64位数据结构处理。
5.2 几类实用的C语言滤波函数与适用场景
ADC采样数据滤波是嵌入式开发里的高频需求。无论硬件滤波做得多好,数字滤波都能再挽救一把。我常用的几类滤波函数各有适用场景。
限幅滤波适合剔除明显毛刺。设定一个最大允许跳变值,若当前采样与上次值之差超过阈值,则丢弃本次值,采用上次值。
int16_t limit_filter(int16_t new_val, int16_t old_val, int16_t max_delta) { if (abs(new_val - old_val) > max_delta) { return old_val; } return new_val; }滑动平均适合平稳信号的随机噪声抑制。维护一个长度N的环形缓冲区,输出为N个点的算术平均。N越大越平滑,但响应也越慢,N通常取4到16。
#define FILTER_N 8 int32_t moving_average(int16_t sample) { static int16_t buf[FILTER_N] = {0}; static uint8_t idx = 0; static int32_t sum = 0; static uint8_t cnt = 0; sum -= buf[idx]; buf[idx] = sample; sum += sample; idx = (idx + 1) % FILTER_N; if (cnt < FILTER_N) cnt++; return sum / cnt; }一阶低通滤波(IIR)是最轻量的选择,适合实时性要求高的场合,只保存一个上次值,计算量极小。系数α越接近1,滤波越弱、响应越快;α越接近0,滤波越强、相位延迟越大。
int16_t iir_lowpass(int16_t new_val, int16_t last_val, uint16_t alpha) { // alpha:0~1024,代表滤波强度 return (int16_t)(((int32_t)new_val * alpha + (int32_t)last_val * (1024 - alpha)) >> 10); }防脉冲滑动平均是中值滤波和滑动平均的组合,先取几个采样点排中值剔除离群点,再做滑动平均,适合既有尖峰干扰又有随机噪声的工业环境。
工程里不能盲目套滤波算法。如果系统的真实信号变化很快(比如电流环反馈),滑动平均会把相位延迟引入环路,可能引发振荡;如果只是做告警监测,滤波效果优先,响应慢一点无所谓。滤波前后一定要留好原始值和滤波值的打印,便于现场比对判断是否过度滤波。
5.3 数据漂移问题排查:是基准漂了,还是PCB漏电
“ADC数据漂移”是热词,也是现场问题里最磨人的一类。同一采集点,冷启动和运行一小时之后数值差几十个码,或者冬天测一个值、夏天测另一个值,这样的现象背后通常有四个嫌疑:
- 参考电压基准源温漂。普通三端稳压器当基准用,温度变化会让参考电压跟着变,这才是最常见的“ADC不准”根源。
- 分压电阻温漂。电阻温度系数不为零,环境温度变化时分压比就变了。
- PCB漏电。电源层和信号层之间若残留助焊剂或湿气,会形成微安级漏电流。在高阻分压电路里,微弱漏电足以造成明显误差。
- ADC自身失调漂移。温度变化引起内部比较器和采样开关的偏移变化,有些数据手册会给出“offset drift”参数。
排查手段很简单,但要有顺序:先把ADC输入引脚直接接到板上的模拟地,连续采样,如果读数仍然漂,问题在ADC内部或参考源;再把输入端接到一个高精度外部电压源(比如万用表的校准输出),如果读数稳定,问题在板上被测电路;用加热枪或制冷剂对参考源和分压电阻局部加热/降温,观察读数的变化趋势,就能锁定漂移最大的器件。
高阻分压电路(比如100kΩ以上)里,PCB走线间隙的漏电流是隐藏炸弹。解决办法是走线之间加地隔离,做覆铜保护,板子清洗要彻底,必要时在采样点并联一个100nF~1μF电容吸收漏电流。有些高精度设计会采用“先分压成小电压,再经运放缓冲,最后进ADC”的结构,这种结构的输入阻抗高、PCB漏电影响小,也是工业仪表常采用的做法。
6. 核心应用场景复盘:从ADC按键到板级电源监控的实战经验
6.1 单ADC引脚识别多个按键:分压网络与区间判定
“ADC按键”是MCU开发里非常经典的应用,一颗ADC引脚配合分压电阻网络,就能识别一排按键。原理就是每个按键按下时,把不同的分压组合接入ADC引脚,产生不同的电压码值,固件根据码值落在哪个区间判断是哪个键。
一个4键电路通常用一棵串联分压树:R1接Vref到ADC引脚到R2到GND,中间依次引出几个节点接按键到ADC引脚。为了避免相邻按键电压差太小导致误判,每个键对应的ADC码值要拉开足够裕量。建议按满量程的10%为一个档位,比如12位ADC下相邻键的码值差至少给400个码。这里有个经验值:按键点最接近0V和接近Vref的两个键最容易误触,因为输入接近边界时,电源噪声或地弹容易把码值推出判定区间,所以镜像把分压树设计成对称结构,两端留出至少5%的电压盲区。
按键去抖不能用简单延时了事。ADC采到的按键值本身也会有噪声,工程做法是连续采样多次,取中值或均值,再判断稳定后的区间。还可以加滞回逻辑:进入某个键的区间后,只有码值偏离该区间超过一个窗口才认为松开,防止临界抖动导致重复触发。这个思路和热词里的“外部ADC三点校准”有点类似,本质上都是定义一个可靠的“判断区间”而不是单纯依赖一个点。
6.2 BMC读取主板电压:从分压到阈值的完整链路
BMC(带外管理控制器)读取主板电压是整个服务器硬件设计里最常见的ADC应用之一。热词“bmc通过adc读取电压是怎么做的”问的就是这个完整链路。
典型实现里,BMC芯片(比如ASPEED的AST2500/AST2600)内部集成多路ADC,每路输入范围0~1V左右。主板上的12V、5V、3.3V、Vcore等电源轨先经过不同的分压网络降到0~1V范围内,再送入BMC的ADC引脚。BMC固件读取ADC码值,按照分压比反算出实际电压,然后跟阈值做比较,决定是否触发告警或报告给上层管理软件。
这里有几个工程上很重要但文档里不大会写的细节:
- 分压电阻的阻值不宜太小,否则待机功耗太大;也不宜太大,否则BMC内部ADC采样时的输入抽载会造成误差。一般取几十kΩ量级,并在分压点并联一个1nF~100nF滤波电容。
- 每个电源轨都要做钳位保护,避免电源瞬态尖峰打坏BMC的ADC引脚。常用做法是在采样点对地并联一颗齐纳二极管或TVS,限制电压不超过ADC量程。
- 分压比必须逐板标定。电阻有±1%、±0.1%精度之分,如果不校准,实际测得的电压值和万用表读数难免对不上。工厂校准通常就是用精密电压源送入几个已知电压,记录码值,生成每板的校准系数存进EEPROM。
用ADC监控电压还有一个容易被忽略的点:电压瞬态响应。比如CPU瞬间拉高功耗,电源轨的电压会快速跌落再恢复。如果ADC采样时机每次都正好错过跌落谷值,监控系统就会漏报。工程上针对这种动态电源监控,要么提高采样率、多采几次,要么配合比较器做快速事件捕获,BMC只管最终状态汇总。
6.3 容易被忽略的工程细节:漏电流、串联电阻与通道串扰
几个低配但高频出现的ADC问题值得单独提醒。
一个是输入引脚漏电流。MCU的ADC输入引脚在高温下可能有几十nA到上百nA的漏电流,给高阻源带来明显误差。比如信号源等效阻抗100kΩ,100nA漏电流最坏情况会带来10mV的误差,这在12位ADC下相当于13个LSB。对策是控制信号源阻抗、做好PCB清洁、选择高输入阻抗的器件。
另一个是串联电阻对建立时间的影响。很多人为了滤波在输入通路里串了1kΩ电阻,结果忘了评估建立时间。MCU内部采样电容典型值在4~10pF量级,外部串联1kΩ加上内部开关电阻,总源阻抗可能到2~3kΩ。如果这时候采样时间还配置得很短,充电在采样窗口内完不成,读数就会偏低。判断方法很简单:把采样时间增加一倍,采样值如果明显变大,那就是源阻抗太大导致建立不充分。
还有通道间串扰。多通道扫描模式下,当前通道转换完成后,采样电容会被重新接到下一通道,但由于模拟多路开关的隔离度有限,前一个通道的电压会残留一部分在采样电容上,特别是前一个通道电压很高、后一个通道电压很低时,后者的读数可能偏高。对策包括:在序列之间插入一个接地通道做清洗、降低通道间电压摆幅、给每个通道输出都加低阻驱动。
结尾
最后再分享一点我个人的体会。ADC的坑从来不在数据手册第一页,而在后面的时序图、应用笔记和某条不起眼的“注意”里。做采集项目这几年,我最大的长进不是学会用某个外设,而是养成了一套习惯:先确认采样时序是否足够、再检查参考电压是否干净、最后才怀疑固件代码。很多“采样值跳动”的问题,最后都查到了PCB布局和前端驱动上。
如果你现在正被“ADC采样值跳”“多通道采集错位”“数据随温度漂移”折磨,我建议你先按这个顺序排查:第一步,把输入短路到GND并采1000次,看本底噪声;第二步,用稳定电压源输入,检查增益和失调;第三步,检查时钟和电源脚附近的去耦电容。百分之八十的问题在这三步里就能现出原形。以后有空我再单独写一篇关于高速ADC时钟配置和Σ-Δ前端稳压电路的文章,这两块内容一旦吃透,你再回头看数字化仪和数据采集系统,会突然发现一切都通了。