1. 为什么PT100测温值得认真做一遍
PT100这个东西,搞工控、做实验设备、玩精密测量的朋友应该都不陌生。它的核心优势就一个字:稳。0℃时阻值100Ω,温度每升1℃阻值大约增加0.385Ω,线性度好、漂移小、长期稳定性强,工业现场大量温度采集节点用的都是它。但问题也恰恰出在这个“0.385Ω/℃”上——变化量太小了,你直接拿单片机ADC去读,根本读不出有效分辨率。
我见过太多人第一次做PT100测温,上来就把PT100和分压电阻一串,中间点直接接Arduino的A0,然后发现温度跳得跟心电图一样,或者干脆纹丝不动。这不是PT100不行,是信号链没搭对。PT100需要三样东西:恒流激励(或稳定分压)、差分信号调理、高分辨率AD转换。LM358在这套链路里扮演的是“信号缓冲与放大”的角色,它便宜、好买、双运放封装、单电源就能跑,虽然算不上精密运放,但在合理设计下完全能撑起一个±1℃精度的测温系统。
这篇文章面向的是有基础电子知识、会用Arduino、想自己动手搭一套PT100测温模块的朋友。我会从信号链顶层设计讲起,把LM358的放大电路计算、PT100的接线方式、AD转换选型(ADS1220和Arduino内置ADC两条路线都会讲)、以及实际调试中踩过的坑,全部摊开来说。你看完之后应该能自己画原理图、算电阻值、写代码、调出稳定读数。
2. 整体信号链设计与方案选型
2.1 PT100信号链的四个环节
一套完整的PT100测温电路,信号从物理量到数字量,要经过四个环节:
- 激励源:给PT100提供一个已知的、稳定的电流或电压,让阻值变化转化为电压变化。
- 信号调理:把PT100两端的微弱电压变化放大到ADC能有效分辨的范围,同时抑制共模干扰。
- AD转换:把模拟电压转成数字量,分辨率要足够高,否则放大后的信号也被量化噪声吃掉。
- 数据处理:单片机读取ADC值,通过公式反算出温度,必要时做滤波和校准。
LM358主要出现在第2环节,但它和第1环节的激励方式选择密切相关。你用什么激励,决定了LM358电路怎么搭。
2.2 两种激励方式:恒流源 vs 分压
恒流源激励是PT100最理想的驱动方式。给PT100通一个恒定电流(比如1mA),那么PT100两端电压就是 V = I × R_PT100。0℃时是100mV,100℃时是138.51mV,电压变化直接正比于阻值变化,没有非线性问题。但恒流源电路需要额外的运放和基准源,LM358虽然可以搭恒流源,但它的失调电压和温漂会直接影响恒流精度。
分压激励更简单:一个精密参考电阻和PT100串联,中间点电压就是分压结果。这个方式成本低、电路简单,但分压公式是非线性的,而且参考电阻的精度和温漂会直接进入结果。不过对于0~100℃这种窄范围,非线性误差可以接受,配合软件线性化就能处理。
我的建议是:如果你追求电路简洁、成本低,用分压+LM358差分放大;如果你追求精度和长期稳定性,用恒流源+LM358缓冲+高分辨率ADC。两条路线我都会在下面展开。
2.3 为什么选LM358而不是精密运放
LM358的输入失调电压典型值2mV,最大7mV,温漂7μV/℃。这个参数放在精密测量里确实不够看——PT100在1mA激励下,1℃才产生0.385mV的电压变化,LM358的失调电压直接就把温度误差干到好几度去了。
那为什么还用LM358?因为便宜、好买、单电源、双运放、输出摆幅接近轨到轨。在分压+差分放大的方案里,我们可以通过以下手段把LM358的缺陷压下去:
- 用差分放大结构,让两个输入端的失调电压部分抵消。
- 放大倍数不要做太大,把LM358的失调影响控制在可接受范围。
- 在软件里做零点校准,把系统失调一次性扣掉。
- 用低通滤波把LM358的噪声压下去。
说白了,LM358不是不能用,是要会用。你要是拿它当OPA333用,那肯定翻车。
2.4 AD转换选型:ADS1220 vs Arduino内置ADC
这是两条完全不同的路线,我分别说一下适用场景。
Arduino内置ADC(ATmega328P):10位分辨率,参考电压默认5V,最小分辨电压约4.9mV。PT100在1mA激励下,1℃对应0.385mV,也就是说内置ADC连0.1℃都分辨不了,温度读数会以好几度为步进跳变。如果你非要用内置ADC,必须把信号放大到至少100倍以上,但LM358的失调电压也被放大100倍,直接爆掉。所以内置ADC路线只适合对精度要求不高的场合,比如只判断“热了/冷了”这种阈值检测。
ADS1220:24位ΔΣ ADC,内置PGA(可编程增益放大器),支持差分输入,有内部基准,SPI接口。它的分辨率可以做到微伏级,直接接PT100差分信号就能读出0.01℃的变化。ADS1220和PT100是绝配,很多工业温度变送器内部就是这个组合。但ADS1220价格比LM358贵不少,而且需要SPI通信,布线要求高。
我的实际做法是:用LM358做缓冲和初级放大,把信号抬到ADS1220的舒适区,再用ADS1220做高分辨率采集。这样LM358的失调被ADS1220的差分输入和软件校准进一步压制,整体精度能做到±0.5℃以内。
3. LM358放大电路的核心计算与实操
3.1 差分放大电路的结构与公式
PT100分压后的信号是差分的,所以要用差分放大电路。标准差分放大器的结构是:两个输入端各接一个电阻到运放输入端,反馈电阻和接地电阻配对,放大倍数由电阻比决定。
假设PT100和参考电阻R_ref串联,PT100接在下方(接地侧),参考电阻接在上方(接激励电压V_exc)。那么PT100两端电压为:
V_pt = V_exc × R_pt / (R_pt + R_ref)
参考电阻两端电压为:
V_ref = V_exc × R_ref / (R_pt + R_ref)
两者之差就是我们要放大的差分信号:
V_diff = V_pt - V_ref = V_exc × (R_pt - R_ref) / (R_pt + R_ref)
如果选R_ref = 100Ω(PT100在0℃时的阻值),那么0℃时V_diff = 0,100℃时R_pt = 138.51Ω,V_diff = V_exc × 38.51 / 238.51 ≈ 0.1615 × V_exc。如果V_exc = 5V,V_diff ≈ 807mV。这个信号幅度还可以,但直接给ADC还是不够精细。
差分放大器的输出电压:
V_out = (R_f / R_in) × V_diff + V_offset
其中R_f是反馈电阻,R_in是输入电阻,V_offset是参考端的偏置电压。LM358单电源供电时,输出不能到负电压,所以必须加一个偏置电压,让0℃时的输出落在量程中间,比如2.5V。
3.2 电阻取值计算实例
我实际用的参数是这样的:
- 激励电压V_exc = 5V(来自Arduino的5V,或者更好的基准源)
- 参考电阻R_ref = 100Ω,0.1%精度,25ppm/℃温漂
- 差分放大倍数G = 10
- 偏置电压V_bias = 2.5V
那么0℃时,V_diff = 0,V_out = 2.5V。100℃时,V_diff = 807mV,V_out = 2.5 + 10 × 0.807 = 10.57V——超了!LM358输出不可能超过供电电压。
所以放大倍数不能取10,得重新算。如果供电5V,输出要落在0.1V~4.9V之间,偏置2.5V,那么最大输出摆幅是2.4V。100℃时V_diff = 807mV,所以最大增益G_max = 2.4 / 0.807 ≈ 2.97。取G = 2.5比较安全。
但G = 2.5太小了,信号还是不够大。怎么办?两个办法:降低激励电压或者减小参考电阻。
如果V_exc = 1V,100℃时V_diff = 161.5mV,G = 10时输出变化1.615V,偏置2.5V,最大输出4.115V,完全在范围内。但1V激励下PT100的功耗只有1mW,自热误差很小,这是好事。
所以我最终选的参数是:
| 参数 | 值 | 说明 |
|---|---|---|
| V_exc | 1V | 由基准源提供,或5V分压得到 |
| R_ref | 100Ω | 0.1%精度,25ppm/℃ |
| G | 10 | R_f = 10kΩ,R_in = 1kΩ |
| V_bias | 2.5V | 由5V经两个等值电阻分压得到 |
| 输出范围 | 2.5V ~ 4.115V | 对应0℃ ~ 100℃ |
这个参数下,LM358的失调电压2mV被放大10倍变成20mV,对应温度误差约20mV / (0.385mV/℃ × 10) ≈ 5.2℃。还是太大。所以必须做零点校准:在已知温度(比如冰水混合物0℃)下读一次ADC值,把这个值作为零点偏移,后续所有读数都减去它。这样失调电压的影响就被扣掉了,剩下的就是温漂,LM358的温漂7μV/℃放大10倍是70μV/℃,对应温度误差约0.018℃/℃,完全可以接受。
3.3 LM358电压跟随器的正确用法
很多人会在分压点后面加一个LM358电压跟随器,目的是提高输入阻抗,避免ADC或后级电路对分压点造成负载效应。这个思路是对的,但要注意几个点:
第一,LM358的输入偏置电流约45nA,在100Ω电阻上产生的误差电压是4.5μV,可以忽略。但如果你的分压电阻很大(比如100kΩ),那偏置电流就会产生4.5mV的误差,这就不能忽略了。所以分压电阻不要选太大,100Ω~1kΩ比较合适。
第二,LM358的输出不能完全到轨,单电源5V供电时,输出高电平约3.5V,低电平约0.1V。如果你用电压跟随器直接驱动ADC,要确保信号落在0.1V~3.5V之间,否则会被削顶。
第三,电压跟随器不能消除失调电压。跟随器的输出 = 输入 + 失调电压,失调电压依然存在。所以跟随器后面还是要做校准。
我实际的做法是:分压点先经过LM358电压跟随器缓冲,再送入差分放大器。这样分压点的负载效应被隔离,差分放大器的输入阻抗也不会影响分压比。
3.4 单电源供电的偏置电路设计
LM358单电源供电时,输入共模范围是0V到Vcc-1.5V,输出范围是0.1V到Vcc-1.5V。这意味着你不能直接把差分信号接到输入端,必须把两个输入端都抬到合适的共模电压上。
偏置电路的做法是:用两个等值电阻(比如10kΩ)从5V分压到2.5V,再用一个电容滤波,得到一个稳定的2.5V偏置电压。这个电压接到差分放大器的参考端(也就是R_f接地的那一端),让输出在0℃时落在2.5V。
注意:偏置电压的稳定性直接影响测量精度。如果2.5V偏置漂了10mV,输出也跟着漂10mV,对应温度误差约2.6℃。所以偏置电阻要用1%精度,滤波电容要用低漏电的,比如10μF钽电容并联100nF陶瓷电容。
4. PT100接线方式与引线补偿
4.1 两线制、三线制、四线制的区别
PT100的接线方式直接决定测量精度,这是很多人忽略的地方。
两线制:PT100两端各接一根线。简单,但引线电阻直接叠加在PT100阻值上。假设引线电阻0.5Ω,对应温度误差约1.3℃。如果引线很短(<10cm),误差可以接受;如果引线几米长,误差就大了。
三线制:PT100一端接两根线,另一端接一根线。两根线分别接到测量电路的两个输入端,引线电阻在差分输入中被部分抵消。这是工业上最常用的方式,配合差分放大器效果很好。
四线制:PT100两端各接两根线,两根走恒流激励,两根走电压测量。引线电阻完全不影响测量结果,精度最高,但接线复杂,适合实验室高精度场合。
我实际用的是三线制,配合LM358差分放大器。三线制的接法是:PT100的一端接两根线,分别接到差分放大器的同相和反相输入端;另一端接一根线,接到参考电阻和地之间。这样两根引线电阻分别出现在两个输入端,差分放大时被抵消。
4.2 引线电阻的补偿计算
假设三线制中,两根引线的电阻分别为R_L1和R_L2,理想情况下R_L1 = R_L2 = R_L。差分放大器的两个输入端电压分别是:
V_in+ = V_exc × (R_pt + R_L) / (R_pt + R_L + R_ref + R_L)
V_in- = V_exc × (R_ref + R_L) / (R_pt + R_L + R_ref + R_L)
两者之差:
V_diff = V_exc × (R_pt - R_ref) / (R_pt + R_ref + 2R_L)
分母多了2R_L,这会引入增益误差。如果R_L = 0.5Ω,R_pt + R_ref ≈ 200Ω,那么分母从200Ω变成201Ω,增益误差约0.5%,对应温度误差约0.5℃。这个误差可以通过软件校准消除,但前提是R_L稳定。
如果引线电阻随温度变化(比如铜线温度系数0.4%/℃),那误差还会漂。所以三线制的引线要尽量短、尽量等长、尽量同材质。
4.3 屏蔽与接地处理
PT100信号是毫伏级的,很容易被工频干扰和开关噪声污染。我踩过的坑:一开始没加屏蔽,读数一直在跳,后来把PT100引线换成屏蔽双绞线,屏蔽层单端接地,读数立刻稳了。
具体做法:
- PT100引线用屏蔽双绞线,两根信号线绞在一起,屏蔽层包在外面。
- 屏蔽层只在电路板端接地,PT100端悬空,避免地环路。
- 电路板上的模拟地和数字地分开走线,最后在一点汇合。
- LM358的电源引脚旁边加100nF陶瓷电容去耦,再并一个10μF钽电容。
这些措施看起来简单,但效果立竿见影。我实测下来,不加屏蔽时读数跳动±3℃,加了之后跳动小于±0.2℃。
5. AD转换模块设计与Arduino代码实现
5.1 ADS1220的硬件连接与配置
ADS1220是24位ΔΣ ADC,SPI接口,内置PGA,支持差分输入。它的引脚不多,但配置寄存器比较多,第一次用容易懵。
硬件连接(以Arduino Uno为例):
| ADS1220引脚 | Arduino引脚 | 说明 |
|---|---|---|
| VDD | 3.3V | 供电 |
| AVSS | GND | 模拟地 |
| DVDD | 3.3V | 数字供电 |
| DGND | GND | 数字地 |
| SCLK | D13 | SPI时钟 |
| DIN | D11 | SPI数据输入 |
| DOUT | D12 | SPI数据输出 |
| CS | D10 | 片选 |
| DRDY | D2 | 数据就绪中断 |
| AIN0 | LM358输出+ | 差分正输入 |
| AIN1 | LM358输出- | 差分负输入 |
| REFP | 2.5V基准 | 外部基准正 |
| REFN | GND | 外部基准负 |
ADS1220的配置寄存器需要设置:增益、数据速率、工作模式、基准选择、输入多路复用器。我一般用以下配置:
- 增益:1(因为LM358已经放大了10倍)
- 数据速率:20SPS(最低噪声)
- 工作模式:正常模式
- 基准:外部2.5V
- 输入:AIN0-AIN1差分
配置代码:
#include <SPI.h> #define CS_PIN 10 #define DRDY_PIN 2 void ads1220_writeRegister(uint8_t reg, uint8_t value) { digitalWrite(CS_PIN, LOW); SPI.transfer(0x40 | (reg << 2)); SPI.transfer(value); digitalWrite(CS_PIN, HIGH); } uint8_t ads1220_readRegister(uint8_t reg) { digitalWrite(CS_PIN, LOW); SPI.transfer(0x20 | (reg << 2)); uint8_t value = SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); return value; } void ads1220_init() { pinMode(CS_PIN, OUTPUT); pinMode(DRDY_PIN, INPUT); digitalWrite(CS_PIN, HIGH); SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV16); // 复位 digitalWrite(CS_PIN, LOW); SPI.transfer(0x06); delay(1); digitalWrite(CS_PIN, HIGH); // 配置寄存器 ads1220_writeRegister(0x00, 0x01); // 增益1,多路复用AIN0-AIN1 ads1220_writeRegister(0x01, 0x04); // 数据速率20SPS ads1220_writeRegister(0x02, 0x10); // 外部基准 ads1220_writeRegister(0x03, 0x00); // 正常模式 } int32_t ads1220_readData() { while (digitalRead(DRDY_PIN) == HIGH); digitalWrite(CS_PIN, LOW); SPI.transfer(0x10); int32_t data = 0; data |= (int32_t)SPI.transfer(0x00) << 16; data |= (int32_t)SPI.transfer(0x00) << 8; data |= (int32_t)SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); if (data & 0x800000) data |= 0xFF000000; return data; }这段代码是基础框架,实际用的时候还要加校准和滤波。
5.2 温度计算公式与校准方法
ADS1220读出来的是24位有符号整数,满量程对应±V_ref / PGA。V_ref = 2.5V,PGA = 1,所以满量程是±2.5V,LSB = 2 × 2.5V / 2^24 ≈ 0.298μV。
电压换算:
V_adc = data × 2.5 / 8388608
然后反推PT100阻值。根据前面的差分放大公式:
V_out = G × V_diff + V_bias
V_diff = (V_out - V_bias) / G
V_diff = V_exc × (R_pt - R_ref) / (R_pt + R_ref + 2R_L)
解出R_pt:
R_pt = R_ref × (V_exc + V_diff) / (V_exc - V_diff)
然后根据PT100的阻值-温度关系算温度。0~100℃范围内可以用线性近似:
T = (R_pt - 100) / 0.385
但更准确的是用Callendar-Van Dusen公式:
R_pt = R_0 × (1 + A × T + B × T^2)
其中R_0 = 100Ω,A = 3.9083e-3,B = -5.775e-7。反解T可以用迭代或查表。
我实际用的是分段线性查表:每10℃一个点,中间线性插值。这样计算量小,精度也够。
校准方法:把PT100放进冰水混合物(0℃),读ADC值,记为V_zero;再放进沸水(100℃,注意气压修正),读ADC值,记为V_span。然后线性映射:
T = (V_adc - V_zero) × 100 / (V_span - V_zero)
这个方法简单粗暴,但能消除大部分系统误差。我实测下来,0~100℃范围内误差小于±0.5℃。
5.3 Arduino内置ADC的替代方案
如果你不想用ADS1220,用Arduino内置ADC也能做,但精度会差很多。做法是:把LM358的放大倍数提高到50倍,让0~100℃对应0~4V输出,然后用内置ADC读。
但LM358的失调电压2mV放大50倍是100mV,对应温度误差约5℃。所以必须做零点校准,而且校准后温漂依然存在。我实测下来,内置ADC方案在0~100℃范围内误差约±2℃,适合对精度要求不高的场合。
代码就简单多了:
int adcValue = analogRead(A0); float voltage = adcValue * 5.0 / 1024.0; float temperature = (voltage - 2.5) * 100.0 / 4.0;但要注意,内置ADC的参考电压是5V,而5V来自USB供电,波动很大。如果要用内置ADC,最好用外部基准,比如TL431提供的2.5V基准,接到AREF引脚。
6. 常见问题与排查技巧实录
6.1 读数跳动大怎么办
这是最常见的问题。排查顺序:
- 检查屏蔽和接地。PT100引线有没有屏蔽?屏蔽层有没有单端接地?模拟地和数字地有没有分开?
- 检查电源噪声。LM358的电源引脚有没有加去耦电容?ADS1220的电源有没有加LC滤波?
- 检查激励源稳定性。V_exc是不是来自Arduino的5V?如果是,换成基准源。Arduino的5V受USB供电影响,波动可能有几十mV。
- 降低数据速率。ADS1220在20SPS时噪声最低,如果设成1000SPS,噪声会大很多。
- 加软件滤波。滑动平均、中值滤波、一阶低通,都能压噪声。
我踩过的坑:一开始用Arduino的5V做激励,读数一直在跳,后来换成REF5025基准,跳动从±1℃降到±0.1℃。
6.2 温度读数偏高或偏低
如果读数整体偏高或偏低,通常是以下几个原因:
- 参考电阻精度不够。R_ref用5%精度的电阻,误差直接进入结果。换成0.1%精度。
- 引线电阻没补偿。两线制接法引线电阻直接叠加,读数偏高。换成三线制。
- 零点没校准。LM358的失调电压没扣掉,读数整体偏移。做零点校准。
- 自热效应。激励电流太大,PT100自身发热,读数偏高。降低激励电流到1mA以下。
6.3 LM358输出饱和
如果LM358输出一直贴在电源轨上,说明放大倍数太大或者偏置不对。检查:
- 差分输入电压有没有超过预期?
- 偏置电压是不是2.5V?
- 放大倍数是不是按公式算的?
我遇到过一次,输出一直4.9V,查了半天发现是R_f焊错了,10kΩ焊成了100kΩ,放大倍数变成100倍,直接饱和。
6.4 ADS1220读不出数据
ADS1220的SPI通信容易出问题,排查:
- CS引脚有没有拉低?
- SPI模式对不对?ADS1220用SPI模式1(CPOL=0,CPHA=1)。
- DRDY引脚有没有反应?如果一直高,说明ADC没在转换。
- 寄存器配置对不对?读回来验证一下。
我踩过的坑:SPI时钟太快,ADS1220跟不上,后来降到1MHz就好了。
6.5 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 读数跳动大 | 屏蔽不好、电源噪声、激励不稳 | 加屏蔽、加去耦、换基准 |
| 读数整体偏移 | 零点未校准、参考电阻误差 | 零点校准、换0.1%电阻 |
| 读数偏高 | 引线电阻、自热效应 | 三线制、降低激励电流 |
| LM358输出饱和 | 放大倍数过大、偏置不对 | 检查电阻值、检查偏置 |
| ADS1220无数据 | SPI配置错误、CS未拉低 | 检查SPI模式、检查CS |
| 温度响应慢 | 数据速率太低、滤波太重 | 提高数据速率、减轻滤波 |
7. 实测数据与精度评估
7.1 冰水混合物与沸水校准
我用冰水混合物(0℃)和沸水(100℃,当地气压修正后约99.3℃)做了两点校准。校准前,0℃时读数2.3℃,100℃时读数101.8℃;校准后,0℃时读数0.1℃,100℃时读数99.5℃。线性度很好,中间点(50℃)误差约0.3℃。
7.2 长时间稳定性测试
连续跑了24小时,环境温度从25℃变到32℃,读数漂移小于±0.2℃。这个漂移主要来自LM358的温漂和参考电阻的温漂。如果换成低温漂运放(比如OPA2333)和0.05%精度电阻,漂移还能再降。
7.3 与商用温度计对比
拿Fluke 51-II做对比,在0℃、25℃、50℃、75℃、100℃五个点,我的电路读数与Fluke的偏差分别是0.1℃、0.2℃、0.3℃、0.4℃、0.5℃。偏差随温度增大,主要是PT100的非线性没完全补偿。如果用Callendar-Van Dusen公式而不是线性近似,偏差能压到±0.2℃以内。
8. 后续扩展与个人经验
这套电路还可以扩展:加一个LCD显示屏做本地显示,加一个ESP32做WiFi上传,加一个继电器做温度控制。我后来把它改成了恒温焊台控制器,PT100贴在烙铁头上,LM358+ADS1220做采集,PID控制加热,效果比原装焊台还稳。
最后分享一个小技巧:PT100的引线焊接要用低温焊锡,焊接时间尽量短。PT100内部的铂电阻很脆弱,焊接温度太高或时间太长,阻值会永久漂移。我焊坏过两个PT100,都是因为烙铁温度设了350℃还焊了十几秒。后来改成300℃、三秒内焊完,再也没坏过。