先说结论:做高精度采集,硬件是决定上限的,软件是拉高下限的。参考源选不好、PCB布错了,再牛的软件算法也救不了。一般工业采集,12位ADC(STM32自带的)够用了;要求高一点上16位(ADS1115、AD7606之类);再高精度就上24位Σ-Δ型(ADS1256、ADS1299),但主要用在称重、温度等低速高精度场景。别盲目追求高分辨率,先搞清楚你需要多少精度。
ADC采集是嵌入式开发里最常见的功能之一,也是坑最多的功能之一。很多人觉得"不就是读个ADC值嘛,有什么难的",结果做出来的数据跳得一塌糊涂,最后不知道问题出在哪。
我们团队做工业数据采集做了6年,从单通道到32通道,从12位到24位,从毫伏级信号到毫安级信号,各种采集场景都做过。踩过的坑能列一长串——参考源温漂大、PCB地线没布好、电源纹波大、前端运放自激、软件滤波过度……
今天这篇文章,从ADC选型到硬件设计,从PCB布局到软件滤波,从误差校准到常见坑,给你讲透。
一、先搞清楚:分辨率、精度、采样率不是一回事
很多人一上来就说"我要16位精度的ADC",这是典型的概念混淆。三个概念先分清楚:
- 分辨率(Resolution):ADC能输出多少位的数字量,比如12位就是4096个刻度,16位就是65536个刻度。这是个理论值,跟精度不是一回事。
- 精度(Accuracy):测量值和真实值之间的误差,用%FS(满量程百分比)或者多少个LSB表示。分辨率高不代表精度一定高,参考源不准、噪声大的话,16位可能还没12位准。
- 采样率(Sampling Rate):每秒能采多少次样。采样率越高,对高速变化的信号还原越好,但数据量也越大,处理越费资源。
那怎么选ADC?看你的需求:
| ADC类型 | 典型分辨率 | 典型精度 | 采样率 | 适用场景 |
|---|---|---|---|---|
| MCU内置ADC | 10~12位 | ±1%~±3%FS | 几十kHz~几MHz | 电池电压、按键检测、一般测量 |
| SAR型外置ADC | 12~16位 | ±0.1%~±0.5%FS | 几百kHz~几MHz | 工业采集、多通道、中速高精度 |
| Σ-Δ型ADC | 18~24位 | ±0.01%~±0.1%FS | 几Hz~几十kHz | 称重、温度、压力等低速高精度 |
二、硬件设计:精度的上限是硬件决定的
做高精度采集,硬件设计是基础。硬件没设计好,软件再怎么滤波、再怎么校准,也达不到理想的精度。
1. 参考电压源——最关键的元件
ADC的测量基准是参考电压(Vref)。参考源不准,ADC再准也白搭。举个例子:你的参考源标称2.5V,实际只有2.45V,差了2%,那所有测量结果都会差2%。
常见的参考源方案:
| 参考源方案 | 初始精度 | 温漂 | 价格(约) | 适用场景 |
|---|---|---|---|---|
| MCU内部参考 | ±3%~±5% | 几十~上百 ppm/℃ | 0元 | 粗略测量、对精度要求不高 |
| TL431并联基准 | ±0.5%~±1% | ~50 ppm/℃ | 0.2元 | 一般应用,性价比高 |
| REF3030/REF3325 | ±0.1%~±0.2% | 10~20 ppm/℃ | 3~5元 | 高精度工业级,常用 |
| REF5025/ADR431 | ±0.05%以内 | 3~5 ppm/℃ | 15~30元 | 高精度仪器、仪表 |
| LTZ1000 | ±0.005%以内 | 0.05 ppm/℃ | 上百元 | 基准源、计量级 |
2. 电源设计——纹波和噪声是杀手
ADC对电源噪声很敏感。电源上的纹波会直接耦合到ADC的采样结果里,表现为数据跳变。
电源设计要点:
- 模拟部分和数字部分分开供电:AVCC和DVCC分开走线,最好用磁珠或者LDO隔离。
- 充足的去耦电容:ADC电源引脚旁边放0.1μF的陶瓷电容+10μF的钽电容,越近越好。
- 参考源输出加滤波:参考电压输出端加RC滤波(比如100Ω+1μF),减小噪声。
- 别用开关电源直接给模拟部分供电:开关电源纹波大,一定要加LDO稳压或者用LC滤波。
3. 前端信号调理——让信号匹配ADC
很多传感器输出的信号很弱(比如毫伏级),直接给ADC的话,分辨率不够用。这时候就需要前端放大——用运放把信号放大到ADC满量程附近。
前端调理的常见需求:
- 放大:小信号放大,比如称重传感器的mV级信号放大到V级
- 电平偏移:双极性信号(正负)转成单极性(ADC只能采正电压)
- 阻抗匹配:高阻抗输出的传感器,需要高输入阻抗的运放跟随
- 抗混叠滤波:低通滤波,滤掉采样率一半以上的频率成分
运放选型要点:
- 失调电压(Vos):越小越好,特别是放大小信号的时候。失调电压会被放大倍数一起放大。
- 温漂(Vos drift):工业场合温度变化大,温漂很重要。
- 输入偏置电流:高阻抗传感器要选偏置电流小的运放(比如JFET或CMOS输入的)。
- 带宽:比你的信号最高频率高10倍以上比较保险。
4. PCB布局——很多问题都是布出来的
ADC电路的PCB布局特别讲究,布不好,硬件再完美也白搭。几个关键原则:
- 模拟地和数字地分开,单点连接:模拟部分一个地,数字部分一个地,两个地只在一个点连在一起(一般在ADC下面)。别到处混接。
- 模拟信号线尽量短:走线越长,越容易受干扰。输入信号线要短、要直,别绕来绕去。
- 高速数字信号远离模拟部分:SPI、时钟这些高速信号,别从ADC旁边走,会串扰。
- 参考源和去耦电容紧贴ADC引脚:越近越好,引线越短越好。
- 模拟信号线用地包起来:重要的模拟信号线,周围用地线包一圈,减少干扰。
三、软件滤波:拉高下限的利器
硬件做好了,数据还是会有噪声——这很正常,世界上没有绝对干净的信号。这时候就要靠软件滤波了。
常用的几种滤波算法,各有优缺点:
| 滤波算法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 限幅滤波 | 两次采样差值超过阈值就丢弃 | 简单、高效、防脉冲干扰 | 不能滤除平滑噪声 | 变化缓慢的信号,去掉跳点 |
| 算术平均滤波 | N次采样取平均值 | 简单、对随机噪声效果好 | 有延迟、浪费资源 | 对实时性要求不高的信号 |
| 滑动平均滤波 | 用队列存N个数据,每次更新一个 | 实时性好、平滑 | RAM占用多、有延迟 | 大多数采集场景,最常用 |
| 中值滤波 | N次采样取中间值 | 对脉冲干扰效果极好 | 计算量大、对平滑噪声效果一般 | 有尖峰干扰的信号 |
| 一阶低通滤波 | 本次结果 = α×本次采样 + (1-α)×上次结果 | 超简单、省RAM、平滑 | 有相位滞后 | 慢速变化的信号,简单有效 |
| 卡尔曼滤波 | 基于状态方程的最优估计 | 效果好、动态响应快 | 复杂、需要调参数 | 对动态响应和精度都有要求 |
我们团队最常用的组合是:限幅 + 滑动平均。先去掉明显的跳点,再做平均,效果不错,计算量也不大,一般的MCU都能跑。
// 限幅 + 滑动平均 组合滤波 —— 我们项目里用得最多的组合 // 原理:先限幅去掉尖脉冲,再滑动平均平滑噪声 #define FILTER_N 10 // 滑动平均窗口大小,越大越平滑但延迟越大 #define MAX_DIFF 50 // 限幅阈值,单位是ADC的LSB,根据实际情况调 static int filter_buf[FILTER_N]; // 滤波缓冲区 static int buf_index = 0; static int last_val = 0; static int sum = 0; int adc_filter(int new_val) { // 第一步:限幅滤波 —— 变化太大的数据直接扔掉 if (last_val != 0) { int diff = new_val - last_val; if (diff > MAX_DIFF || diff < -MAX_DIFF) { return last_val; // 跳变太大,不用这个值,返回上次的 } } // 第二步:滑动平均 —— 用新数据替换最老的数据 sum -= filter_buf[buf_index]; // 减去最老的数据 filter_buf[buf_index] = new_val; // 存入新数据 sum += new_val; // 加上新数据 buf_index++; if (buf_index >= FILTER_N) { buf_index = 0; } last_val = sum / FILTER_N; // 取平均值 return last_val; } // 团队经验:窗口大小别太大,10~20次足够了 // 太大了延迟严重,传感器数据都变了,滤波结果还没跟上 // 实测发现:10次滑动平均,随机噪声能降70%左右,延迟也不大
四、校准:消除系统误差的必杀技
硬件做得再好,也会有系统误差——参考源有误差、运放有失调、电阻有公差。这些误差靠滤波是滤不掉的,必须校准。
常用的校准方法:
1. 两点校准(增益和失调校准)
最常用的校准方法。输入两个已知的标准值(比如0V和满量程),测出ADC的读数,算出增益系数和偏移量,以后每次采样都用这个系数修正。
公式:实际值 = (ADC读数 - 偏移量) × 增益系数
// 两点校准 —— 工业采集最常用的校准方法 typedef struct { float gain; // 增益系数 float offset; // 偏移量 } adc_calib_t; adc_calib_t calib; // 校准函数:输入两个标准值和对应的ADC读数 void adc_calibrate(float std1, int adc1, float std2, int adc2) { // 算增益:(标准值差) / (ADC读数差) calib.gain = (std2 - std1) / (float)(adc2 - adc1); // 算偏移:标准值1 - ADC读数1 × 增益 calib.offset = std1 - calib.gain * (float)adc1; } // 校准后的实际值计算 float adc_get_real_value(int adc_val) { return calib.gain * (float)adc_val + calib.offset; } // 这里容易踩坑:校准参数要存在Flash里,不能每次上电重新算 // 而且要加校验,防止Flash数据坏了导致结果完全不对 // 我们一般存两份参数,各加CRC校验,互为备份
2. 多点校准
如果系统的非线性比较严重,两点校准不够用,就用多点校准——测N个点,存一张校准表,实际使用的时候插值计算。精度更高,但也更麻烦。
一般来说,12位ADC两点校准就够了;16位以上的可以考虑多点校准。
五、工业数据采集项目:8通道16位ADC设计实录
我们给一家化工厂做的8通道数据采集模块,要求采集4~20mA电流信号,精度±0.2%FS,工作温度-20~60℃。
大概的设计思路:
- ADC:ADS1115,16位,4通道,I2C接口,两片级联实现8通道
- 参考源:REF3030,3.0V,0.1%精度,10ppm/℃温漂
- 前端:250Ω精密电阻把4~20mA转成1~5V,再加RC滤波
- 电源:24V输入→DC-DC降到5V→LDO降3.3V给数字,LDO降5V给模拟
- 校准:出厂前两点校准,校准参数存在内部Flash
- 软件滤波:限幅 + 20次滑动平均
最后实测的精度数据:
| 测试条件 | 原始误差(未校准) | 校准后误差 | 加滤波后波动 |
|---|---|---|---|
| 常温(25℃) | ±0.8% FS | ±0.15% FS | ±0.02% FS(峰峰值) |
| 低温(-20℃) | ±1.5% FS | ±0.3% FS | ±0.03% FS |
| 高温(60℃) | ±1.2% FS | ±0.25% FS | ±0.025% FS |
客户要求±0.5%FS,我们做到了±0.3%FS(全温范围),余量还挺大的。
六、7个ADC采集常见大坑
坑1:参考源用系统电源代替
现象:ADC读数随供电电压变化,电源波动的时候数据也跟着跳。
原因:很多图省事的做法——ADC的参考源直接接VCC。VCC是给系统供电的,上面有纹波,而且电压也不一定稳定,当然不准。
解决:高精度采集一定要用专用的参考电压源。哪怕是TL431,也比直接用VCC强。
坑2:模拟地和数字地乱接
现象:数据噪声大,有规则的干扰(比如50Hz工频、开关电源频率)。
原因:数字电路的开关噪声通过地回路耦合到模拟部分。
解决:模拟地和数字地分开布局,单点连接。如果是多层板,模拟层下面铺模拟地,数字层下面铺数字地。
坑3:输入阻抗不够,信号被拉低了
现象:测出来的值比实际值小,而且换个传感器误差就不一样。
原因:ADC的输入阻抗不够高,或者前端分压电阻太小,把传感器的信号拉低了。高输出阻抗的传感器(比如某些温度传感器、pH电极)特别容易出这个问题。
解决:前端加高输入阻抗的运放做电压跟随,或者选输入阻抗更高的ADC。
坑4:采样率太高,混叠了
现象:采到的信号里有奇怪的频率成分,跟实际信号对不上。
原因:采样率低于信号最高频率的2倍(奈奎斯特采样定理),发生了混叠。
解决:提高采样率,或者在ADC前面加抗混叠低通滤波,把采样率一半以上的频率滤掉。
坑5:多通道采集串扰
现象:多通道ADC,一个通道有信号,其他通道也跟着有,而且值跟那个通道有关。
原因:多通道ADC内部的模拟开关切换太快,前一个通道的电荷还没放掉,就切到了下一个通道,导致串扰。特别是高阻抗输入的时候更明显。
解决:切换通道后加个小延时再采样,或者每个通道采样多次,第一次的结果扔掉。也可以降低采样率,给模拟开关留出稳定时间。
坑6:运放自激振荡
现象:前端加了运放之后,噪声反而更大了,数据有高频抖动。
原因:运放电路自激了——可能是负载电容太大、反馈参数不对、或者电源退耦不好。
解决:在运放输出端加个小电阻(比如100Ω)驱动容性负载,或者在反馈电阻上并联一个小电容做相位补偿。用示波器看运放输出波形,有自激的话能看到高频振荡。
坑7:只校准常温,不考虑温漂
现象:常温下很准,到了现场(夏天热/冬天冷)就不准了。
原因:参考源、运放、电阻都有温漂,温度变了误差就大了。
解决:选低温漂的元器件(参考源、运放、电阻)。如果要求特别高,可以做温度补偿——测个温度传感器的值,根据温度修正校准系数。但这比较麻烦,能选好元器件就尽量选好的。
七、常见问题FAQ
Q1:STM32自带的ADC能做到多高精度?够用吗?
答:STM32的12位ADC,如果用内部参考源,不校准的话,大概±2%~±3%的精度。如果用外部参考源+校准+滤波,能做到±0.5%左右,一般应用够了。但要求更高(比如±0.1%以内),还是建议用外置ADC。内置ADC受MCU内部数字电路干扰大,而且参考源一般,极限精度有限。
Q2:16位ADC真的能用到16位吗?有效位数是多少?
答:一般用不到满16位。16位是分辨率,有效位数(ENOB)一般在12~14位左右,具体看你的设计水平。噪声大的话,可能有效位数还不到10位。衡量ADC实际效果,看有效位数比看分辨率更有意义。数据手册上一般会标ENOB参数,那个是理想条件下的,实际打个折。
Q3:4~20mA信号怎么采集?为什么用250Ω电阻?
答:4~20mA是工业上最常用的信号标准。采集的话,串一个精密电阻把电流转成电压,然后ADC采电压。250Ω电阻的话,4mA对应1V,20mA对应5V,刚好是很多ADC的输入范围(0~5V)。注意电阻要选精密的(0.1%精度以上),温漂也要小,不然电阻本身的误差就把精度吃掉了。
Q4:软件滤波是不是滤得越狠越好?
答:不是。滤波越狠,数据越平滑,但延迟也越大。比如你用100次平均,数据是平滑了,但信号变了之后要等很久滤波结果才跟上。对于需要快速响应的场合(比如报警、控制),延迟大了会出问题。选什么滤波、参数设多少,要根据实际需求来——平衡好平滑度和响应速度。
Q5:差分ADC和单端ADC有什么区别?什么时候用差分?
答:单端ADC是测信号对地的电压,差分ADC是测两个输入端之间的电压差。差分输入的好处是可以抑制共模干扰——比如两根线上都感应到了50Hz干扰,差分之后就抵消了。工业现场干扰大、或者信号是差分输出的(比如称重传感器、热电偶),就用差分输入。干扰小、信号是单端输出的,用单端就行,省通道。
总结
高精度ADC采集,是个系统工程——不是选个高分辨率ADC就完事了,参考源、前端运放、电源、PCB布局、软件滤波、校准,每个环节都影响最终精度。
我们做了这么多年采集,最深的体会是:硬件决定上限,软件决定下限。硬件设计好了,软件稍微处理一下就能达到不错的精度;硬件没设计好,再复杂的软件算法也救不了你。
所以做采集项目,先把硬件做扎实,再考虑软件优化。顺序别搞反了。
希望这篇文章对你有帮助。如果你的采集项目遇到了精度问题、干扰问题,也可以找我们聊聊——从毫伏到伏、从12位到24位,各种坑我们都踩过。
还想接着看
- 嵌入式传感器系统硬件设计——ADC 前端信号链、运放选型和 EMC 是采集精度的硬件半边天。
- 数据采集系统架构设计——把 ADC 数据一路送到云平台的完整工程链路,正好接在采集口后面。
- DS18B20 多点测温组网——温度这类常见模拟量采集,用单总线传感器的低成本实现也是一种典型做法。