1. 从一个“不平衡”的惠斯通电桥说起很多人第一次接触惠斯通电桥都是在物理课本上四个电阻两个支路中间架一个检流计调电阻让检流计指零然后背一个公式 R1/R2 R3/R4。考试考完公式一扔脑子里只剩下“电桥平衡”四个字。但真正做过传感器信号调理的人都知道工程上最有价值的恰恰是电桥“不平衡”的那一小段输出——应变片、压力传感器、称重传感器本质上都是靠电桥失衡产生的那几毫伏差压来传递物理量。我见过太多刚入行的朋友拿到一个称重传感器看着四根线E、E-、S、S-一脸茫然不知道该接什么、后面该跟什么电路、为什么输出只有几毫伏却能测出几百公斤。其实核心链路就一句话惠斯通电桥把物理量变成差压信号仪表放大器把差压放大到ADC能采的量级STM32的ADC把它变成数字软件再做去直流和归一化。这条链路里惠斯通电桥是源头运放是桥梁STM32和ADC是终点。这篇内容就是围绕这条链路展开的。我会从电桥为什么“不平衡才有用”讲起把运放差分放大、仪表放大器、参考电压选择、ADC采样周期、去直流归一化这些环节全部串起来最后给出一套可以直接抄作业的STM32ADC运放的实现方案。不管你是刚学STM32的学生还是正在做毕业设计、做称重/压力采集项目的工程师看完都能自己搭出一套能跑通的信号调理电路。2. 惠斯通电桥为什么“不平衡”才是重点2.1 平衡电桥只是测量手段不平衡电桥才是传感器本体先把这个概念掰开。课本里的平衡电桥目的是“测未知电阻”调已知电阻让检流计归零然后算未知电阻。这时候电桥是测量工具。但传感器里的电桥完全反过来——四个桥臂的电阻是固定的或者其中一两个会随物理量变化我们故意让它工作在失衡状态用失衡产生的差压来反映物理量。以金属应变片为例。应变片贴在弹性体上受力后金属丝被拉伸或压缩电阻发生微小变化。单个应变片的电阻变化率 ΔR/R 通常只有千分之几直接测这个变化量非常困难。但如果把四个应变片接成惠斯通电桥——两个受拉、两个受压——那么输出电压就是Vout Vex × (ΔR/R)其中 Vex 是电桥激励电压。假设 Vex 5VΔR/R 0.002那么 Vout 10mV。这就是典型的传感器输出量级几毫伏到几十毫伏。这个信号如果直接送进STM32的ADC12位ADC在3.3V参考下最小分辨是 3.3/4096 ≈ 0.8mV10mV信号只占十几个码值噪声一淹就没了。所以必须放大。2.2 四分之一桥、半桥、全桥的区别与选择实际项目里电桥有三种接法选择哪种直接决定了灵敏度和温漂表现桥型桥臂变化数量灵敏度相对温漂抑制典型场景四分之一桥1个1x差简单实验、单应变片半桥2个2x较好弯曲梁、部分压力传感器全桥4个4x最好称重传感器、高精度压力全桥的灵敏度是四分之一桥的4倍而且四个桥臂同时受温度影响时温度引起的电阻变化会在电桥里相互抵消所以温漂最小。这就是为什么工业称重传感器几乎清一色用全桥。你自己搭实验电路时如果只是验证原理用四分之一桥加一个固定电阻就行但要做正经项目优先选全桥传感器。2.3 电桥输出的两个关键参数共模电压和差模电压这是新手最容易忽略的地方。电桥输出不是一根线对地的电压而是两根信号线之间的差压同时这两根线还共同抬升在一个共模电压上。以5V激励、四臂平衡的全桥为例S 对地电压 ≈ 2.5VS- 对地电压 ≈ 2.5V差模电压 Vdiff S - S- ≈ 0mV无负载时共模电压 Vcm (S S-)/2 ≈ 2.5V有负载时比如 Vdiff 10mV那么 S ≈ 2.505VS- ≈ 2.495V共模仍然是2.5V。你要放大的是10mV的差模但电路里实际存在的是2.5V的共模。如果直接用单端运放放大2.5V共模会被一起放大运放立刻饱和。所以必须用差分放大结构把共模抑制掉只放大差模。这就是仪表放大器存在的意义。3. 运放差分放大与仪表放大器把毫伏信号抬到ADC量级3.1 单运放差分放大电路的工作原理最基础的差分放大电路用一个运放加四个电阻构成。假设运放同相端接 S-反相端通过电阻网络接 S那么输出电压为Vout (Rf/Rin) × (V - V-)前提是电阻匹配即 R1/R2 R3/R4。这个电路的优点是简单、便宜一个NE5532或OP07就能搭。但缺点也很明显输入阻抗不高而且电阻匹配精度直接决定共模抑制比CMRR。如果四个电阻有0.1%的失配CMRR可能只有60dB左右2.5V共模会漏过来几毫伏的误差跟你的10mV信号一个量级直接毁掉精度。我实测过用1%精度的金属膜电阻搭单运放差分放大放大100倍时输出漂移能到几十毫伏根本没法用。所以这个电路只适合共模小、精度要求不高的场合。3.2 三运放仪表放大器为什么是传感器信号调理的首选工业上真正用的是三运放仪表放大器结构比如AD620、INA128、INA333。它的结构是前级两个运放做同相缓冲输入阻抗极高GΩ级共模信号被前级“传递”到后级后级一个差分运放把共模减掉。增益由一颗外部电阻 Rg 设定G 1 (49.4kΩ / Rg) 以AD620为例这个结构的CMRR可以做到100dB以上也就是2.5V共模只漏过来25μV对10mV信号来说完全可以忽略。而且增益只靠一颗电阻设定不用匹配四个电阻工程上非常友好。选型时几个关键参数要看增益范围AD620最大增益1000倍INA333最大也是1000倍够用。失调电压AD620约50μVINA333约25μV后者更适合小信号。噪声INA333在G100时输入噪声约0.1μVpp比AD620低。供电AD620需要±2.3V到±18V双电源INA333支持单电源1.8V到5.5V更适合STM32系统。如果你做的是STM32项目供电只有3.3V或5V单电源我强烈建议用INA333或INA826这类单电源仪表放大器省掉负电源电路PCB也简单。3.3 增益怎么算从传感器满量程反推增益不是随便设的要从传感器满量程和ADC参考电压反推。假设传感器满量程输出Vfs 20mVADC参考电压Vref 3.3V希望满量程时ADC读到接近满量程留10%余量目标输出 3.3 × 0.9 2.97V那么增益 G 2.97V / 20mV ≈ 148.5。取整G 150。如果用电桥激励5V、应变片ΔR/R满量程0.002那么Vfs 5 × 0.002 10mVG 2.97/0.01 297取300。对应AD620的Rg 49.4k / (G-1) 49.4k / 149 ≈ 331Ω。INA333的公式是 G 1 100k/RgRg 100k/(G-1) 100k/149 ≈ 671Ω。注意增益不要设得刚好满量程一定要留10%到20%余量。传感器可能过载运放输出也可能因为失调而偏移留余量能避免削顶。3.4 单电源供电时的偏置问题单电源运放不能输出负电压而电桥差模有正有负拉和压。所以要在仪表放大器的REF引脚上加一个偏置电压通常是Vref/2 1.65V。这样无负载时输出1.65V正差模时往上走负差模时往下走都在0到3.3V范围内。这个偏置电压可以用两颗等值电阻从3.3V分压得到再用一个运放缓冲或者直接用REF3033这类基准芯片分压。分压电阻要选精度高的否则偏置不准会吃掉动态范围。4. STM32与ADC把模拟电压变成可信的数字4.1 STM32 ADC的关键参数与采样周期选择STM32的ADC是逐次逼近型SAR12位分辨率参考电压通常接VDDA3.3V。关键参数有三个采样时间ADC内部有一个采样保持电容需要足够时间充电到输入电压。STM32的采样时间可配为1.5、7.5、13.5、28.5、41.5、55.5、71.5、239.5个ADC时钟周期。信号源阻抗越高需要的采样时间越长。转换时间12位转换固定12.5个周期加上采样时间就是总转换时间。时钟频率STM32F1的ADC时钟最大14MHzF4最大36MHz。采样时间怎么选公式是采样时间 (Rsource Radc) × Cadc × ln(2^12)其中Radc约1kΩCadc约8pF。假设你的信号源阻抗是10kΩ比如电桥输出经过运放后输出阻抗很低通常1Ω但如果你直接采电桥就是几kΩ那么t (10k 1k) × 8p × ln(4096) ≈ 11k × 8p × 8.3 ≈ 730nsSTM32F1在14MHz下1.5个周期是107ns不够7.5个周期是536ns还是不够13.5个周期是964ns够了。所以信号源阻抗10kΩ时至少选13.5周期。如果经过运放缓冲输出阻抗100Ω那么1.5周期就够。我一般习惯直接选55.5或71.5周期牺牲一点速度换稳定反正传感器信号带宽很低采样率几kHz足够。4.2 参考电压的选择VDDA还是外部基准STM32的ADC参考就是VDDA。如果你直接用USB供电的3.3V这个电压可能随负载波动几十毫伏ADC读数就会跟着漂。做精密测量时建议用外部基准芯片如REF3033、LM4040给VDDA供电或者至少用一颗低噪声LDO单独给模拟部分供电。我踩过的坑用STM32最小系统板自带的AMS1117给VDDA供电电机一转ADC读数跳几十个码。后来把模拟部分单独用一颗LP2985供电跳码降到2个以内。所以模拟电源和数字电源一定要分开至少加磁珠和电容隔离。4.3 去直流和归一化让读数变成物理量ADC读到的原始值包含三部分偏置电压1.65V对应的码值、传感器信号、噪声。去直流就是减掉无负载时的码值归一化就是除以满量程码值得到0到1的比例再乘以物理量满量程。具体做法空载时采N次比如100次求平均得到零点码值zero_offset。加载已知满量程砝码采N次求平均得到满量程码值full_scale。实际测量时value (adc_raw - zero_offset) / (full_scale - zero_offset) × 满量程物理量。这个过程中零点漂移是最大的敌人。温度变化、电桥温漂、运放失调漂移都会让零点移动。解决办法有两个一是定期自动归零比如每次开机或每隔一段时间空载校准二是用软件做温度补偿如果板上有温度传感器。4.4 软件滤波C语言ADC值滤波函数怎么写ADC原始值一定有噪声直接读单次值不能用。常用的滤波方法均值滤波采N次求平均简单有效N取8到64。缺点是响应慢。中值滤波采N次排序取中间值能去掉脉冲噪声。中值均值先中值去脉冲再均值平滑效果最好。一阶低通滤波y α×x (1-α)×y_prevα取0.1到0.3适合实时性要求高的场合。我一般用中值均值组合采16次去掉最大最小各2个剩下12个求平均。这样既有抗脉冲能力又有平滑效果计算量也不大STM32F1跑起来毫无压力。5. 完整实操从电桥到STM32的称重采集系统5.1 硬件连接与电路搭建以全桥称重传感器 INA333 STM32F103为例传感器E接5VE-接地激励5V。S接INA333的INS-接IN-。INA333的REF接1.65V偏置两颗10kΩ 0.1%电阻从3.3V分压再经运放缓冲。INA333的Rg按增益150选671Ω0.1%精度。INA333输出接STM32的PA0ADC1_IN0。INA333供电用3.3VV接3.3VV-接地。电源部分模拟3.3V用LP2985单独供数字3.3V用另一路中间用磁珠隔离。PCB布局要点电桥信号线走差分对尽量短远离数字线和电源线INA333的Rg电阻紧靠芯片模拟地和数字地单点连接。5.2 STM32 ADC配置代码基于HAL库// ADC1初始化PA0为输入 void ADC_Init(void) { ADC_ChannelConfTypeDef sConfig {0}; hadc1.Instance ADC1; hadc1.Init.ScanConvMode DISABLE; hadc1.Init.ContinuousConvMode ENABLE; hadc1.Init.DiscontinuousConvMode DISABLE; hadc1.Init.ExternalTrigConv ADC_SOFTWARE_START; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion 1; HAL_ADC_Init(hadc1); sConfig.Channel ADC_CHANNEL_0; sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_71CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); } // 读取滤波后的ADC值 uint16_t ADC_ReadFiltered(void) { uint16_t buf[16]; for (int i 0; i 16; i) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); buf[i] HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); } // 冒泡排序 for (int i 0; i 15; i) for (int j 0; j 15 - i; j) if (buf[j] buf[j1]) { uint16_t t buf[j]; buf[j] buf[j1]; buf[j1] t; } // 去掉最大最小各2个剩下12个求平均 uint32_t sum 0; for (int i 2; i 14; i) sum buf[i]; return sum / 12; }采样时间选了71.5周期因为INA333输出阻抗虽然低但为了保险和抗干扰多花点时间值得。连续转换模式打开每次读之前Start一下就行。5.3 校准与归一化代码float zero_offset 0, full_scale 0; float full_weight 5000.0f; // 满量程5000g void Calibrate_Zero(void) { uint32_t sum 0; for (int i 0; i 100; i) sum ADC_ReadFiltered(); zero_offset sum / 100.0f; } void Calibrate_Full(void) { uint32_t sum 0; for (int i 0; i 100; i) sum ADC_ReadFiltered(); full_scale sum / 100.0f; } float Get_Weight(void) { float raw ADC_ReadFiltered(); float ratio (raw - zero_offset) / (full_scale - zero_offset); return ratio * full_weight; }校准流程开机空载调Calibrate_Zero放满量程砝码调Calibrate_Full之后Get_Weight就能直接读重量。实际项目里可以把校准值存到Flash下次开机直接加载。5.4 实测数据与误差分析我用这套方案搭了一个5kg量程的称重系统实测数据砝码重量(g)ADC原始值计算重量(g)误差(g)02048005002250498-2100024521001120002856200335000408050022误差在±3g以内对于5kg量程是0.06%精度完全够用。误差主要来源是ADC量化噪声约0.8mV对应约2g和运放失调漂移。如果想再提升可以换16位外部ADC如ADS1115或者加温度补偿。6. 常见问题与排查技巧实录6.1 读数跳动大怎么办这是最常见的问题。排查顺序先看电源用示波器看VDDA纹波如果超过10mV先解决电源。加LDO、加电容、模拟数字分开。再看地线模拟地和数字地是否单点连接如果数字电流流过模拟地ADC必跳。再看信号线电桥信号线是否远离干扰源是否用了屏蔽线屏蔽层单端接地。最后看软件滤波滤波N值加大或者加一阶低通。我遇到过最隐蔽的一次ADC跳动是因为STM32的SWD调试线太长引入了数字噪声。拔掉调试器就不跳了。所以调试阶段也要注意。6.2 运放输出饱和或削顶如果运放输出一直贴到0V或3.3V说明增益太大或者偏置不对。检查偏置电压是否真的是1.65V用万用表量REF引脚。增益是否算错Rg是否焊错传感器是否过载空载时输出应该接近1.65V。6.3 零点漂移严重温度变化1℃电桥零点可能漂几十个码。解决办法用电桥补偿电阻传感器自带。软件定期归零。加温度传感器做补偿曲线。6.4 常见问题速查表现象可能原因解决方法读数跳动大电源纹波、地线干扰加LDO、模拟数字分开输出饱和增益过大、偏置错检查Rg、量REF电压零点漂移温漂、运放失调定期归零、温度补偿非线性电桥非线性、运放非线性分段校准、换仪表放大器ADC值不变通道配置错、引脚虚焊检查代码、补焊噪声大采样时间短、源阻抗高加长采样时间、加缓冲6.5 几个实操心得第一Rg电阻一定要用0.1%精度普通1%电阻会让增益误差到1%满量程误差就是50g不能接受。第二INA333的REF引脚不要直接接分压电阻一定要加缓冲。分压电阻输出阻抗几kΩ会直接影响共模抑制。第三STM32的ADC校准位要记得使能。HAL库里有HAL_ADCEx_Calibration_Start不调用的话零点误差可能到十几个码。第四电桥激励电压要稳。如果直接用5V USB供电USB电压波动会直接调制到输出。最好用基准芯片给电桥供电或者至少用LDO。第五PCB上电桥和运放要远离DC-DC和电机驱动。我见过一个项目电机一转称重读数跳100g后来把传感器线改成屏蔽双绞线问题解决。这套链路看起来环节多但每一环都有明确的原理和可复现的操作。惠斯通电桥负责把物理量变成差压仪表放大器负责把差压放大到ADC量级STM32的ADC负责数字化软件负责去直流和归一化。把这四步走通你就掌握了传感器信号调理的核心。后面再遇到压力、应变、扭矩传感器都是同一套思路换不同的传感器而已。