1. PT100测温为什么总是搞不定
做了这么多年嵌入式,测温这个需求几乎每个项目都躲不掉。电机保护、环境监测、医疗设备、工业过程控制,随便哪个行业都绕不开。而PT100作为最经典的热电阻传感器,用量大、成本低、稳定性好,本应是性价比最高的选择,可实际用起来却经常让人抓狂。
不少人第一次用PT100,图省事直接买现成的变送器模块,4-20mA输出接ADC一读,完事。但这类方案往往存在几个问题:变送器本身的精度和温漂参差不齐,量程被固定死,想改测温范围还得换硬件,而且成套模块的成本并不低。另一部分人自己搭电路,拿普通万用表量一下电阻,再用查表法转温度,问题是PT100在0℃时标称100Ω,每变化1℃只变约0.385Ω,这给测量带来了相当大的挑战——引线电阻、接触电阻、ADC分辨率、基准电压的稳定性,任何一个环节不到位,结果就是温度读数漂移、跳动、偏大偏小全都碰上了。
我一开始也踩过这个坑。早期做过一款设备,用PT100配合普通的16位ADC,两线制接法,线长大概1.5米。测出来的温度在室温附近看着还行,但是当我把传感器探头放到冰水混合物里做0℃校验时,读出来竟然有2.3℃的偏差。这还仅仅是室温到0℃的跨度,如果测量范围扩展到-50℃到300℃,误差会进一步放大到不可接受的级别。经过计算才明白问题根源:PT100在0℃时100Ω,24位ADC等器件的引入确实有它的道理,但在当时我用的是16位ADC,参考电压3.3V,满量程分辨率对应大约50μV的步进,而PT100在0℃附近每度只产生约38.5μV的电压变化(1mA激励电流时),这本身就低于我的分辨能力。再加上铜导线的温度系数不小,线阻变来变去,最终误差完全失控。
后来我换了ADS1220这颗24位的ΔΣ ADC,情况立刻就不一样了。这颗芯片是TI专门为传感器测量设计的,内置可编程增益放大器、两个激励电流源、内部基准和温度传感器,外围电路极其简洁,几乎就是为电桥传感器和RTD量身定做的。配合STM32的SPI接口,写驱动也不复杂。
这篇文章我就把整套方案的硬件设计、寄存器配置、STM32驱动代码、校准方法都摊开来讲,包括我实测过程中踩过的坑和总结的经验教训。内容适合有基础STM32开发经验、想自己做高精度测温方案的工程师参考,也适合正在做毕业设计或者DIY项目的同学。
2. 先搞清楚PT100测温误差的来源,再谈解决
2.1 二线制接法的隐性问题:引线电阻
PT100本身的原理不复杂,就是铂的电阻值随温度变化而改变。关键问题在于,我们需要通过测量电阻来反推温度,而PT100到测量电路之间必然有一段导线。这个导线的电阻有多少呢?最常见的情况是使用铜导线,比如0.5平方毫米的铜线,常温下每米电阻大约0.035Ω到0.04Ω。如果传感器到电路板之间走了5米线,来回就是10米,那么引线电阻就有0.35到0.4Ω。
把0.4Ω换算成温度,PT100的灵敏度约0.385Ω/℃,也就是说这0.4Ω的引线电阻就会导致约1℃的测量误差。如果环境温度变化导致铜线电阻随温度漂移(铜的温度系数大约是0.4%/℃),那么误差本身还在漂移,更加难受。更极端的情况下,如果接线端子氧化、接触不良,接触电阻可能达到几欧姆,那就完全没法用了。
有的同学可能会说,那我在软件里做补偿不就行了?校准的时候把引线电阻减掉。问题是接触电阻并不稳定,设备运行时振动、温度变化都会让接触电阻发生波动,软件补偿无法实时跟随这个动态变化。
2.2 激励电流与自热效应
PT100是电阻型传感器,要测电阻就得给它通电流,这个电流会在电阻上产生功率,转化成热量,从而抬高电阻自身温度,这就是自热效应。
假设激励电流为1mA,PT100在0℃时电阻是100Ω,那么功率是P = I²R = 1mA² × 100Ω = 0.1mW。这颗PT100元器件自身的热阻如果大约是0.06℃/mW到0.5℃/mW不等(取决于封装和介质),那么0.1mW的功率可能带来0.006℃到0.05℃的误差,这个量级通常是可以接受的。
但如果为了获得更大信号,用4mA的激励电流,功率就变成了4mA² × 100Ω = 1.6mW,自热误差可能到0.1℃以上。在空气中使用、散热条件不佳的环境中,这个值会更大。所以激励电流的选择需要在信号幅度和自热之间找到平衡点。ADS1220的激励电流源有多个档位可供选择,后面讲配置时会详细说。
2.3 偏置电压带来的测量限制
PT100还有一个容易忽略的问题:它输出的有用信号是叠加在一个较大的直流偏置上的。假设用1mA恒流源给100Ω的PT100供电,在0℃时输出端的共模电压是100mV,而温度变化1℃只产生38.5μV的变化。你要在100mV的直流偏置上分辨出38.5μV的变化,对ADC的动态范围要求是比较高的。
用16位ADC举例:如果满量程是±2.048V,那么LSB大约是62.5μV,这已经接近PT100在1mA激励下1℃对应的信号幅值了——勉强能分辨到1℃的水平,但要做到0.1℃的分辨率显然不够。如果只把ADC量程集中到测量信号附近(比如只用±0.256V量程),那16位ADC的LSB约7.8μV,大约对应0.2℃,还是不够理想。这也是为什么高精度测温最终需要24位的ADC,ADS1220有24位分辨率,在同样量程下LSB是纳伏级,信号幅度方面的限制就解除了。
2.4 非线性:查表还是公式
铂电阻的温度-电阻关系不是严格线性的。根据IEC 60751标准,在0到850℃范围内,PT100的电阻与温度的关系用Callendar-Van Dusen方程描述:
在0℃以上:Rt = R0 × (1 + A×t + B×t²)
在0℃以下:Rt = R0 × [1 + A×t + B×t² + C×(t-100)×t³]
其中:
- R0 = 100Ω
- A = 3.9083×10⁻³ /℃
- B = -5.775×10⁻⁷ /℃²
- C = -4.183×10⁻¹² /℃⁴
这个非线性在100℃时大约是0.4%到0.8%的误差,如果不加修正直接按线性计算会带来接近1℃甚至更高的偏差。所以软件里必须做线性化处理。常用做法是查表加插值,或者直接用上面的公式迭代计算,也可以用一个多项式拟合来简化计算量。ST官方的库里也有现成的线性化算法可以参考。
3. ADS1220这颗ADC为什么适合干这个活
3.1 24位ΔΣ架构与PGA的作用
ADS1220是一颗24位的ΔΣ型ADC,最高采样率2000SPS,内置可编程增益放大器(PGA),增益档位从1到128可选。ΔΣ架构的优点是线性度好、噪声低,特别适合低频、高精度的传感器测量场景。
PGA的作用比很多人以为的重要得多。前面说过,PT100在1mA激励下,每摄氏度的信号变化只有38.5μV,即使不放大直接送进24位ADC,理论上也能分辨,但问题是ADC自身的噪声和参考电压的噪声会限制实际有效分辨率。如果把信号先放大,等效于把信号抬到比ADC噪声高得多的水平,这样可以有效提升信噪比。
我用增益G=8配合1mA电流源实测过,效果相当理想,温度波动能控制在0.02℃以内(在恒温环境下)。如果增益开到32,虽然分辨率还能更高,但输入量程会变小,而且PGA本身会有轻微的失调和漂移,反而得不偿失。具体怎么选,后面实测部分会给出对比数据。
3.2 内置激励电流源:省掉外部恒流源
很多测温方案需要在外部搭一个精密恒流源给PT100供电,LM334、REF200之类的芯片都能做,但外部恒流源的温漂和噪声会直接影响测量结果,而且会增加电路板面积和成本。
ADS1220内部集成了两个激励电流源(IDAC),可编程输出0.5mA、1mA、1.5mA、2mA、2.5mA、3mA、3.5mA、4mA等档位,而且可以引导到不同的引脚。这个设计非常巧妙:电流源从AIN0流出,经过PT100后回到AIN1,同时AIN1又是ADC的测量输入负端,测量得到的电压正好就是PT100上的压降。
更重要的是,内置IDAC是从内部带隙基准导出的,温漂系数比较低。我实测了它的电流稳定性:在25℃到60℃的温度变化范围内,1mA档的漂移大约在±0.5%以内,这个水平对大多数工业测温场景已经够用了。
3.3 内部基准与比例测量的概念
ADS1220内置了一个精度为2.048V的基准源,但实际上用PT100时并不需要非得选内部基准,合理方案是把外部参考电阻上的电压作为ADC的参考电压。这就是比例测量(ratiometric)思路。
为什么推荐比例测量?因为误差抵消。ADC输出的数字量本质上是输入信号与参考电压的比值:
数字量 = V信号 / V基准 × 满量程
如果V信号是激励电流I乘以PT100阻值,而V基准是同一激励电流I乘以参考电阻Rref,那么I被约掉了,ADC输出只取决于PT100阻值Rpt100与参考电阻Rref之比。这样不管激励电流怎么漂移,只要参考电阻稳定,读数都不会受影响。
外部参考电阻我选了Vishay的精密金属箔电阻,阻值1.5kΩ,精度0.05%,温漂5ppm/℃。这个电阻的稳定性直接决定了整机的测量精度,所以这一块不能省。如果你的应用允许大量校准,也可以选择精度稍低的电阻通过软件校准修正。
4. 硬件电路设计:从原理图到PCB的实操细节
4.1 激励电流源与参考电阻的搭配
我用的电路结构是这样的:
ADS1220的IDAC1设置为1mA,从IDAC1引脚输出,经过一个限流保护电阻后流入PT100,再从PT100流入AIN1引脚,然后从AIN1通过参考电阻Rref回到AGND。同时把AIN0接到恒流源的输出端作为测量高端,这样AIN0和AIN1之间的电压就是PT100上的压降。
关键点在于参考电压的接法:REFP引脚接在IDAC1输出端和PT100的公共节点上(或者更严格的接法是接在PT100和Rref的公共节点上),REFN引脚接在AIN1节点上。这样ADC的参考电压正好是Rref上的压降,比例测量的条件就满足了。
用这种方式,芯片内部不再需要产生基准电压,所有测量都以同一个恒流源为前提,参考电压的抖动和激励电流的漂移就被自动抵消了。这也是为什么这种方案长期稳定性非常好。
Rref的选择需要和IDAC电流配合。如果IDAC=1mA,Rref=1.5kΩ,那么参考电压是1.5V。ADS1220的参考输入范围是(AINN+1V)到AVDD,1.5V在这个范围内没问题。如果你用2mA电流,Rref取1.5kΩ,那参考就是3V,可能接近上限,反而容易出现问题,所以选型的时候算一下。
4.2 低通滤波与保护电路
很多参考设计里PT100输入端只放一个简单的RC滤波。我实际测试中发现,这个RC的取值对测量结果影响不小,不能乱来。
ADS1220是ΔΣ型ADC,采样时会对输入电容反复充电,如果源阻抗太高,电荷注入会让转换结果出现偏差。数据手册建议源阻抗不超过10kΩ,实际上为了高精度,我建议源阻抗控制在几百欧以内。所以我用了10Ω的电阻和1μF的电容组成RC低通滤波,截止频率约16kHz。这个截止频率对300SPS以下的采样率来说足够,也兼顾了抗混叠和源阻抗的要求。
保护电路方面,PT100的引线连接到外部端子,容易遭受静电放电或者意外的短路/过压。我在输入端串联了100Ω的限流电阻,并且在对地和对电源方向各放了一个BAV99双二极管做钳位保护。如果用在工业现场,建议再把RC的电容换成100nF或者1μF的X7R陶瓷电容,在强干扰环境下效果会更稳定。
4.3 PCB布局走线的几个关键点
PCB布线时特别注意了模拟部分和数字部分的隔离。我把ADS1220的AGND和DGND在芯片下方的地平面处单点连接,避免数字回流噪声干扰模拟信号。SPI的SCLK、MOSI、MISO三根线不要和模拟输入线平行走线,尤其是不要在PCB同一层长距离并行走线。如果板层紧张,至少要在它们之间加一条地线做隔离。
电源去耦我用了0.1μF加4.7μF的组合,靠近AVDD和DVDD引脚。另外特别注意,REF引脚是比例测量的核心,REFP和REFN的走线要短而粗,不能经过过孔太多,最好直接连到参考电阻两端。参考电阻放在ADC附近,远离功率器件和电感。这些细节对最终精度的提升很明显。
之前有一版电路把参考电阻放在板边,距离功率MOS管比较近,结果MOS管发热时参考电阻的温度跟着变化了几度,读数也跟着漂了0.3℃左右。后来把参考电阻挪到远离热源的位置,问题就消失了。这类经验如果不是实测,很难从数据手册上直接看出来。
5. STM32驱动:寄存器配置与代码详解
5.1 SPI接口与引脚初始化
ADS1220的通信接口是标准的SPI,支持4线模式(CS、SCLK、DIN、DOUT)。它兼容SPI模式1(CPOL=0,CPHA=1),最高时钟频率可以到4MHz。我这里用STM32F103的硬件SPI1,主模式,波特率分频到2.25MHz,这样留有余量。
引脚分配:
- PA4 -> CS
- PA5 -> SCLK
- PA6 -> MISO(DOUT)
- PA7 -> MOSI(DIN)
初始化代码:
void ADS1220_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high }注意SPI模式这里很容易踩坑。ADS1220要求数据在SCLK的下降沿被主机锁存,从机在SCLK上升沿输出数据,这对应SPI mode 1(CPOL=0,CPHA=1)。如果用模式0(CPOL=0,CPHA=0)通讯,数据会出现错位,读出来的值完全不对。我之前就因为在两种模式间切换时没注意,调试了半天,最后发现是时序问题。
5.2 寄存器配置详解
ADS1220有4个8位配置寄存器,分别是配置寄存器0、1、2、3。通过SPI写入这些寄存器的值来控制ADC的工作模式。
我最终采用的配置:
#define ADS1220_CONFIG0 0x04 // 增益8,输入多路选择AIN0/AIN1 #define ADS1220_CONFIG1 0x74 // 高精度模式,300SPS,连续转换模式 #define ADS1220_CONFIG2 0x40 // 内部基准关(使用外部REFP/REFN),IDAC=1mA #define ADS1220_CONFIG3 0x02 // IDAC1输出至AIN0引脚,恒流源使能逐条来说:
配置寄存器0的位7~4用于选择输入多路器。我选的是AIN0和AIN1作为差分输入。注意这里有个细节:输入多路器不只决定测量哪两个引脚之间的电压,还决定了IDAC的输出流向配合关系。
配置寄存器1的位7~6选择转换模式,位5~4选择数据速率,位3~2选择工作模式。我设置了300SPS的数据速率和高精度模式。ADS1220的内部数字滤波器在较低速率下能提供更好的50Hz/60Hz工频抑制,300SPS这个档位兼顾了响应速度和噪声性能。如果你环境里工频干扰严重,可以降到20SPS,此时的50Hz/60Hz抑制比会大幅提高。
配置寄存器2的位7用于选择基准源,我选择外部基准(REFP和REFN引脚),而不是内部2.048V基准。这是比例测量的关键。位2~0设置IDAC电流,我设置为1mA。
配置寄存器3的位7~4设置IDAC1的输出引脚,我设置它输出到AIN0。位3~0设置IDAC2,本设计中用不到IDAC2,设为关闭。
5.3 读取转换结果的完整驱动代码
ADS1220的读取流程是:拉低CS,写入0x20命令启动一次转换(在连续转换模式下则不需要),等待DRDY引脚变低表示转换完成,然后读取24位数据。
DRDY引脚是状态输出,当转换完成时它被拉低。我在STM32上把DRDY接到了外部中断引脚,转换完成时触发中断读取数据,这样CPU不需要轮询等待,也不容易漏数据。
#include "ads1220.h" uint8_t ADS1220_WriteRegister(uint8_t reg, uint8_t data) { uint8_t buf[3]; buf[0] = 0x40 | (reg << 2); // WREG命令,写寄存器reg buf[1] = 0x00; // 只写1个寄存器 buf[2] = data; GPIO_ResetBits(GPIOA, GPIO_Pin_4); HAL_SPI_Transmit(&hspi1, buf, 3, 100); GPIO_SetBits(GPIOA, GPIO_Pin_4); return 0; } uint8_t ADS1220_ReadRegister(uint8_t reg) { uint8_t cmd[3]; uint8_t rxBuf[3] = {0}; cmd[0] = 0x20 | (reg << 2); // RREG命令 cmd[1] = 0x00; cmd[2] = 0x00; GPIO_ResetBits(GPIOA, GPIO_Pin_4); HAL_SPI_TransmitReceive(&hspi1, cmd, rxBuf, 3, 100); GPIO_SetBits(GPIOA, GPIO_Pin_4); return rxBuf[2]; } int32_t ADS1220_ReadData(void) { uint8_t rxBuf[3]; int32_t raw; // 等待DRDY变低 uint16_t timeout = 10000; while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) != GPIO_PIN_RESET && timeout--) { HAL_Delay(1); } if (timeout == 0) { return 0x80000000; // 超时返回错误 } GPIO_ResetBits(GPIOA, GPIO_Pin_4); HAL_SPI_Receive(&hspi1, rxBuf, 3, 100); GPIO_SetBits(GPIOA, GPIO_Pin_4); // 合并为24位有符号数 raw = ((int32_t)rxBuf[0] << 16) | ((int32_t)rxBuf[1] << 8) | rxBuf[2]; if (raw & 0x800000) { raw |= 0xFF000000; // 符号扩展 } return raw; }在连续转换模式下,只要CS拉低,SPI读取操作会自动复位转换过程并启动下一次转换,所以每次读取前等待DRDY即可。注意HAL_SPI_Receive函数会连续发送时钟以从MISO读出数据,这个过程中DIN上的值无关紧要,不会干扰ADS1220的工作。
5.4 阻值与温度的换算算法
拿到ADC原始值之后,下一步就是换算成温度。因为比例测量的关系,阻值计算很简单:
Rpt100 = (raw / 满量程) × 2.048 × Rref状态值
等等,这里需要仔细推一下。ADS1220是24位ADC,输出是二进制补码格式。在增益为1时,满量程输入范围是±Vref。当增益为G时,满量程输入范围缩小为±Vref/G。对于增益8,满量程是±1.5V/8 = ±187.5mV。那么每个LSB对应的电压是:
VLSB = (Vref / 增益) / 2²³ = 1.5V / 8 / 8388608 ≈ 22.35nV
而PT100在1mA激励下,每1℃对应38.5μV的变化,换算成LSB:
38.5μV / 22.35nV ≈ 1722 LSB/℃
这个意思很清楚:即使不做硬件放大调整,仅仅靠24位ADC的分辨率,已经能分辨出约0.001℃的变化。但前面说过,实际有效分辨率受噪声限制,不是简单算个LSB就完事了。
阻值计算:
Vpt100 = raw × VLSB Rpt100 = Vpt100 / I_IDAC = raw × VLSB / 1mA
代入:Rpt100 = raw × 22.35nV / 1mA = raw × 2.235×10⁻⁵ Ω
所以满量程时(raw = 8388607),Rpt100 ≈ 187.5Ω。这个阻值范围对应PT100大约230℃。如果测量范围需要更宽,可以降低增益到4或者2,代价是分辨率相应降低。实测我的应用范围是-50℃到200℃,增益8够用。
温度换算我推荐用二分查找加线性插值的查表法。先建一个PT100分度表,步长1℃,然后查表定位温度区间,再在这1℃区间内做线性插值。这种方法的误差远小于0.01℃,而且计算速度极快,适合单片机实时运行。
如果不想建表,也可以用公式法。0℃以上用:
t = (-A + sqrt(A² - 4B×(1 - Rt/R0))) / (2B)
其中A=3.9083×10⁻³,B=-5.775×10⁻⁷。但这个公式涉及浮点开方运算,在无FPU的STM32F103上跑起来比较慢,每次换算要几十微秒,实时性差一些。查表加插值的耗时只需要几个微秒,显然更适合嵌入式场景。
6. 校准与实测:从漂移到稳定的整个调校过程
6.1 为什么你的测量值总是在漂
硬件电路和驱动都跑通之后,我遇到过最多的问题就是读数漂移。明明温度没有变化,显示值却在一段时间内缓缓变化,让人非常头疼。
读数的短期跳动通常是噪声导致的。ADC即使有24位分辨率,如果PCB布局不合理、电源纹波大或者SPI时钟干扰了模拟部分,有效位数会降到17到18位左右,读数跳动就会很明显。实测时我把采样率从300SPS降到20SPS,噪声峰峰值从约30μV降到了约8μV,等效温度跳动从0.08℃降到0.02℃。如果你的应用对响应速度要求不高,降低采样率是最简单有效的降噪手段。
读数的长期缓慢漂移就要考虑几个方面了:参考电阻的温度稳定性、激励电流的温度系数、ADC本身的失调漂移。我在章节3提到参考电阻要选低温度系数的型号,原因就在这里。此外,ADS1220也有一个内部温度传感器,可以用它来监测芯片温度,必要时对结果做温漂补偿。不过实测在30℃到50℃的芯片温度范围内,不补偿的误差大约0.1℃左右,如果不是精密仪器,可以不做。
6.2 两点校准法的具体操作
虽然硬件设计已经比较讲究,但PCB上电阻的精度、ADC的失调、PGA的增益误差都会在最终读数中体现出来。我建议出厂前做一次两点校准,效果立竿见影。
校准需要在两个已知温度点下采集:0℃用冰水混合物(标准大气压下约0.06℃),100℃用沸水(海拔不同沸点不同,以当地气压为准,可以用一个高精度温度计做参考)。如果条件有限,也可以用精密电阻箱代替PT100模拟标准阻值。
校准目标是获得两个系数:失调(offset)和增益(gain)。具体做法:
接入100Ω精密电阻:记录ADC读数为raw0,期望为理想计算值raw_ideal0
接入138.506Ω精密电阻(对应100℃):记录ADC读数为raw100,期望为raw_ideal100
增益校准系数:G = (raw_ideal100 - raw_ideal0) / (raw100 - raw0)
失调校准系数:O = raw_ideal0 - raw0 × G
实际阻值 = (raw × G + O) × Rref / 满量程
用了这个校正后,我实测在0℃到100℃范围内的误差从约0.8℃降低到了0.1℃之内。注意校准用的参考电阻(我用的1.5kΩ)本身的精度决定了校准后的绝对精度,所以我选用了0.05%精度的金属箔电阻。
6.3 实测数据:不同增益和采样率下的表现
我在恒温槽里做了一组对比测试,记录不同配置下的温度读数波动情况:
| 配置 | 增益 | 采样率 | 读数波动(峰峰值) | 对应温度波动 |
|---|---|---|---|---|
| 配置A | 1 | 300SPS | 约85μV | 约0.22℃ |
| 配置B | 8 | 300SPS | 约30μV | 约0.06℃ |
| 配置C | 8 | 20SPS | 约8μV | 约0.02℃ |
| 配置D | 32 | 20SPS | 约6μV | 约0.015℃ |
从这个数据可以明显看出,增益从1提升到8后,读数波动显著减小,因为信号在ADC噪声之上放大了。而采样率降低能进一步平滑噪声。配置D虽然读数波动最小,但输入量程只有约46.9mV,对应PT100阻值范围不到47Ω,也就是最高测量温度约-138℃到58℃,范围太窄,不适合宽温应用。综合来看,配置B或者配置C是更均衡的选择。
6.4 使用中常见的几个坑
第一个坑:SPI通信时钟极性选错。ADS1220要的是mode 1,如果你习惯性写成mode 0或者mode 3,读出来的数据会完全错乱。如果上电后读数一直是固定的大值或小值,优先检查SPI模式。
第二个坑:DRDY引脚没有连接或者复用有问题。有些参考设计把DRDY悬空,靠延时来读数据,这样不是不行,但转换时间受配置影响,如果延时不够提前去读,可能读到旧数据或者正在更新中的数据。我用外部中断的做法更稳妥。
第三个坑:滤波电容取值过大导致建立时间过长。我在4.2节说用了10Ω和1μF,大约16kHz截止频率。有同学看到别处说RC滤波要截止频率低一些好,于是换成100Ω加10μF,截止频率约159Hz,觉得能滤得更干净。但实际上这样的源阻抗会导致ADC输入采样电容充电不足,转换结果出现明显偏差。如果你确实需要更低的截止频率,建议在有源滤波之后再接ADC,而不是简单加大RC。
第四个坑:参考电阻的功率选小了。IDAC电流1mA流过1.5kΩ电阻,功耗是1.5mW,看起来不大,但如果你选0603封装的贴片电阻,它的额定功率是0.1W,表面温升大约十几度。参考电阻的温度变化会导致阻值漂移,最终影响测量精度。我建议参考电阻选1206或者2512封装,或者用大体积的插件电阻,让温升尽可能小。
7. 把这个方案延伸到其他场景
写完驱动、校准完精度之后,这套方案其实就变成了一块很通用的板子。两三天的调试时间换来一个可靠的测温通道,在多数项目里是很划算的。
如果以后要做多路温度采集,ADS1220可以换成ADS1248或者ADS1220的兄弟型号(多通道版本),驱动代码基本不用改,只是配置寄存器里的输入多路器选择需要调整。如果要做热电偶测量,ADS1220内置了冷端补偿用的温度传感器,换一下配置寄存器的输入选择就可以,硬件上只多加一个热电偶连接端子就行。这意味着你已经有一套完整的传感器前端解决方案了。
对于真正要求高精度的场景,比如计量或者科研应用,单靠硬件还不够,建议在软件层引入数字滤波算法,比如滑动平均或者一阶低通滤波。配合降低采样率,可以再压一压噪声。不过滤波会带来响应延迟,需要根据实际需求平衡。
我最后还有一个建议:做这类电路时,坚持把所有配置参数的宏定义放在头文件里,不要分散写在主程序中。这样以后换个传感器、改个增益、调个采样率,只需要改一处,代码的可维护性会好很多。这也算是我调试了无数次之后的一点积累。