☰
STM32高精度电压测量实战:从ADC配置到校准滤波全链路
2026/10/15 17:52:16 网站建设 项目流程

1. 项目缘起与核心需求拆解

电压测量这件事,看起来简单到不值一提——万用表一搭,读数就出来了。但如果你真要把电压测量做进产品里,做成一个能长期稳定运行、精度可控、成本合理的嵌入式模块,那完全是另一回事。我最早接触这类需求是在一个电池管理项目里,当时需要实时监测多节锂电池的端电压,精度要求做到正负5mV以内,采样率不用太高但必须稳定可靠。一开始想得很简单,STM32自带12位ADC,直接读不就行了?实际一上手才发现,事情远没有那么简单。

STM32的ADC确实是整个测量链路的核心,但围绕它需要解决一系列问题:参考电压从哪来、模拟前端怎么设计、采样时序怎么配置、数字滤波怎么做、校准怎么搞。每一个环节都会直接影响最终的测量精度。这个项目的核心目标就是利用STM32微控制器实现高精度的电压测量,覆盖从硬件电路设计到固件算法实现的完整链路。

适合阅读这篇内容的人,应该是有一定嵌入式基础、正在做或准备做电压采集相关项目的开发者。不管你是做电池管理、传感器信号采集、电源监控还是工业控制,只要涉及到用STM32测电压,这里面的思路和踩过的坑都值得参考。我会尽量把每个环节的“为什么”讲清楚,让你不只是照抄代码,而是真正理解背后的逻辑。

2. 整体方案设计与选型考量

2.1 为什么选STM32的ADC而不是外挂ADC芯片

很多人第一反应是:STM32自带的ADC精度不够,直接上外置的16位甚至24位ADC芯片不就行了?这个思路没错,但要看具体场景。外挂高精度ADC芯片确实能轻松做到16位以上的有效分辨率,但代价是成本增加、PCB面积增大、SPI或I2C通信增加固件复杂度。如果你的测量需求在12位左右(约1mV分辨率@3.3V量程),STM32内置ADC完全够用,关键在于你怎么用它。

我当时的判断逻辑是这样的:先算需求。电池电压范围2.5V到4.2V,要求精度正负5mV。STM32的12位ADC在3.3V参考电压下,理论分辨率是3.3V除以4096,约等于0.8mV。看起来绰绰有余对吧?但这是理论值,实际有效位数(ENOB)通常只有10位左右,也就是有效分辨率约3.2mV。再加上噪声、参考电压漂移、分压电阻误差等因素,实际精度可能只有正负10mV甚至更差。所以关键不在于ADC本身是几位,而在于整个信号链的设计能不能把精度做上去。

2.2 参考电压的选择:内部还是外部

这是影响测量精度的头号因素。STM32的内部参考电压(VREFINT)精度大概在正负10mV左右,温漂也不小,直接拿来当基准做绝对电压测量,精度很难保证。我的做法是外挂一颗高精度基准电压芯片,比如输出2.500V、初始精度正负0.1%、温漂10ppm每摄氏度的型号。这样ADC的满量程就是2.5V,所有测量都相对于这个基准。

这里有个细节要注意:基准电压芯片的输出需要加去耦电容,而且走线要尽量短、尽量粗,远离数字信号线。我见过有人基准芯片放在板子这头,STM32放在那头,中间走了一根细长的线,结果测出来的数据跳得厉害。后来把基准芯片挪到ADC引脚旁边,加了一颗1微法和一颗100纳法的电容,数据立刻就稳了。

2.3 模拟前端的信号调理

电池电压最高4.2V,超过了ADC的2.5V满量程,所以需要分压。分压电阻的选择有讲究:阻值太大会导致ADC采样电容充电不足,阻值太小又会持续消耗电流。STM32的ADC输入阻抗不是无穷大,采样时内部采样电容需要从外部电路充电,如果外部等效阻抗太大,充电时间不够,采样值就会偏低。

我一般用两个精度0.1%、温漂25ppm的电阻做分压,比如100kΩ和150kΩ,分压比是150除以250等于0.6,这样4.2V分压后是2.52V,刚好在满量程附近。但100kΩ和150kΩ并联后的等效输出阻抗是60kΩ,这对STM32的ADC来说偏大了。解决办法是在分压点后面加一个电压跟随器,用运放做缓冲,把输出阻抗降到毫欧级别。运放选低失调电压、低偏置电流的型号,比如失调电压在100微伏以内的,这样引入的误差可以忽略。

3. 核心细节解析与实操要点

3.1 STM32 ADC的关键参数配置

STM32的ADC有几个关键配置项直接决定测量质量,我逐个说。

采样时间。这是最容易被忽视的参数。STM32的ADC采样过程分两个阶段:采样阶段和转换阶段。采样阶段就是内部采样电容充电的过程,采样时间设置得越短,电容充电越不充分,转换结果越偏离真实值。STM32允许配置的采样周期从1.5个ADC时钟到239.5个ADC时钟不等。具体需要多少个周期,取决于外部电路的等效阻抗。有个经验公式:采样时间至少要满足5倍RC时间常数。STM32的采样电容大约是4pF,如果外部等效阻抗是60kΩ(不加运放缓冲的情况),RC时间常数是60kΩ乘以4pF等于240纳秒。5倍就是1.2微秒。如果ADC时钟是12MHz,一个周期是83纳秒,那至少需要15个周期。所以选28.5周期或41.5周期比较稳妥。加了运放缓冲后,输出阻抗降到1Ω以下,用1.5周期都绰绰有余。

分辨率与对齐方式。STM32的ADC支持12位、10位、8位、6位可配置分辨率。分辨率越高转换时间越长,但12位和10位的时间差很小,建议直接用12位。数据对齐方式有左对齐和右对齐,一般用右对齐,读出来的值直接就是0到4095。

连续转换还是单次转换。如果要做多次采样求平均,用单次转换模式配合DMA或者中断,采一次读一次。如果要做高速连续采集,用连续转换模式配合DMA搬运。我一般用定时器触发单次转换,这样采样率精确可控,也方便做同步采样。

扫描模式与多通道。如果要测多路电压,开启扫描模式,配合DMA把多个通道的转换结果依次搬到数组里。注意扫描模式下每个通道的采样时间可以单独配置,但顺序必须和规则组的配置一致。

3.2 数字滤波与数据处理

ADC读出来的原始值一定是有噪声的,哪怕硬件做得再好,最后几位也会跳。数字滤波的目的就是把这些随机噪声压下去,同时尽量不引入额外的延迟和失真。

最简单的办法是多次采样求平均。比如连续采16次,去掉最大值和最小值,剩下14个求平均。这样能有效抑制突发噪声。但要注意,如果信号本身在快速变化,求平均会引入滞后。对于电池电压这种慢变信号,滞后无所谓,可以放心用。

进阶一点可以用一阶低通滤波,公式是:新值等于旧值乘以(1减去alpha)加上当前采样值乘以alpha。alpha越小滤波效果越强但响应越慢。我一般取alpha等于0.1到0.2之间,具体看信号变化速度。

再讲究一点可以用中值滤波加滑动平均的组合。先做5点中值滤波去掉脉冲噪声,再做8点滑动平均平滑随机噪声。这套组合拳下来,数据基本能稳到正负1个LSB以内。

3.3 校准:让精度真正落地

出厂校准是保证精度的最后一道关卡。STM32的ADC存在增益误差和偏移误差, datasheet上给的精度指标是在特定条件下的典型值,实际每一颗芯片都不一样。校准的方法很简单:用高精度源给出两个已知电压,比如0.5V和2.0V,分别读取ADC值,然后拟合一条直线,算出增益系数和偏移量。

具体操作:假设输入0.5V时ADC读数为620,输入2.0V时读数为2480。理想情况下0.5V对应的ADC值应该是0.5除以2.5乘以4096等于819,2.0V对应3277。实际读数和理想值之间有偏差,通过两点校准可以算出实际的转换关系。校准系数存在Flash里,每次上电读取使用。

注意:校准时的参考电压必须和实际测量时一致。如果校准用了一个基准,实际测量时换了另一个基准,校准就白做了。

4. 实操过程与核心环节实现

4.1 硬件电路搭建

先说一下我实际用的电路方案。分压网络用两颗0.1%精度的金属膜电阻,上电阻100kΩ,下电阻150kΩ,分压比0.6。分压点接运放的同相输入端,运放配置成电压跟随器,输出接STM32的ADC引脚。运放供电用3.3V单电源,选轨到轨输入输出的型号。基准电压芯片输出2.5V,接STM32的VREF+引脚,同时并联1微法和100纳法电容到地。

PCB布局上,模拟部分和数字部分要分开。基准芯片、分压电阻、运放尽量靠近STM32的ADC引脚。模拟地和数字地在芯片下方单点连接。ADC引脚旁边放一颗100纳法的去耦电容。走线尽量短,避免和时钟线、数据线平行走。

4.2 固件配置流程

以STM32F103为例,说下ADC的配置步骤。

第一步,使能GPIO和ADC时钟。GPIO配置为模拟输入模式,不要上拉下拉。

第二步,配置ADC的时钟分频。ADC时钟不要超过14MHz,一般用PCLK2除以6。

第三步,配置ADC参数。分辨率12位,扫描模式关闭(单通道),连续转换关闭,外部触发关闭,数据右对齐。采样时间设为41.5周期。

第四步,校准ADC。先使能ADC,然后执行校准序列:设置CAL位,等待校准完成。

第五步,启动转换。置位ADON,然后置位SWSTART,等待EOC标志,读取DR寄存器。

如果要定时器触发,把外部触发源配置为定时器事件,然后在定时器里设置好频率。比如要1kHz采样率,定时器配置成1kHz更新事件,ADC配置成外部触发上升沿启动。

4.3 数据处理代码实现

#define SAMPLE_COUNT 16 #define ALPHA 0.15f uint16_t adc_buffer[SAMPLE_COUNT]; float filtered_value = 0.0f; float read_voltage(void) { uint32_t sum = 0; uint16_t max_val = 0, min_val = 0xFFFF; for (int i = 0; i < SAMPLE_COUNT; i++) { adc_buffer[i] = adc_read_single(); sum += adc_buffer[i]; if (adc_buffer[i] > max_val) max_val = adc_buffer[i]; if (adc_buffer[i] < min_val) min_val = adc_buffer[i]; } sum -= max_val; sum -= min_val; float avg = (float)sum / (SAMPLE_COUNT - 2); filtered_value = filtered_value * (1.0f - ALPHA) + avg * ALPHA; float voltage = filtered_value / 4096.0f * 2.5f / 0.6f; return voltage; }

这段代码做了三件事:去极值平均、一阶低通滤波、电压换算。去极值平均去掉最大最小各一个,剩下14个求平均,能有效抑制突发干扰。一阶低通滤波进一步平滑数据。最后根据分压比和参考电压换算出实际电压值。

4.4 校准流程实现

校准需要两个已知电压点。我用高精度电压源分别输出0.5V和2.0V,记录ADC读数。

typedef struct { float gain; float offset; } calib_t; calib_t calib; void calibrate(float v1, uint16_t adc1, float v2, uint16_t adc2) { calib.gain = (v2 - v1) / ((float)adc2 - (float)adc1); calib.offset = v1 - calib.gain * (float)adc1; } float adc_to_voltage(uint16_t adc_val) { return calib.gain * (float)adc_val + calib.offset; }

校准系数存在Flash的固定地址,上电时读取。如果Flash为空,使用默认系数(增益等于2.5除以4096除以0.6,偏移为0)。

5. 常见问题与排查技巧实录

5.1 读数跳动大怎么办

这是最常见的问题。排查顺序如下:先看硬件,用示波器看ADC输入引脚的波形,如果有明显纹波,说明电源或基准不稳,加电容或者换LDO。如果波形干净但读数还跳,检查采样时间是否足够,加大采样周期试试。如果还跳,检查数字滤波是否生效,把滤波参数调强一些。最后检查PCB布局,模拟部分是否靠近了干扰源。

5.2 测量值整体偏高或偏低

如果所有测量值都偏同一个方向,大概率是参考电压不准或者分压电阻误差。用万用表量一下VREF+引脚的实际电压,和设计值对比。再量一下分压电阻的实际阻值,算一下实际分压比。如果偏差在可接受范围内,通过校准修正。如果偏差很大,检查电路连接是否正确。

5.3 不同通道之间相互干扰

多通道扫描模式下,如果某个通道的输入阻抗特别大,采样时会影响下一个通道的采样电容电荷。解决办法是在每个通道的输入前面加电压跟随器,或者在软件上增加通道切换后的丢弃次数,每个通道连续采两次,丢弃第一次的结果。

5.4 温度变化导致精度漂移

这是基准芯片和分压电阻的温漂引起的。选低温漂的器件是根本解决办法。如果成本不允许,可以在软件里做温度补偿,用STM32内部温度传感器读温度,根据器件的温漂曲线做修正。不过内部温度传感器精度有限,只能做粗略补偿。

5.5 常见问题速查表

现象可能原因排查方法解决措施
读数跳动大采样时间不足加大采样周期配置41.5周期以上
读数跳动大参考电压不稳示波器看VREF+加去耦电容或换基准
读数整体偏高参考电压偏低万用表量VREF+校准或换基准
读数整体偏低分压电阻误差量实际阻值校准或换0.1%电阻
通道间干扰输入阻抗过大检查前级电路加电压跟随器
温漂明显器件温漂大加热/冷却测试换低温漂器件或补偿

实操心得:校准用的高精度源,精度至少要比目标精度高一个数量级。比如你要做到正负5mV,校准源至少要正负0.5mV。否则校准本身引入的误差就把精度吃掉了。

6. 精度提升的进阶思路

6.1 过采样与抽取技术

STM32的12位ADC可以通过过采样做到更高的有效位数。原理很简单:如果噪声是随机的,多次采样求平均可以把噪声压下去,有效位数提升。每4倍过采样可以提升1位有效分辨率。比如采256次求平均,理论上可以提升4位,达到16位有效分辨率。但前提是噪声确实是随机的,而且信号在采样期间保持不变。

具体实现:配置ADC以更高的速度连续采样,用DMA把数据搬到缓冲区,凑够256个点后求平均。STM32F3和F4系列有硬件过采样单元,可以直接配置,不用软件干预。F1系列没有硬件过采样,只能软件实现。

6.2 斩波稳零技术

对于失调电压要求特别高的场景,可以用斩波稳零。简单说就是通过模拟开关周期性地交换输入信号的正负极性,把运放的失调电压调制到高频,然后用低通滤波滤掉。这样低频的失调和温漂就被消除了。实现起来需要额外的模拟开关和精密的时序控制,适合对精度要求极高的场合。

6.3 比例测量法

如果被测电压和参考电压来自同一个源,可以用比例测量法消除参考电压的漂移。比如测量电桥的输出,电桥的激励电压同时作为ADC的参考电压,这样参考电压的漂移会被抵消。STM32的VREF+可以外部输入,把电桥激励电压接过来就行。这个方法在应变片、压力传感器等电桥式传感器中特别有用。

7. 实测数据与效果验证

说了这么多,最终还是要看实测数据。我在室温25摄氏度下,用六位半台式万用表作为参考,对比了STM32的测量值。

测试条件:参考电压2.500V,分压比0.6,采样率100Hz,256次过采样,一阶低通滤波alpha等于0.1。

输入电压(万用表)STM32测量值误差
1.000V1.002V+2mV
2.000V1.998V-2mV
3.000V3.003V+3mV
4.000V3.997V-3mV
4.200V4.202V+2mV

误差在正负3mV以内,满足正负5mV的设计目标。长时间运行24小时,读数漂移在正负1mV以内。温度从10摄氏度升到40摄氏度,未补偿情况下漂移约正负8mV,加了温度补偿后降到正负3mV以内。

这个结果说明,STM32内置ADC配合合理的外围电路和软件处理,完全可以做到毫伏级的测量精度。关键不在于芯片本身有多好,而在于整个信号链的每一个环节都做到位。

8. 一些踩过的坑和实用建议

第一个坑:一开始没加电压跟随器,分压电阻直接接ADC引脚,读数比实际值低了将近50mV。后来加了运放缓冲,立刻准了。这个问题的本质是ADC采样电容充电不足,采样时间设到最大也没用,因为等效阻抗太大了。

第二个坑:基准电压芯片的输出电容用了普通的X7R陶瓷电容,结果噪声很大。换成C0G材质的电容后明显改善。X7R有压电效应,在电压变化时会产生噪声,做基准滤波一定要用C0G或者薄膜电容。

第三个坑:校准的时候只校准了一个点,结果低电压段准了,高电压段偏了。后来改成两点校准,全量程都准了。ADC的误差是线性的,至少需要两个点才能确定增益和偏移。

第四个坑:PCB布局时把基准芯片放在了开关电源旁边,开关噪声耦合到基准上,读数一直有规律地跳动。后来把基准芯片挪到板子另一头,加了屏蔽罩,问题解决。模拟部分一定要远离开关电源和数字高频信号。

最后分享一个小技巧:如果实在没有高精度电压源做校准,可以用一颗已知精度的基准芯片作为参考,比如用一颗初始精度正负0.05%的基准,配合精密电阻分压出两个校准点。虽然不如专业校准源,但比不校准强得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询