
一个12位ADC满量程3.3V理论上一个LSB不到0.8mV。可很多人调出来之后发现读数比实际电压低了不少还以为是信号源有问题换表一量却明明是对的。问题往往不在前端而在ADC采样电容压根没充到位。ADC、采样电容、充电误差这几个词凑在一起基本就概括了采样保持电路最核心的精度矛盾。更麻烦的是这种误差表现为“稳定偏低”软件滤波完全救不回来。这篇文章我把整条链路从头拆到尾内部采样保持是怎么工作的充电误差为什么会影响LSB精度采样时间到底该设多少外围滤波和源阻抗怎么搭最后再放几个我实际排障的案例。适合正在调ADC采集电路的硬件工程师、嵌入式工程师尤其适合被数据手册里RADC、CADC、采样周期数搞得一头雾水的人。1. 采样保持电路的“家底”一个开关加一个电容凭什么能采准1.1 采样、保持、转换三个阶段到底在干什么先理清内部结构。绝大多数MCU内置ADC是逐次逼近型SAR核心就是模拟开关、采样电容数据手册里常写CADC或CSH、比较器和逐次逼近逻辑寄存器。整个工作过程可以分成三步采样阶段采样开关闭合输入电压通过外部源阻抗、内部开关导通电阻、内部串联电阻一起给采样电容充电。电容上的电压从0或上一次状态的残余电压爬升到接近输入电压。保持阶段采样开关断开电容上保存的电压不再跟随输入变化。这时候SAR逻辑开始准备做逐次逼近比较。转换阶段内部电容阵列以参考电压Vref为基准通过“电荷再分配”和比较器逐位比较把模拟电压转成数字码。这个阶段输入信号被彻底隔离比较器面前只有一个稳定的保持电压。很多人容易忽略一个关键点SAR转换不是一个“读瞬态电压”的过程它比的是采样电容上那个电压。如果采样阶段电容没充满保持电压和真实输入之间存在偏差后面比较器再准也是白搭——因为它的输入从一开始就是错的。用生活类比解释采样电容就像一个水罐采样开关是水龙头输入信号是水管里的水。水龙头打开的时间不够长、水管本身太细、水源压力又不稳罐子里的水位就达不到真实水位。ADC只能测量罐子里的水位测不到水管里的实际水位。区别只是这个水罐特别小往往只有几个皮法而水龙头打开的时间是以纳秒计的。1.2 充不满的电容RC充电模型给精度定下的“硬约束”采样电容的充电过程是一个典型的一阶RC阶跃响应。设总电阻R包括信号源输出阻抗Rsource、内部开关导通电阻Rsw、内部串联电阻RADC总电容C以采样电容CADC为主外部滤波电容的参与方式我们后面单独讲。则电容电压随时间变化的关系是Vc(t) Vin × (1 - e^(-t / (R × C)))那么充电误差就是Verr(t) Vin - Vc(t) Vin × e^(-t / (R × C))这个公式一旦写出来一切就清楚了只要充电时间t不是无穷大误差就永远存在。关键问题变成——误差允许有多大ADC是量化设备它天生存在量化误差最小分辨率就是LSB。一个N位ADC在参考电压Vref下LSB Vref / 2^N。对于采样保持电路行业里的通用要求是采样电容上的电压误差必须小于0.5 LSB。这不是某个厂商的拍脑袋规定而是为了保证总误差不会被充电环节“吃掉”太多留出余量给比较器失调、参考源纹波、噪声等其它误差源。把0.5 LSB代入误差公式Vin × e^(-t / (R × C)) ≤ Vin / 2^(N1)两边消掉Vin取自然对数t / (R × C) ≥ (N1) × ln2这个公式特别有用。它告诉你N位ADC至少需要(N1)×ln2个时间常数。12位ADC大约需要9个τ16位大约需要11.8个τ。位数越高对充电时间的要求越苛刻。这也是为什么高分辨率ADC对采样时间、源阻抗的要求近乎苛刻的原因。2. 从充电误差到LSB精度这笔账必须算得明明白白2.1 LSB到底有多小0.5 LSB为什么是“红线”LSB不是抽象概念它是实实在在的电压。以3.3V满量程为例不同位数下的LSB如下ADC位数LSB大小3.3V满量程0.5 LSB误差允许8位12.9 mV6.45 mV10位3.22 mV1.61 mV12位805.7 μV402.8 μV14位201.4 μV100.7 μV16位50.4 μV25.2 μV看到数字就明白了12位以下的采集0.5 LSB还是有几百微伏的宽容度但一到16位允许误差只有25微伏。这时候不要说采样时间不够了PCB板上的一段走线电阻、一个TVS管的漏电流、甚至参考电压引脚上的一点点纹波都可能轻松超过这个数。所以0.5 LSB不是谁规定的“法律”而是工程设计的一条经验红线。它保证了ADC的微分非线性DNL和积分非线性INL主要受内部比较器和电容阵列限制而不会被前端充电误差掩盖。真要用在精密测量场合我通常会把充电误差余量留到0.25 LSB以下给温度漂移和老化留空间。2.2 快速估算所需“时间常数个数”和采样周期数刚才推导的核心结果我直接整理成速查表ADC位数所需RC时间常数个数(N1)ln286.24107.62129.011410.401611.78实际计算时总电阻R不只是信号源输出阻抗。数据手册中所说的“最大源阻抗”前提是内部已经串了一个阻抗。完整的公式是τ (Rsource Rsw RADC) × CADC举例说明。假设信号源输出阻抗10kΩ内部开关导通电阻约1kΩRADC典型值给6kΩCADC为8pF。那么总R就是17kΩτ 17kΩ × 8pF 136ns。12位ADC需要9.01个τ也就是1.226μs。如果ADC时钟是36MHz周期约27.8ns需要的采样周期数为1.226μs ÷ 27.8ns ≈ 44.1个周期而常见MCU的采样时间档位未必有44.1这个值。如果可选项是41.5和71.5周期那就必须选71.5周期。选41.5严格来说已经不够会引入超过0.5 LSB的误差。很多人喜欢“差不多”选一个结果就是整机精度差一口气查半天查不出来。这个估算方法可以直接封装成一个Excel小工具输入源阻抗、ADC内部参数、时钟频率自动算出最小采样周期数。我每次换型号、换平台都会第一时间做这个表能省无数调试时间。2.3 从数据手册里把关键参数挖出来不同厂家的数据手册写法不太一样但核心参数是固定的。拿到芯片后重点看四样东西RADC内部串联电阻。这个值直接加在源阻抗上通常几百欧到几千欧不可忽略。CADC采样电容。常见范围2pF到20pF有些低功耗芯片能做到更低。最小采样时间数据手册常常给出“在给定源阻抗下对应的最大ADC时钟频率”表格或曲线。采样保持电路图很多手册会在ADC章节放内部等效电路图图上写了RADC、CADC的具体值。我踩过的坑是有些芯片的CADC参数不明显藏在应用笔记里只有等效电路图才有标注。如果找不到就用典型值来估算并在设计时保留至少30%的采样时间余量。3. 外围设计实战采样电容、源阻抗和滤波网络怎么搭配3.1 一个10k源阻抗的完整估算例子实际项目中信号源很少是理想的零内阻电压源。传感器的输出电阻、电阻分压网络的等效电阻、走线和连接器的接触电阻统统要算进Rsource里。举个最常见的例子用两个10kΩ电阻分压采集电池电压。分压器对地的等效源阻抗是两个电阻的并联值也就是5kΩ。如果信号源本身还有几百欧的内阻加在一起可能就5.5kΩ。套用STM32F4典型的参数RADC约6kΩ、开关导通电阻约1kΩ、CADC约8pF。总R 5.5k 1k 6k 12.5kΩτ 12.5k × 8p 100ns。12位精度需要9τ也就是900ns。ADC时钟36MHz下需要900 ÷ 27.8 ≈ 32.4个周期。选采样时间时至少41.5周期稳妥的话选71.5周期。很多分压电阻设计得很大比如1MΩ目的是降低功耗。这时等效源阻抗是500kΩτ 507k × 8p ≈ 4μs9τ就是36μs在ADC时钟36MHz下需要约1300个周期。绝大多数MCU的采样时间最长也就几百个周期——这已经不是“配置一下采样时间”能解决的事了老老实实加运放缓冲或者用较小的分压电阻加软件修正。3.2 外部滤波电容是不是越大越好电荷共享与恢复时间的权衡抗混叠滤波是ADC外围的标准配置常见做法是在ADC输入引脚前加一阶RC低通。但这里有个反复出现的坑工程师为了“滤波更干净”把电容加到1μF甚至10μF结果读到的是严重滞后的电压。外部大电容Cext确实能降低采样瞬间的电压跌落。开关闭合瞬间外部电容和内部采样电容要电荷共享电压变化大约是ΔV ≈ (CHold / (Cext CHold)) × ΔV_in如果外部电容是100nF、内部采样电容8pF这个比值是0.008%根本不用担心。但如果外部电容太小比如只有1nF以下采样瞬间的外部电压跌落就会比较明显。可问题来了采样结束后外部电容需要通过信号源阻抗Rsource重新充电。外部电容越大恢复时间越长恢复时间常数是Rsource × Cext。假设Rsource是10kΩCext是1μF恢复时间常数是10ms。假如ADC采样速率是100kSPS两次采样间隔只有10μs外部电容上的电压根本来不及恢复到真实输入值ADC读到的就一直是“被拉偏的电压”。我的实践经验信号源是高阻时外部Cext选1nF到10nF足够了R能小则小如果希望把Cext加到大容量就必须先用低输出阻抗的运放缓冲让运放去驱动大电容采样期间的瞬态电流由运放的输出来扛。没有低阻抗驱动源就别迷信“大电容滤波”这一招。3.3 什么时候必须上运放缓冲器判断标准其实很简单算出来的充电误差超过0.5 LSB且采样时间无法再调整时就该上缓冲器。还有一个场景是传感器本身是电流型输出比如光电二极管这时候更需要一个跨阻放大器来转换成电压。运放缓冲器选型时注意三个点单位增益稳定否则接成电压跟随器会自激。输出阻抗要低常温下几十毫欧到几欧姆即可。能驱动容性负载很多普通运放接大电容后相位裕度不足会振铃。运放缓冲器放到ADC前面之后Rsource变成了运放的开环输出阻抗一般在1Ω以下。这时候外部RC的R可以选几十欧C选100nF以上都不会有太大问题。采样电容充电时瞬间需要的电流会由运放输出来补充只要运放的带宽和压摆率跟得上精度就稳了。3.4 别忘了Vref引脚和参考源设计很多人把采样电容充电误差算得清清楚楚结果LSB精度还是不行绕了半天发现参考电压在掉链子。SAR ADC内部比较是拿输入电压和电容阵列上的参考电荷比而电容阵列在每次比较时都会从Vref引脚抽取瞬态电流脉冲。如果Vref引脚上电压不稳内部比较基准就跟着抖最终码值当然不准。Vref引脚的处理有几个原则参考电压引脚必须就近放低ESR电容常见组合是1μF陶瓷电容并联100nF高频去耦电容位置要压在引脚旁边。参考电压源要有足够的电流输出能力不能用一个高阻分压直接去驱动Vref。如果板上模拟供电纹波较大最好使用独立的外部基准芯片比如低噪声精密基准把基准电压和数字电源隔离开。参考电压这一项数据手册一般都会给“Vref引脚允许的最大纹波”或者推荐去耦电容配置照着手册做别自作聪明。4. STM32/GD32/DSP场景下的采样时间配置与软件排查4.1 从CubeIDE到系统库采样周期到底设多少以STM32的HAL库为例ADC采样时间是通过SamplingTime配置的。代码里看起来只是枚举值的一个选择背后对应的却是实实在在的充电时间。ADC_HandleTypeDef hadc1; hadc1.Instance ADC1; hadc1.Init.ClockPrescaler ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode DISABLE; hadc1.Init.ContinuousConvMode DISABLE; hadc1.Init.NbrOfConversion 1; HAL_ADC_Init(hadc1); ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_0; sConfig.Rank ADC_REGULAR_RANK_1; // 关键参数采样时间根据源阻抗和内部RC评估来选 sConfig.SamplingTime ADC_SAMPLETIME_71CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 100); uint32_t adc_value HAL_ADC_GetValue(hadc1);ADC_CLOCK_SYNC_PCLK_DIV4这个分频系数也要留意。PCLK越高ADC时钟越高单个周期越短。同样的采样周期数在高ADC时钟下对应的绝对时间更短。比如ADC时钟36MHz时71.5个周期约1.99μs但ADC时钟60MHz时同样71.5个周期只有1.19μs。所以调代码时别只看“周期数多少”还要算上ADC时钟评估绝对充电时间。GD32的配置思路类似寄存器层面就是设置采样时间位段很多库函数里给了SAMPLE_TIME_7_5、SAMPLE_TIME_41_5这类选项。关键还是那句周期数乘以ADC时钟周期得到实际充电时间再和计算出的最小τ数去比对。DSP平台比如通过EPWM触发ADC采集就更直接了你不仅要设置采样时间还要保证触发频率给采样留下的窗口足够宽。假如EPWM触发周期是5μs而ADC采样转换要6μs那就必然出问题。这种场景下要么降低触发频率要么牺牲过采样率工程上永远在做取舍。4.2 软件滤波救不了硬件误差代码里该做什么、不该做什么网上很多文章教软件滤波中值滤波、滑动平均、卡尔曼滤波五花八门。这些方法对“随机噪声”确实有效但对付“充电不足导致的系统性偏移”完全无效——因为每次采样都偏低滤波只是把偏移平均成另一个稳定的偏移。我见过一个经典案例产品测试发现ADC读数比实际电压低了约15个码软件工程师加了一层的滑动平均码值曲线平滑了但平均之后还是低15个码。他把滤波窗口拉长到256点依旧低15个码。最后硬件工程师把采样时间从7.5周期改成41.5周期问题直接消失。这就是系统性误差的典型特征平均值不会消失只会稳定地错下去。所以在代码层面我的建议是先确认采样时间是够的再谈软件滤波。对于随机噪声滑动平均或中值滤波就够了不需要上复杂算法。如果追求等效分辨率可以用过采样加求平均但这只能降低噪声依然不能修正充电误差。DMA多通道采集时切换通道后第一笔数据经常不稳直接丢弃第一笔结果。4.3 DMA多通道采集的时序隐患多通道轮流采集很常见但通道串扰问题容易被忽略。采样开关从一个通道切换到另一个通道后如果前一个通道的电压和当前通道电压差很大而采样时间不足内部电容可能还处在“前一个电压”的记忆里就会测出介于两个电压之间的错误值。解决手段有三个一是采样时间尽量留足二是通道切换顺序让相邻通道的电压差尽量小三是在DMA循环里主动丢掉每个通道的第一笔转换结果。很多时候第三招最省事能直接消除绝大多数串扰问题。5. 实测案例与常见问题速查表5.1 案例一12位ADC读数整体偏低换采样时间就好了某次电机电流采集项目信号链是电流采样电阻运算放大器控制板用一颗通用MCU。运放输出实测1.65V但ADC读回来只有1.62V12位大概差了37个LSB。一开始怀疑运放偏置电阻问题用万用表量又没问题。最后回看配置发现ADC采样时间被设成了最快档。用示波器探头夹在ADC引脚根本看不到异常因为万用表和示波器探头带宽有限测不到纳秒级的建立过程。重新按公式算运放输出阻抗很低但PCB走线长、中间加了个1kΩ的串联电阻R_total ≈ 4kΩτ ≈ 32ns9τ ≈ 288ns。在ADC时钟24MHz下最快档的绝对采样时间只有62.5ns不够。把采样时间提到41.5周期约1.73μs后读数立刻恢复正常误差小于2个LSB。这个案例提醒我那个串联电阻看似规范实际上吃掉了大量充电预算。在ADC输入端串联电阻务必同步算好采样时间的增量。5.2 案例二高阻分压读电池电压总是跳采集电池电压为了省功耗用了两个1MΩ分压电阻等效源阻抗500kΩ。表现在软件层面上是码值跳得厉害从1210到1250乱蹦。加了平均滤波也一样跳才意识到这不是噪声问题——是充电根本没完成。计算发现12位下需要约38μs的充电时间远超MCU能提供的最大采样窗口。最终方案保留了分压电阻但中间加了一颗低功耗运放跟随器运放输出阻抗低于1Ω问题直接消失。5.3 案例三加了大滤波电容后波形反而跟不上另一个项目里为了滤除纹波在ADC输入引脚放了10μF陶瓷电容结果采集快速变化的信号时波形严重滞后简直像个积分器。持续时间常数是1kΩ×10μF10ms而信号的频率是1kHz每周期才1ms滤波器直接把信号砍掉了大半。后来把R降到100Ω、C改成100nF截止频率约16kHz对1kHz信号衰减很小同时因为后面有运放缓冲充电电流也不成问题。这个案例说明滤波不是越大越好一定要结合信号带宽和源阻抗来定。5.4 常见问题速查表现象可能原因排查点解决办法读数整体偏低且误差稳定采样时间不足充电误差超标采样时间配置、源阻抗增加采样周期加运放缓冲读数跳动、滤波后依旧不稳参考电压不稳、噪声偏大Vref去耦电容、PCB布局增加基准去耦改善模拟地布局多通道DMA采集相互串扰采样电容残留前通道电压通道切换顺序、采样时间丢弃每通道第一笔数据加大采样时间加了大电容后波形滞后RC恢复时间过长或低通截止频率太低Cext、Rsource、信号带宽降低Cext或R用运放驱动大电容高阻信号源读数偏低源阻抗过大充电时间常数超标Rsource、等效电路加运放缓冲器降低分压电阻阻值接近满量程时误差变大采样开关导通电阻受输入电压影响输入电压范围保证输入在合理范围用运放做电平搬移6. 最后补充两个调试时很有用的“土办法”如果手头没有可调采样时间的现成工程有个简单办法能验证充电误差把ADC输入接到一个低阻抗源比如运放输出再对比直接接一个经过高阻电阻的输入看码值差。如果两者差很多充电问题基本实锤。还有一个办法是把输入接到Vref上直接测满码理想情况应该读到接近409512位。如果接Vref都读不到满码而下降一点电压就能读准说明采样保持电路在最高电压时建立能力最差这也是源阻抗和开关电阻非线性的典型表现。我个人的习惯是拿到一个新平台先按最保守的采样时间配置跑一版基线记录所有通道在已知电压下的码值然后用项目允许的极限采样时间再跑一遍对比偏差。这组数据能帮你快速定位后续所有精度问题也省得每次都要从零开始排查。采样电容充电误差这件事听起来是模拟电路里的一个小细节但它卡住了太多“明明电路没问题、代码没问题”的项目。搞懂那根指数曲线算清那几次时间常数比盲目堆滤波算法实在得多。