ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32 ADC电压采集精度提升:分压电阻设计避坑指南

STM32 ADC电压采集精度提升:分压电阻设计避坑指南 1. 分压测量方案的整体设计思路1.1 为什么分压电阻的选择会直接决定ADC采集的精度先聊个很常见的翻车场景某天你拿STM32F103C8T6想做电池电压采集电路很简单电池正极串个电阻、再并个电阻分出一路电压拉到PA0脚。板子焊完上位机一看电压读数比万用表差了0.3V还多甚至跳来跳去。你先怀疑ADC坏了再怀疑代码写得不对折腾半天才发现问题出在那两个电阻上。这种问题我在实际项目里踩过不止一次也帮人排查过好多次。ADC本身只是一个把模拟电压变成数字量的部件它测量的对象是“引脚上的电压”而不是“你脑子里想的那个电压”。而分压电阻的作用就是把待测电压转换成一个ADC能安全、准确采样的低电压。这个转换过程如果设计得不严谨误差会一路传播到最终显示结果里。很多开发者在计算时只用了最简单的公式Vout Vin × R2 / (R1 R2)算出个比例完事却忽略了电阻精度、源阻抗、温漂、采样时间、引脚下拉等效阻抗等一系列问题最后精度崩了都不知道原因出在哪。这篇内容不会只停留在“你要用高精度电阻”这种正确的废话上而是从电路设计、参数计算、STM32的ADC配置到实测数据完整走一遍流程告诉你每个环节该怎么算、怎么选、怎么验证。适合正在做嵌入式项目但被模拟量采集精度折磨过的朋友也适合刚接触STM32 ADC、想少走弯路的学生开发者。1.2 高边采样和低边采样先想清楚电路结构很多人直接拿两个电阻一串就完事但对“被测电压”和“系统参考地”之间的关系没有仔细思考。以最常见的电池电压采集为例电池正极接R1R1另一端接R2R2另一端接GNDR1和R2的连接点就是ADC输入。这个结构把“相对于GND的正电压”降压后送给ADC是低边分压的标准接法。但如果被测信号本身不是以GND为参考的比如你要采集一个悬浮电压或者采集的是高边电流采样电阻两端电压这种简单分压结构就不成立需要引入差分放大器或仪表放大器。PCB空间允许、成本允许的前提下把信号预处理放到运算放大器里处理比强行分压要靠谱得多。这里还有一个容易踩的坑如果你要采集的是类似TL431反馈网络中的分压点或者ATX电源辅助绕组反馈电路里的采样电压这种场景下分压电阻不仅决定反馈量还会影响到TL431的阴极电流和环路稳定性电阻取值不能只看分压比还要满足最小工作电流要求。原理和STM32分压是相通的都是一个电阻网络做比例衰减但工程约束条件不同。放到我们ADC采集的场景里同样要先问三个问题被测电压范围是多少会不会有超过ADC输入耐压的瞬间被测电压的源阻抗高不高信号能不能驱动ADC内部采样电容分压网络的功耗是否在系统预算内电池供电的设备长期接在分压电阻上耗电是不能忽略的。这三个问题确定之后再去算R1、R2的具体阻值才不会返工。2. 电阻选型背后的量化计算2.1 由待测电压范围反推分压比假设现在要做的是一个0V到16V的直流电压采集STM32的VDDA接3.3VADC是12位满量程对应数字量4095。第一步不是上手挑电阻而是定分压比。分压比n Vout / VinVout取ADC输入范围要留出安全余量。比如3.3V供电的ADC理论上最大能测3.3V但实际电路里3.3V有波动而且接近满量程时转换误差会略变大所以最好把最大采样电压控制在3.0V以下。对应16V范围分压比n 3.0 / 16 ≈ 0.1875。分母分子同时乘个方便计算的数比如取R1 100kΩ、R2 22kΩ分压比就是22/122 ≈ 0.180316V输入时输出2.885V余量足够。如果取R1 100kΩ、R2 20kΩ分压比0.166712V输入时输出2.0V16V输入时输出2.667V这也行而且计算方便。分压比选好后还要检查一个东西待测电压的源内阻。假设待测信号源本身有10kΩ内阻那么串联在分压网络里的等效电阻就会改变实际分压比也跟着变。对高内阻信号源最好在分压前加一级电压跟随器。对绝大多数项目这里的前提是被采集电压源的输出阻抗很低或已经过稳压。这里顺带验证一个点即“总电阻取多大”。有些初学者习惯用4.7kΩ甚至1kΩ电阻做分压理由是“阻抗低了采样稳”。确实源阻抗低了ADC采集更稳但代价是分压网络功耗变大。12V供电时R1R25.7kΩ消耗电流约2.1mA24小时就要耗掉约0.6Wh对电池供电的产品是不能接受的。100kΩ加22kΩ组合下待机功耗只有约0.1mA一年耗电不到1mAh按3.7V电池折算约0.3mAh完全不是一个量级。所以定位决定取值实验室验证板可以用低阻电阻便携设备必须考虑静态功耗。2.2 电阻精度与温漂带来的误差到底有多大这是整个避坑指南的核心。电阻允许误差、温度系数都直接影响分压精度而分压比的误差会直接映射到被测电压上。先看电阻精度。假设用了最不讲究的5%精度电阻R1标称100kΩ实际阻值可能在95kΩ到105kΩ之间R2标称20kΩ实际在19kΩ到21kΩ之间。分压比可能出现的极限情况最大分压比R2取上限21kΩR1取下限95kΩn_max 21/116 ≈ 0.1810最小分压比R2取下限19kΩR1取上限105kΩn_min 19/124 ≈ 0.1532两者相比分压比从理想值的0.1667偏离到不同方向最大偏差约-8.1%。在读12V电压时计算得到的测量结果可能是11.0V到12.6V之间这在很多应用里已经不可接受了。换成1%精度电阻R1的范围是99kΩ到101kΩR2是19.8kΩ到20.2kΩ极限分压比约在0.1618到0.1717之间偏差约-2.9%到3.0%。读12V时结果落在11.65V左右到12.36V左右仍然有0.7V的波动。再换成0.1%精度电阻极限偏差约0.3%12V读数偏差大约0.036V这个精度对于多数电压监测项目就足够了。不过0.1%电阻价格高而且市场上很多标称0.1%的贴片电阻温度漂移仍然有25ppm/°C甚至50ppm/°C。温度系数带来的影响也不能忽视设两个电阻温漂方向相反温度变化50°C分压比可能再偏移千分之几。有一种做法是只买0.1%电阻然后在校准阶段用高精度万用表实测每个电阻的准确值把实际阻值写进代码做参数修正。实测校准能补偿常温下的固定偏差但不能补偿温漂。所以最稳妥的方法是选温漂指标明确的电阻比如25ppm/°C或更好如果系统温度变化很大可以用软件查表做温度补偿或者干脆上数字电位器加自动校准回路。2.3 源阻抗、采样电容和采样时间低频电路里的高频问题分压电阻选得再准如果采样时间不够ADC读出来的电压还是会偏低。很多人不理解为啥会有这回事其实道理很简单STM32 ADC内部并不是一个无穷大输入阻抗的理想电压表它工作时会通过内部模拟开关把外部电压接到一个采样电容上这个电容在采样阶段要充电到跟引脚电压一致。电容充电需要时间时间常数正比于外部源阻抗和内部采样电容的乘积。STM32F1系列ADC内部采样电容典型值在几皮法到十几皮法之间具体数值要查参考手册。以12pF为例如果外部源阻抗是20kΩ那么时间常数τ约等于0.24µs。ADC转换过程中采样阶段由采样时钟周期数决定。STM32F103的ADC采样时间可配置为1.5周期、7.5周期、13.5周期、28.5周期、41.5周期、55.5周期、71.5周期和239.5周期。ADC时钟频率最大14MHz对应周期约71.4ns采样时间是周期数乘这个值。如果采样时间设为1.5周期采样窗口只有约107ns连0.5个τ都不到电容根本充不满测量结果会明显偏低。如果设为41.5周期约2.96µs大约能充到接近12个τ误差已经很小。为保证12位精度我习惯至少用55.5周期以上。这里有个快速估算公式外部源阻抗尽量控制在10kΩ以下。当分压网络等效阻抗超过50kΩ时哪怕是239.5周期的采样时间也不一定能把误差压到1个LSB以内尤其ADC时钟还跑到14MHz时更要小心。特别提醒一下分压网络的等效阻抗不是R1R2而是R1和R2的并联值也就是从ADC引脚往回看进去看到的等效源阻抗。100kΩ和20kΩ并联后约16.7kΩ这数字看着不大但在高速采样或高精度应用里已经不能再往上加了。想让源阻抗更低可以把两个电阻同时缩小比如改成10kΩ和2kΩ并联等效约1.67kΩ采样非常“稳”但静态功耗也会涨到约1mA12V。所以做设计时永远是在精度、功耗、面积和成本之间做交换。3. STM32工程配置与采集代码实现3.1 CubeMX配置要点采样时间、时钟和参考电压这里以STM32F103C8T6 STM32CubeIDE为例把ADC采集流程过一遍。CubeMX里需要关注几个关键配置项。一是ADC时钟分频。F1的ADC时钟来自APB2最高14MHz。如果APB2是72MHz那么ADC预分频至少选6分频得到12MHz。有人为了追求采样率把ADC时钟压到最大14MHz是可以的但高ADC时钟下同样采样周期数对应的绝对采样时间更短外部源阻抗要更低。二是扫描模式和连续转换。对单通道采集ScanConvMode选择DisableContinuousConvMode看需求。如果只是定期测一次电压关掉连续转换用定时器触发或软件触发更省电如果要监控动态电压开连续转换加DMA传输更合适。三是采样时间这是最容易踩坑的配置项。之前在2.3节算过外部源阻抗16.7kΩ时采样时间至少要55.5个周期才能保证12位精度。CubeMX里直接把Sampling Time选成55.5 Cycles或239.5 Cycles别图省事保留默认的1.5 Cycles。虽然很多信号源内阻低时1.5周期也能用但对分压电阻网络这种动不动几十kΩ源阻抗的场景采样时间不够就是读数偏低而且这种偏低不是线性偏移是你很难用软件校准补偿的。四是校准。STM32F1的ADC内部有校准机制CubeMX会在ADC初始化时自动执行校准函数但要注意校准必须在ADC使能之前完成。如果你手动重写了HAL初始化流程漏了校准ADC的零点偏移和增益误差会变大实测结果会整体偏移几十个LSB。参考电压方面STM32F103的ADC参考电压就是VDDA引脚电压一般接3.3V或外部精密基准源。如果项目对电压测量精度要求很高建议给VDDA单独用LDO供电或加一个外部基准芯片比如REF3030、TL431改的参考源否则VDDA随负载波动会直接影响转换结果。这里顺便说个经验很多开发板用AMS1117给3.3V供电AMS1117输出精度本身有2%到3%的偏差所以“Vref3.3V”只是一个理想假设实际要拿万用表实测VDDA电压再代入软件计算。3.2 采集与换算代码除了公式还有两个坑CubeMX配置完成后直接调HAL库的ADC接口就能完成一次采集。核心代码很简单uint32_t adc_read_once(void) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 100); uint32_t val HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); return val; }软件换算部分我用的公式是float VREF_ACTUAL 3.28f; // 用万用表实测VDDA得出 const float R1 100000.0f; // 实测实际阻值 const float R2 20000.0f; // 实测实际阻值 const float DIVIDER_RATIO R2 / (R1 R2); const float FULL_SCALE 4095.0f; float get_input_voltage(void) { uint32_t raw adc_read_once(); float vadc (float)raw * VREF_ACTUAL / FULL_SCALE; float vin vadc / DIVIDER_RATIO; return vin; }这里有两个易错点。第一个是VREF不能写死3.3V。STM32F103内部没有高精度基准VDDA就是参考电压。很多开发板的3.3V实测只有3.23V甚至3.15V你写死3.3V读出来的电压整体偏高2%以上。所以要么实测要么把VREF_ACTUAL做成可校准参数。第二个是分压电阻的实测值要替代标称值。我习惯在硬件调试阶段用四位数以上的台式万用表测量每个电阻拿到实际阻值后写进宏定义。这样做的效果等同于把电阻精度从5%或1%提升到仪表精度成本几乎为零。有朋友问那我能不能只做软件校准不分压比直接线性拟合当然可以但用实测电阻值更接近物理本质校准点也更稳定。实际项目里我还会在ADC前端加一个RC低通滤波从分压点到ADC引脚之间串联一个1kΩ电阻再对地并联一个100nF电容。这个RC有两个作用一是滤掉高频噪声二是给ADC采样电容提供一个低阻抗的电荷源。加了RC之后ADC引脚处等效阻抗还是1kΩ左右但高频干扰被滤掉了读数稳定性会明显提升。注意串联电阻也不能太大超过10kΩ会反而增加源阻抗对采样时间要求更高。3.3 均值滤波与异常值剔除好数据是过滤出来的即使电路设计没问题单次ADC转换结果也可能受噪声干扰出现跳动。最直接的办法是连续采多次再求平均。我常用的软件滤波方式分三层中值滤波连续采5次或7次排序后取中间值能有效剔除尖峰毛刺。算术平均对中值滤波后的连续N个结果求平均比如取16次平均能把白噪声降低到原来的四分之一。限幅滤波和上一次有效值的差超过一定阈值就丢弃防止异常跳变直接进到显示值。这三个组合起来就类似滑动窗口的中值平均滤波适合直流电压采集场景。代码写起来也不复杂uint32_t filter_adc_value(void) { uint16_t buf[5]; for (int i 0; i 5; i) { buf[i] adc_read_once(); } // 冒泡排序取中值 for (int i 0; i 4; i) { for (int j i 1; j 5; j) { if (buf[j] buf[i]) { uint16_t tmp buf[i]; buf[i] buf[j]; buf[j] tmp; } } } return buf[2]; }之后还要求平均的话把上面这个中值函数调多次再平均即可。滤波不能替代硬件设计但能解决最后那一点“读数乱跳”的问题。对实时性要求高的场景每次转换都在做一个滤波反而影响采样率这时可以选择开DMA 连续转换把一批采样结果收集完再做后处理。4. 实测数据与误差复盘4.1 使用不同精度电阻的实测对比讲完理论用实际数据说话。我在工作台上用一个可调稳压电源输出标称12.00V用六位半万用表实测为12.003V分别搭了三套分压网络接入同一块STM32F103C8T6的最小系统板VDDA实测3.28V采样时间设为55.5周期软件用3.2节代码换算电压。第一组用5%精度电阻R1标称100kΩ实测97.8kΩR2标称20kΩ实测21.3kΩ。第二组用1%精度电阻R1实测100.4kΩR2实测19.9kΩ。第三组用0.1%精度电阻R1实测100.01kΩR2实测20.03kΩ。结果如下表电阻组别ADC原始值软件显示电压相对误差备注5%电阻276411.09V-7.6%分压比偏差直接带入1%电阻281412.19V1.56%误差来自R1和R2单只偏差0.1%电阻279612.02V0.14%接近万用表实测值由此可见5%电阻带来的误差高达0.9V这在整个12V系统里足以触发低压告警或者电压异常判断。而0.1%电阻的实测结果已经非常理想但这个准确度的前提还是“没有把电阻阻值实际测量后写进代码”。更夸张的是如果我先用万用表实测了5%电阻的阻值再用实测值代入软件计算误差会立刻从7.6%降到0.2%以内。这说明什么说明电阻标称精度是硬件层面的上限而实际精度可以通过校准来逼近。4.2 误差来源分解与优先级排序做一个12V电压采集系统可能误差来源按贡献排序大概是这样的误差来源典型大小是否可以软件补偿分压电阻精度偏差0.5%到8%可以用实测阻值或两点校准VDDA参考电压偏差1%到3%部分可以实测后写死ADC量化误差0.024%12位1LSB0.8mV可降噪但不能消除ADC积分非线性误差INLF1系列约2 LSB难校准采样时间不足导致的充电误差0.1%到5%修改采样时间设置PCB走线串扰、电源噪声0.1%到1%硬件滤波、布局改善在排查一个采集误差问题时我的习惯是从大到小一项项排除先用万用表确认分压点实际电压如果这个电压都不对那是硬件问题如果分压点电压对但ADC读数不对再查采样时间和VDDA如果这些都排除才怀疑STM32本身的非线性。很多朋友一上来就怀疑ADC坏掉其实八成是前端电路和基准电压的问题。这里面最容易被人忽略的是VDDA的影响。STM32F103的VDDA如果直接接在电源模块输出端而电源模块带载后电压下降了0.1V那么在3.3V基准下计算出的结果就会偏移约3%。如果你的系统里VDDA还同时给LED或电机驱动供电纹波会直接干扰ADC结果。设计时务必把VDDA和数字电源用磁珠或RC隔离有条件就用独立LDO。5. 常见问题排查与避坑笔记5.1 读数异常跳动的排查流程把这个排查过程写成速查表遇到问题可以按顺序对照现象可能原因排查方法读数整体偏高VDDA实际值比程序写的低用万用表测VDDA修正代码读数整体偏低分压电阻阻值偏小或采样时间不足实测电阻值加大采样周期数读数跳动大外部噪声、引脚悬空、接线过长加RC低通滤波缩短飞线使用屏蔽线靠近满量程读数非线性分压设计余量不足接近ADC饱和重新规划分压比留足余量采样值有规律波动开关电源纹波干扰改善电源滤波ADC输入加电容多通道间相互干扰引脚间串扰或DMA搬运出错增加采样间隔检查通道切换时序这里特别提一个现象用杜邦线从分压板连到开发板引脚时读数的跳动往往大于0.5%。杜邦线尤其是那种十几厘米长的母对母杜邦线等效电感和天线效应都很明显在开关电源环境里能耦合出几十毫伏的噪声。最简单的办法是直接把分压电阻焊在靠近STM32引脚的地方或者用双绞线把分压信号引到板上并在引脚附近加100nF电容。5.2 向ADC引脚灌入过高电压的后果有人会用分压比算出一个看似安全的输出电压但忽略了上电瞬间或热插拔时可能出现的高压脉冲。如果待测电压突然冲到24V而分压比按16V满量程设计输出电压就达到4V这已经超过STM32引脚允许的耐压范围长时间或反复冲击会损坏引脚内部的ESD保护二极管严重时芯片直接报废。设计阶段从两个方向防第一分压比按“可能出现的最大值”而不是“正常工作值”来设计第二给ADC引脚加钳位保护比如对地并联一个3.3V的TVS二极管或肖特基二极管或者串联一个10kΩ电阻限制输入电流。加了钳位保护的代价是源阻抗变大需要再加深RC滤波。我在批量产品上通常使用串联10kΩ电阻加外部肖特基二极管的方式既能钳位又能限流RC截止频率也低抗干扰能力更强。5.3 换用GD32、S32K等芯片时STM32经验不能照搬最近不少项目在往国产芯片或车规芯片迁移比如GD32、HC32、S32K系列。它们部分型号的ADC架构和寄存器兼容STM32但时序和内部模拟参数差别不小。GD32H7系列就带有硬件滤波功能可以在ADC内部对多次采样结果做平均不需要CPU参与。S32K312的ADC支持不同采样相位、不同转换模式配置比STM32复杂很多不能把CubeMX里那一套完全照搬过去。经验就一条拿到任何芯片先看参考手册里的ADC章节找到采样电容大小、最大源阻抗推荐值和采样时间计算表。数据手册里写的“最大外部阻抗”往往是一个保守参考值实际工程里要结合采样时间和精度要求重新算。之前硬搬STM32配置到国产芯片翻过车的案例不少症状基本就是寄存器配置看着没问题转换结果却偏得离谱其实是因为内部采样电容更大或ADC时钟分频不同。除了芯片本身开发环境也容易卡壳。比如Keil里同时装了C51和STM32的芯片包或者STM32F103的芯片包版本过旧工程编译时会出现头文件冲突。换芯片后务必检查芯片包版本CubeIDE里也要确认下载的固件包与芯片型号匹配。遇到USB识别不了设备这种问题先查驱动和ST-Link固件版本别急着认为是芯片坏了。5.4 两个被反复问到的小问题问得最多的一个问题是能不能直接采集STM32内部的VREFINT通道来校准VDDA答案是可以。STM32F103内部有一个内置参考电压约1.2V可以通过对应的通道采集。但这个参考电压精度不算高数据手册给的是1.16V到1.24V范围也就是带 ±3%用它校准VDDA能做粗略基准但达不到精密电压测量要求的精度。如果项目真需要高精度电压采集还是建议外置基准。另一个问题在数据采集项目中更常见怎么把波形数据导入电脑分析比如在WaveVersion等工具里画ADC采集到的数据。简单可靠的办法是把ADC原始值以文本格式通过串口发送一帧一个采样值然后导入到电脑端的串口绘图软件或数据分析工具中处理。注意设置一致的行分隔符和波特率否则数据错位。如果要分析高频信号直接拷贝串口输出的文本效率太低建议开DMA环形缓冲区批量打包后用二进制传输。6. 根据我个人经验做个收尾这篇内容本来打算写到电阻选型就收住但实在觉得对新手来说后面的实测数据和排查思路才是真正会节省他们几天时间的东西。我想用自己踩坑换来的经验做个结尾。分压电阻这件事看起来是元器件选型里最不起眼的一环实际却决定了整个ADC采集系统的精度上限。我现在的习惯不管项目多急都会先算清楚分压比、等效源阻抗、功耗预算再确定电阻精度和温漂指标最后在打样阶段用万用表把实际阻值测出来写进校准参数。这一套动作做完后面再出电压读数不准的概率几乎为零。如果你正在做基于STM32的电压采集不管是毕业设计、智能家居项目还是工业设备监控都建议先拿一块板子做最小验证搭电阻、量VDDA、调采样时间、看ADC原始值。把流程跑通后再考虑滤波、隔离、校准这些进阶内容。最后再分享一个小技巧调试时在代码里保留一个“原始ADC值”的调试输出这比直接看换算后的电压更能帮你判断问题出在硬件还是软件我靠这个习惯少走了很多弯路。
返回列表