去年做一套工业循环水温度采集客户验收标准很直接0~120℃范围内显示偏差不能超过±0.2℃。刚开始我用PT100加一片24位ADC再配一个自制的恒流源板子画完调了一周室温下稳定的时候看着还挺准可温度一上去误差跟着飘。最后查清楚恒流源温漂、导线电阻、PCB漏电流一起发力单靠提高ADC位数根本没戏。后来换成TI的ADS1220搭配STM32重做信号链两天就把精度压到了±0.1℃以内。这篇就把整个方案掰开揉碎讲一遍包括为什么PT100测温容易翻车、ADS1220哪些资源正好打在痛点上、硬件怎么搭、寄存器怎么配、代码怎么写、校准怎么做以及我实测时踩过的几个坑。适合正在用PT100做产品、或者毕业设计想选温度采集方向的同学参考全套思路和代码可以直接抄。1. 为什么PT100测温总翻车传统方案差在哪1.1 PT100测温的本质是电阻测量不是电压测量PT100是铂热电阻0℃时阻值正好100Ω温度每升高1℃阻值增加约0.385Ω。所以测温的核心动作其实是在线测一个电阻的当前阻值再把它换算成温度。这个换算关系看起来简单但真要达到0.1℃精度得能分辨0.0385Ω的电阻变化。在100Ω的基值上这个相对分辨率大约是385ppm换算成ADC位数至少要摸到12位以上并且信号链上任何一点误差都会把这0.1℃直接吃掉。很多人在这一步就理解偏了以为换个更高位数的ADC就能解决问题。实际上PT100测温的误差大头从来不在ADC位数而在信号链的其他环节。1.2 恒流源、导线电阻、基准漂移三道坎我最早用的方案是典型的“恒流源仪表放大器ADC”自己用运放搭一个250µA恒流源激励PT100产生毫伏级电压再送进ADC。这套方案有三道坎绕不过去第一恒流源的稳定性。250µA电流只要有0.1%的波动在100Ω电阻上就会产生25µV的电压变化等效温度误差接近0.07℃。而自制的镜像恒流源温漂能做到0.1%以内已经很不错了再加上运放失调电压随温度漂移整个激励源本身就是最大的误差源。第二导线电阻。PT100探头到电路板之间只要拖上两三米线每根导线的电阻就有0.2~0.5Ω。用两线制接法时导线电阻直接串联进测量回路0.5Ω换算成温度就是1.3℃还没算探头本身的误差这已经远超很多项目的验收指标了。第三ADC基准的温漂。ADC测量的是电压而电压值要还原成电阻必须除一个精确的电流值。如果用外部基准芯片给ADC提供参考电压基准自身的温漂又会引入第二重误差。说白了整个测量精度取决于电流源、基准源、放大器三者的综合稳定性想做好得花大力气。这还只是常温下的情况一旦环境温度变化误差会进一步放大。所以后来我理解了一件事PT100高精度测温本质上不是选一个高分辨率ADC而是设计一个“自校准”的信号链让电流源和基准的误差在数学上互相抵消。ADS1220正是奔着这个需求来的。2. ADS1220凭什么能解决一颗芯片扛下所有2.1 芯片资源盘点PT100需要的功能它都内置了ADS1220是TI出的24位ΔΣ ADC专门面向传感器测量场景。这颗芯片最值钱的地方是它把PT100测温需要的几个核心模块全部集成在了一起不需要再外搭恒流源、仪表放大器和基准源。具体来说它包含这些资源24位ΔΣ ADC内部集成PGA可编程增益放大器增益1~128倍两个可编程激励电流源IDAC1、IDAC2电流从10µA到1.5mA可选内部2.048V基准电压也支持外部基准输入20SPS到2000SPS的数据速率可配内置50Hz/60Hz工频干扰抑制的数字滤波器SPI接口通信简单内置温度传感器可以用来做冷端补偿或者自检。其中激励电流源是最关键的一项。它意味着我不用再自己搭恒流源也不用担心运放温漂。ADS1220内部的两个IDAC精度和一致性比绝大多数分离方案要好得多而且能和ADC的采样做到严格同步这是自己搭分立器件很难做到的。2.2 比例测量让电流源的误差彻底消失ADS1220做PT100测温时最核心的设计思路是比例测量。简单说就是让同一个激励电流I分别流过PT100和一个精密基准电阻RREF然后测量两个电压的比值。假设PT100当前阻值是R_RTD那么在它两端产生的电压就是V_RTD I × R_RTD。同时同一个电流流过RREF在基准电阻上产生V_REF I × RREF。ADC的转换结果本质上是这两个电压的比值raw (V_RTD × Gain) / V_REF × 2^23把V_RTD和V_REF的表达式代入电流I在分子和分母中直接约掉最终只剩下raw (R_RTD × Gain) / RREF × 2^23这意味着无论激励电流是250µA还是500µA无论它随温度漂了0.1%还是0.5%只要PT100和RREF是用同一个电流驱动的电流的绝对误差就不会进入最终结果。这个特性类比一下就是用同一把尺子量两个物体尺子本身长了短了都无所谓只要两段长度是同一个刻度体系下读出来的比值就是准的。这就是为什么ADS1220能做到高精度它不是把误差消除而是把误差设计成不参与运算。2.3 两线制、三线制、四线制怎么选比例测量解决了电流源误差但导线电阻还得单独处理。实际工程里PT100有三种接法。两线制最简单PT100一端接AIN0另一端接AIN1电流和测量共用一根导线。代价是导线电阻直接进入测量结果所以只适合探头到板子距离很短、导线电阻能校准掉的场景。三线制针对长线场景用两根等长导线做补偿。ADS1220有两个等幅IDAC电流源分别流经两根对称的导线利用差分测量把导线的压降抵消掉是工业现场最常用的折中方案。四线制是精度天花板两根线走激励电流两根线专门做电压测量测量线上几乎没有电流导线电阻对测量完全没有影响。代价是线缆成本高接线麻烦。我在自己的项目里最终选了四线制因为探头到主板的距离接近两米而且精度指标卡得紧。如果你只是近距离测个温度两线制加软件校准也完全够用。3. 硬件搭建元件选型和PCB布局的关键细节3.1 基准电阻RREF选型整个方案的精度天花板比例测量虽然让电流源退居二线但基准电阻RREF反而成了新的核心。因为最终测量公式里R_RTD和RREF直接成正比RREF的精度和温漂会原样搬进结果。这里我给出一组关键参数PT100在0℃时100Ω在100℃时约138.5Ω灵敏度约0.385Ω/℃。如果RREF用1kΩ、温漂25ppm/℃的普通精密电阻环境温度变化10℃时RREF变化约0.25Ω等效成PT100温度误差就有0.65℃左右。这显然不行。我最终选择的是2kΩ、0.1%精度、5ppm/℃温漂的低温漂电阻。5ppm/℃意味着温度每变化10℃RREF只漂移0.01%等效温度误差约0.026℃基本可以忽略。如果你对精度还有更高要求可以上0.5ppm的金属箔电阻不过性价比会下降很多。激励电流我选了250µA原因有两个。一是PT100在250µA下自热功率只有约6µW自热引起的温升几乎可以忽略二是250µA配合2kΩ基准电阻VREF正好是0.5V刚好满足ADS1220外部基准电压不低于0.5V的要求。3.2 输入滤波与基准去耦ADS1220是ΔΣ型ADC输入端要加RC低通滤波器做抗混叠和滤噪。我在AIN0和AIN1上各串了一个100Ω电阻对地各接一个1nF的C0G电容组成差分低通。截止频率大约1.6kHz既能滤掉高频干扰又不会明显衰减50Hz工频附近的信号。滤波电容一定要选C0G或NP0介质不能用X7R。X7R电容在直流偏置下容值会下降而且有压电效应用在这里容易引入额外噪声。基准输入REFP0和REFN0之间我并了一个100nF和1µF的电容。这两个电容一定要靠近芯片的基准引脚放置走线尽量短粗。因为基准电压是ADC转换时的参考任何叠加在VREF上的噪声都会直接进入测量结果比信号线上的噪声更致命。3.3 布局走线与电源处理PCB布局上有几个经验值得专门说。第一ADS1220下方要铺完整的地铜皮模拟地和数字地在芯片附近单点连接不要让数字电流穿过模拟区域。第二激励电流走线和信号采样走线要分得远一点。虽然激励电流只有250µA但它毕竟是开关电流如果和差分信号线平行走很长距离会通过寄生电容耦合噪声。第三模拟电源AVDD用单独的LDO供电不要直接用系统数字3.3V。实测下来用数字电源直接供电时读数跳动会比LDO供电大30%左右。而且LDO的输出端要加一个1µF和100nF的组合去耦电容靠近电源引脚放置。第四DRDY、SCLK这些数字信号线要远离REFP0/REFN0和AIN0/AIN1。数字翻转时的串扰会直接耦合进模拟输入。我见过很多人画板子时把ADC部分当成普通数字芯片处理结果调试时噪声大得离谱怎么滤波都压不下去最后只能改板。ADS1220的封装引脚间距不算密布局时多留点心思后面调试能省很多事。4. STM32驱动代码从寄存器配置到温度换算4.1 SPI初始化与基础读写函数ADS1220走SPI接口我用STM32的HAL库操作。这里有一点要特别注意ADS1220的SPI时序要求工作在模式1即CPOL0、CPHA1。如果照搬其他SPI器件的模式0大概率读回来的数据是乱的。SPI初始化关键代码void MX_SPI2_Init(void) { hspi2.Instance SPI2; hspi2.Init.Mode SPI_MODE_MASTER; hspi2.Init.Direction SPI_DIRECTION_2LINES; hspi2.Init.DataSize SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity SPI_POLARITY_LOW; hspi2.Init.CLKPhase SPI_PHASE_2EDGE; // CPOL0, CPHA1, SPI Mode 1 hspi2.Init.NSS SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; hspi2.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi2); }片选由普通GPIO控制每次通信前拉低通信结束后拉高。写寄存器函数static void ADS1220_WriteReg(uint8_t reg, uint8_t val) { uint8_t cmd 0x40 | (reg 2); // 写单寄存器命令 ADS_CS_LOW(); HAL_SPI_Transmit(hspi2, cmd, 1, 10); HAL_SPI_Transmit(hspi2, val, 1, 10); ADS_CS_HIGH(); }读寄存器函数static void ADS1220_ReadReg(uint8_t reg, uint8_t *val) { uint8_t cmd 0x20 | (reg 2); // 读单寄存器命令 uint8_t dummy 0; ADS_CS_LOW(); HAL_SPI_Transmit(hspi2, cmd, 1, 10); HAL_SPI_TransmitReceive(hspi2, dummy, val, 1, 10); ADS_CS_HIGH(); }还有一个发送单命令的函数用于复位、启动转换static void ADS1220_SendCmd(uint8_t cmd) { ADS_CS_LOW(); HAL_SPI_Transmit(hspi2, cmd, 1, 10); ADS_CS_HIGH(); }4.2 配置寄存器数值推演ADS1220有5个配置寄存器地址从0x00到0x04。我直接给出针对PT100的完整配置参数并解释每个值的来源。寄存器地址配置值含义寄存器00x000x06AIN0-AIN1差分输入PGA增益8倍寄存器10x010x11数据速率90SPS正常模式单次转换寄存器20x020x40IDAC电流250µA寄存器30x030x08IDAC1路由到AIN0IDAC2路由到AIN2寄存器40x040x40使用外部基准REFP0/REFN0逐个解释。寄存器0的0x06展开是二进制0b00000110其中MUX[2:0]000表示差分输入AINPAIN0、AINNAIN1PGA[2:0]011表示增益8倍。为什么要选8倍增益因为250µA激励电流在PT100上0~100℃范围内产生约25.0mV到34.6mV的电压而外部基准VREF0.5V8倍增益对应的满量程输入是500mV/862.5mV。34.6mV大约占满量程的55%这个比例既能充分利用ADC的动态范围又留出了超温余量。寄存器1的0x11展开是0b00010001DR[2:0]010对应90SPS数据速率MODE[1:0]00是正常模式最低位为1表示单次转换模式。单次转换的好处是每次启动后转换一次就停电磁干扰环境下的读数一致性更好也更省电。初始化函数void ADS1220_Init(void) { ADS1220_SendCmd(0x06); // Reset HAL_Delay(2); ADS1220_WriteReg(0x00, 0x06); // AIN0-AIN1差分PGA8 ADS1220_WriteReg(0x01, 0x11); // 90SPS单次转换 ADS1220_WriteReg(0x02, 0x40); // IDAC250µA ADS1220_WriteReg(0x03, 0x08); // IDAC1-AIN0, IDAC2-AIN2 ADS1220_WriteReg(0x04, 0x40); // 外部基准 REFP0/REFN0 }4.3 数据读取、数字滤波与温度计算启动一次单次转换然后等待DRDY引脚拉低此时转换完成可以读取24位数据static int32_t ADS1220_ReadData(void) { uint8_t buf[3]; while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) GPIO_PIN_SET); ADS_CS_LOW(); HAL_SPI_Receive(hspi2, buf, 3, 10); ADS_CS_HIGH(); int32_t raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (raw 0x800000) { raw | 0xFF000000; // 24位补码扩展为32位有符号数 } return raw; }DRDY引脚是转换完成标志下降沿有效。读取数据这个动作本身也会使DRDY恢复高电平所以不存在“读完还要清标志”的问题。读到的原始码值要换算成电阻值。比例测量的换算公式是R_RTD raw × RREF / (2^23 × Gain)其中2^238388607对应24位ADC的满量程码值RREF2000ΩGain8。换算代码如下#define RREF_OHM 2000.0 #define PGA_GAIN 8.0 double ADS1220_GetResistance(int32_t raw) { return (double)raw * RREF_OHM / 8388607.0 / PGA_GAIN; }这里我特意把RREF和Gain做成宏方便以后换元件或改配置。数字滤波我用的是“去极值滑动平均”连续采5个点去掉最大值和最小值剩下3个取平均。这个方法对脉冲干扰抑制效果好而且不会像纯滑动平均那样把真实温度变化拉得很钝。double ADS1220_GetFilteredResistance(void) { int32_t samples[5]; for (int i 0; i 5; i) { samples[i] ADS1220_StartAndRead(); HAL_Delay(15); // 90SPS单次转换周期约11.1ms留余量 } // 简单选择排序去掉最大最小 for (int i 0; i 4; i) { for (int j i 1; j 5; j) { if (samples[j] samples[i]) { int32_t tmp samples[i]; samples[i] samples[j]; samples[j] tmp; } } } int64_t sum samples[1] samples[2] samples[3]; return (double)sum * RREF_OHM / 8388607.0 / PGA_GAIN / 3.0; }温度换算我用查表加线性插值。先按每1℃间隔生成一张PT100电阻-温度对应表范围取-50℃到250℃。查表时先二分找到区间再线性插值。这个方法比用Callendar-Van Dusen公式算快也不需要引入浮点库之外的特殊函数。完整表格太长这里给出插值核心逻辑#define TEMP_TABLE_START -50.0 #define TEMP_TABLE_STEP 1.0 #define TEMP_TABLE_SIZE 301 double RTD_Table[TEMP_TABLE_SIZE]; // 工程中由上位机脚本生成 double ResistanceToTemp(double r) { if (r RTD_Table[0]) return TEMP_TABLE_START; if (r RTD_Table[TEMP_TABLE_SIZE - 1]) return TEMP_TABLE_START (TEMP_TABLE_SIZE - 1) * TEMP_TABLE_STEP; int lo 0, hi TEMP_TABLE_SIZE - 1; while (hi - lo 1) { int mid (lo hi) / 2; if (RTD_Table[mid] r) hi mid; else lo mid; } double t0 TEMP_TABLE_START lo * TEMP_TABLE_STEP; double r0 RTD_Table[lo], r1 RTD_Table[hi]; return t0 (r - r0) / (r1 - r0) * TEMP_TABLE_STEP; }RTD_Table数组可以在PC上生成好直接以const数组烧进单片机避免运行时计算查表的浮点开销。5. 校准怎么做从出厂值到±0.1℃5.1 为什么比例测量之后还要校准比例测量解决了电流源和基准源的误差但芯片本身的PGA失调电压、增益误差、以及PCB上的寄生参数仍然存在。这些误差的典型值是PGA失调电压在微伏到几十微伏量级增益误差在0.02%左右换算成温度大约是0.05~0.2℃。如果需要±0.1℃的指标就必须做系统级校准。5.2 用精密电阻代替温度源校准温度源非常麻烦冰水浴和沸水浴都受气压影响而且建立一个稳定的恒温槽成本很高。更实用的做法是用精密电阻代替PT100做校准因为ADS1220测的本来就是电阻只要把已知阻值的电阻接上去就能反推出整个信号链的误差。我准备了三只标准电阻0Ω、100.000Ω、138.505Ω。0Ω用来校准偏置100Ω和138.505Ω分别对应0℃和100℃的PT100标准阻值用来校准增益。校准步骤如下第一步把模拟输入端AIN0和AIN1用短线短接采样得到raw_offset。这个值就是整个信号链的固有偏置码值。第二步接上100.000Ω标准电阻采样得到raw_100。第三步接上138.505Ω标准电阻采样得到raw_138。然后计算实际增益系数K (138.505 - 100.000) / (raw_138 - raw_100)这个K就是“每个ADC码值对应的真实电阻变化量”它把PGA的增益误差全部吸收进去了。最终电阻计算公式变成R_RTD (raw - raw_offset) × K 100.000注意这里的K本质上是电阻增量与码值增量的比值已经包含了对PGA增益误差的修正所以不再显式除以Gain。5.3 校准参数固化与自检这三组校准数据算出来后我通常存在EEPROM或Flash的固定地址上电初始化时读出来加载进全局变量。每块板子都单独校准一遍不能批量拷贝因为每片ADS1220的失调和增益误差都不完全一样。校准完成后我还习惯做一次自检用一只已知阻值、但不参与校准的电阻比如120Ω测一遍。120Ω对应PT100大约51.3℃如果实测换算结果在119.98~120.02Ω范围内说明校准成功。一个小提示校准完成之后接探头的线缆长度和线型不要再换。校准只对当前信号通路有效换了线缆等于改变了导线电阻和寄生参数精度会明显退化。6. 实测结果与踩坑记录6.1 稳定性实测数据系统调试完成后我用一个恒温槽把探头分别稳定在0℃、25℃、50℃、100℃四个点每个点连续采集10分钟记录换算后的温度。数据如下恒温槽参考温度实测平均温度最大波动幅度偏差0.00℃-0.02℃±0.02℃-0.02℃25.00℃25.01℃±0.03℃0.01℃50.00℃50.00℃±0.03℃0.00℃100.00℃100.03℃±0.03℃0.03℃这个结果是在90SPS数据速率、去极值滑动平均滤波之后得到的。如果把数据速率降到20SPS波动还能进一步压低但响应速度会变慢。对于大多数工业测温场景90SPS加滤波已经是比较平衡的选择。6.2 三个高频问题及排查手段调试过程中我遇到过三个典型问题每个都花了些时间定位写出来供参考。第一个问题是DRDY引脚一直不拉低。排查方向按顺序查先量SPI的CLK和CS波形确认通信时序正确再确认初始化流程里确实发送过START命令最后检查SPI模式是不是Mode 1。我遇到的情况就是SPI模式配成了Mode 0DRDY始终无响应改回来立刻恢复正常。第二个问题是读数周期性跳动波动幅度达到十几个码值。这是电源和布局问题。用示波器看AVDD引脚发现有幅值约30mV的纹波频率和板上的DC-DC开关频率一致。后来把ADS1220的AVDD供电从数字电源改成独立LDO纹波降到2mV以内读数波动立刻缩小到3个码值以内。如果你的布局空间实在紧张至少在AVDD引脚前加一个磁珠加电容的π型滤波。第三个问题是温度整体偏高。校准之后仍然偏高0.4℃左右很稳定。排查到最后发现是四线制接线时激励线的接触电阻不完全对称而且校准用的标准电阻接线端子有接触电阻。解决方法是把校准用的端子改成开尔文夹同时校准完成后固定锁紧接头不要反复插拔。6.3 一个容易被忽略的经验VREF最小电压限制最后分享一个我早期忽略的细节。ADS1220的外部基准电压VREF是有最低限制的典型值0.5V不能像某些ADC那样用很小的基准电压。我最初设计时RREF取了1kΩ、激励电流250µAVREF只有0.25V结果芯片测量结果非线性得很明显一度以为是SPI通信问题。后来查数据手册才发现是基准电压低于规格下限。这也是我为什么最终选择了2kΩ基准电阻和250µA激励电流的组合VREF正好0.5V卡在最低要求线上留有余量又不至于让电流太大产生自热。如果你复刻这个方案时改了参考电阻或者激励电流务必先算一下VREF是否满足0.5V的最低要求。否则后面遇到无法解释的读数异常很大概率又是这个坑。