ARTICLE DETAIL

资讯详情

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

STM32+ADS1247+PT100高精度三线制测温方案详解

STM32+ADS1247+PT100高精度三线制测温方案详解 做嵌入式开发的人迟早都会碰到测温这个需求。便宜的选择有NTC、DS18B20但真要谈稳定和精度工业现场基本绕不开PT100。PT100测温本身不复杂难点在于PT100的电阻变化量太小0.1℃对应大约0.04Ω的变化要从这么小的信号里把温度准确抠出来普通单片机内置ADC根本不够看必须上一颗高精度ADC。这篇文章我会把STM32ADS1247PT100这套测温方案从头到尾拆开讲。内容包括方案原理、原理图设计、寄存器配置、STM32完整驱动代码、温度换算和校准以及我实际调板子时踩过的坑。不管你是做毕业设计、工业温控还是仪器仪表这套思路都能直接用。1. 方案选型为什么偏偏是ADS12471.1 PT100测温这件事的真正难点先算一笔账。PT100是铂热电阻0℃时阻值100Ω随着温度升高阻值近似按0.385Ω/℃增加。如果目标是0.5℃精度那电阻分辨率至少要做到0.19Ω如果目标是0.1℃分辨率对应就是0.0385Ω。假设激励电流取500μA0.1℃对应的电压变化只有19μV。普通STM32内置12位ADC参考电压3.3VLSB约为0.8mV对应温度约2℃完全没法做到毫伏级别以下的测量。想要把微伏级信号测出来只有两条路要么先用仪表放大器把信号放大到足够幅度要么直接用高分辨率ADC加片内PGA把微弱信号在AD前端就处理掉。用外部仪表放大器方案不是不行但电路结构会复杂很多。INA128一类的仪表放大器本身价格不便宜还要额外搭电流源、基准源、滤波电路信号链路长了每个环节都会引入误差。对于产品设计来说能用一个高集成度ADC解决问题就不要去堆分立器件。1.2 ADS1247的核心优势ADS1247是TI出的一颗24位Delta-Sigma ADC定位就是给传感器测量用的。它在单颗芯片里集成了PGA可编程增益放大器、两个恒流源IDAC、内部基准、温度传感器和SPI接口几乎把RTD测温需要的所有前端电路都做进去了。重点看这几个和PT100强相关的特性24位分辨率内部PGA最大128倍增益微伏级信号直接采。两路可配置恒流源IDAC支持50μA到1.5mA多档输出专门用来做RTD激励。通过比例测量法能达到0.1%以下的电阻测量精度激励电流漂移和电源波动可以被自动抵消。SPI接口单片机通讯简单SPI速度和时序配置在通用范围内。用ADS1247做三线制PT100原理图上基本就是“传感器一个参考电阻几个电容”就能跑起来BOM成本也压得很低。1.3 和ADS1220、其它方案的对比选型时很多朋友会纠结ADS1247和ADS1220。这两颗芯片确实非常像都是24位高精度ADC都支持PT100三线制方案。直观区别主要是通道数和封装ADS12204路输入2路差分不支持低功耗待机价格略低。ADS12472路差分输入带两个IDAC测量三线PT100时IDAC数量刚好够用性能在噪声指标上略好一点。如果以后想同时测4路PT100可以选ADS1248它是4路差分输入版本。我个人选ADS1247的理由很简单本人项目中温度采样通道固定两路AD1247的双通道够用而且它和ADS1248软件寄存器地址兼容以后真要扩展代码改动量也不大。至于直接用STM32内部ADC加运放适合对精度不敏感、只测个大概的应用比如环境温度显示但工业设备、医疗仪器、数据记录仪这类场景还是老老实实用专用ADC。2. 硬件设计原理图与关键器件2.1 三线制PT100的接线逻辑PT100传感器分为两线制、三线制和四线制。两线制最简单传感器两根线直接接到测量电路但导线本身有电阻普通铜线每米阻值大约0.01Ω到0.1Ω不等长距离传输时线阻误差直接叠加进测量结果可能带来好几度的偏差。三线制的思路是用两根等长的导线走同一端的信号再利用ADS1247的两个IDAC产生等量电流把线阻诱导出来的误差在差分测量中抵消掉。四线制精度最高但PT100四线制探头价格也贵一般应用用不上。我在这套方案里强烈建议用三线制PT100接线方式如下PT100一端的引线A接到ADS1247的AIN0引线B接到AIN1。PT100另一端的引线C接到电流回流的公共端。IDAC1和IDAC2分别配置到AIN0和AIN1输出等量激励电流。由于两根引线A和B材质、长度一致它们在各自支路上产生的压降基本相等差分输入测得的就是纯PT100电阻上的电压线阻误差被自动消除。实际画原理图时要注意AIN0和AIN1之间的差分走线要尽量等长避免因为PCB走线电阻不一致引入新的偏差。2.2 比例测量法与参考电阻选型ADS1247做PT100测量我强烈建议用比例测量法而不是直接用内部基准测量。比例测量的本质是让同一个激励电流既流过PT100也流过一颗精密参考电阻RREF然后把RREF两端的电压当ADC的参考电压。这样ADC输出的码值直接等于PT100电阻与RREF的比值激励电流的漂移、电源电压的波动都被抵消掉。计算公式可以简化成R_RTD ≈ (ADC_code × RREF) / (PGA × 2^23)所以关键就在于RREF的精度。这里我建议选精度0.1%、温漂系数5ppm/℃以内的精密电阻阻值选2kΩ或者2.4kΩ。电流源取640μA时2kΩ电阻上的压降约1.28V作为ADC参考电压比较合适不至于超过ADS1247参考输入范围。选型时候多花了大概2块钱但换来了整机温漂大幅降低这个投资非常值。RREF的焊盘建议放宽一点方便后续用低温漂电阻替换。2.3 电源、滤波与STM32最小电路STM32和ADS1247都工作在3.3V一颗AMS1117-3.3就够供电。电源部分有两个细节容易被人忽略AMS1117的输入输出电容我习惯用10μF钽电容并联0.1μF陶瓷电容钽电容ESR低、稳定性好陶瓷电容负责高频去耦。ADS1247的模拟电源AVDD和数字电源DVDD要分别用0.1μF和10μF电容就近接地模拟地和数字地建议单点连接避免数字开关噪声串进模拟前端。STM32与ADS1247之间走SPI口引脚分配如下STM32引脚功能PA4CS片选PA5SCLK时钟PA6MISO数据输出PA7MOSI数据输入PA8DRDY数据就绪PB0RESET复位PB1START启动这组引脚在STM32F103C8T6上都能直接映射到SPI1接线比较顺手。实际使用中如果引脚不够RESET和START也可以直接绑到3.3V或者用普通GPIO控制DRDY必须要接外部中断或输入捕获口因为读数据时要等它拉低。3. STM32驱动开发寄存器配置与代码3.1 SPI时序和初始化ADS1247的SPI接口在SCLK下降沿输出数据上升沿采样数据推荐工作模式是SPI Mode 1CPOL0CPHA1。SPI时钟频率不要拉太高我习惯配置成1MHz左右距离短、时序余量足稳定性优先。STM32这边我用标准库或者HAL库都行这里示例用HAL库。初始化SPI1的代码如下void ADS1247_SPI_Init(void) { SPI_InitTypeDef spi; __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef gpio {0}; gpio.Pin GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7; gpio.Mode GPIO_MODE_AF_PP; gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, gpio); gpio.Pin GPIO_PIN_6; gpio.Mode GPIO_MODE_AF_IN; HAL_GPIO_Init(GPIOA, gpio); hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); }如果你的板子是标准库工程把SPI初始化函数换成库对应的结构体配置就行SPI Mode 1这个关键点千万别改错。3.2 寄存器配置详解ADS1247的寄存器不多和PT100测温相关的就这几个MUX0输入多路复用器配置AIN0和AIN1作为正负输入。MUX1选择参考电压源这里选外部参考即REFP0/REFN0。SYS0配置PGA增益和输出数据速率。IDAC0配置IDAC1和IDAC2的电流大小。IDAC1配置IDAC1和IDAC2的输出引脚。我实际用的配置如下void ADS1247_InitRegs(void) { ADS1247_WriteReg(0x00, 0x01); // MUX0: AIN0正输入, AIN1负输入 ADS1247_WriteReg(0x01, 0x00); // VBIAS: 关闭 ADS1247_WriteReg(0x02, 0x20); // MUX1: 外部参考 REFP0/REFN0 ADS1247_WriteReg(0x03, 0x2A); // SYS0: PGA8倍输出速率20SPS ADS1247_WriteReg(0x04, 0x0C); // IDAC0: IDAC1IDAC2640uA ADS1247_WriteReg(0x05, 0x05); // IDAC1: IDAC1输出AIN2, IDAC2输出AIN3 }其中的SYS0寄存器我没法给出所有位的完整说明不同批次数据手册略有差异但大原则是PGA增益取8倍对应PT100在0℃到300℃范围内的信号幅度都不会超量程输出速率20SPS在低噪声和响应速度之间比较平衡。IDAC配置这里特别提醒一句IDAC不能输出到REFP/REFN引脚也不能输出到没有选择作为输入的引脚否则测量路径会乱。我习惯把IDAC输出固定到AIN2和AIN3和差分输入通道分开省心。3.3 完整驱动代码下面给出一套能直接编译烧录的核心驱动。先看SPI读写命令封装#define ADS1247_CMD_RESET 0x06 #define ADS1247_CMD_START 0x08 #define ADS1247_CMD_STOP 0x0A #define ADS1247_CMD_RDATA 0x12 #define ADS1247_CMD_WREG 0x40 #define ADS1247_CMD_RREG 0x20 void ADS1247_CS_Low(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); } void ADS1247_CS_High(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } void ADS1247_WriteReg(uint8_t addr, uint8_t data) { uint8_t cmd[3]; cmd[0] ADS1247_CMD_WREG | (addr 0x0F); cmd[1] 0x00; // 只写1个寄存器 cmd[2] data; ADS1247_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 3, 10); ADS1247_CS_High(); } uint8_t ADS1247_ReadReg(uint8_t addr) { uint8_t cmd[2] { ADS1247_CMD_RREG | (addr 0x0F), 0x00 }; uint8_t rx[2] {0, 0}; ADS1247_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 2, 10); HAL_SPI_Receive(hspi1, rx, 2, 10); ADS1247_CS_High(); return rx[0]; }读ADC转换结果的函数核心是等DRDY变低再发RDATA命令取三个字节int32_t ADS1247_ReadADC(void) { uint8_t buf[3]; uint8_t cmd ADS1247_CMD_RDATA; uint32_t timeout 1000; while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) GPIO_PIN_SET) { if (--timeout 0) return 0; // 超时保护 HAL_Delay(1); } ADS1247_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, buf, 3, 10); ADS1247_CS_High(); int32_t code ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; // ADS1247输出是二进制补码24位有符号数 if (code 0x800000) code | 0xFF000000; return code; }主流程里初始化完ADS1247之后发送START命令让它连续转换然后主循环里读数据换算温度即可void ADS1247_Start(void) { uint8_t cmd ADS1247_CMD_START; ADS1247_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); ADS1247_CS_High(); }整套流程跑通以后ADS1247会自动在每次转换完成后把DRDY拉低STM32这边通过查询或者外部中断读取都行。我建议在中断里置标志位主循环里处理换算这样可以避免查询阻塞影响其它实时任务。4. 从ADC码值到温度换算与校准4.1 码值换算成电阻值ADS1247的24位ADC输出是二进制补码满量程对应±Vref/PGA。当PGA8、Vref1.28V时LSB对应的电压是V_LSB Vref / (PGA × 2^23)代入计算1.28 / (8 × 8388608) ≈ 19.07nV这个分辨率相当可观。但实际系统噪声会把有效分辨率限制在几个微伏级别所以不要被理论值迷惑还是要靠滤波和校准。电阻值的换算公式R_RTD ADC_code × RREF / (PGA × 2^23)这里的ADC_code是带符号的24位码值PGA是寄存器里配置的增益RREF是实际焊接的参考电阻阻值。比如ADC读出码值是138000PGA8RREF2000ΩR_RTD 138000 × 2000 / (8 × 8388608) ≈ 411.5Ω这个电阻值对应的温度大概是850℃左右说明当前信号已经接近满量程附近。实际应用中PT100测温范围通常不超过500℃所以正常情况下码值不会顶格。4.2 电阻换算成温度公式PT100的温度-电阻关系有两个精度等级。工业上普遍采用IEC 60751标准0℃以上用二次多项式就够了R(t) R0 × (1 A×t B×t²)其中R0100ΩA3.9083×10⁻³B-5.775×10⁻⁷。反过来由电阻求温度可以把方程整理成一元二次方程B×t² A×t (1 - R/R0) 0解出来t (-A sqrt(A² - 4B×(1 - R/R0))) / (2B)这段代码直接在STM32上浮点运行完全没问题float PT100_ResToTemp(float R) { const float A 3.9083e-3f; const float B -5.775e-7f; float r R / 100.0f; float delta A * A - 4.0f * B * (1.0f - r); if (delta 0.0f) delta 0.0f; return (-A sqrtf(delta)) / (2.0f * B); }低于0℃时标准曲线里还有一个三次项修正。如果你要测-50℃甚至更低的温度我建议用查表法分段插值代码提前把PT100分度表按10℃间隔做成数组测出来的电阻值先查区间再做线性插值。查表法对单片机的算力要求极低代码也直观。4.3 两点校准实战即使比例测量法消除了大部分系统误差ADC输入失调、PCB铜箔电阻、参考电阻初始精度还会留下固定偏差。这时候就需要校准。我的做法是在批量生产中做零点和增益两点校准配方不复杂把PT100探头放进冰水混合物中记录ADC读数作为零点校准点温度参考值0℃。把探头放进沸水环境中记录第二个读数温度参考值100℃。计算出增益修正系数K (100 - 0) / (Temp_raw_boil - Temp_raw_ice) Offset 0 - K × Temp_raw_ice实际测量温度 K × 原始计算温度 Offset。这套线性校正对PT100这种近似线性的传感器效果很好可以在0~100℃范围内把误差压到±0.2℃以内。校准参数我习惯存到STM32内部Flash里掉电不丢失。每片板子在生产时写一遍校准参数后面使用就不用再重复标定。5. 常见问题与调试实录5.1 读数不稳定跳动幅度大这是我最常遇到的问题。读数乱跳先不要怀疑ADC本身优先检查几个方向激励电流路径上有没有接触不良比如PT100端子松动、接线端子氧化。SPI的MISO线上有没有受到干扰特别是长排线连接时建议降低SPI时钟频率并加一个10kΩ上拉。电源纹波是否偏大示波器看AVDD是否有毛刺必要时在模拟电源入口加磁珠和LC滤波。DRDY信号线上如果有高频开关干扰可以串一个几百欧姆的电阻或者用RC滤波后进STM32外部中断。我的调试经验是先把SPI时钟降到100kHz排除通信问题再把PGA增益调到1确认信号范围最后看数据均值方差逐段缩小范围。5.2 温度总体偏差固定要么偏高要么偏低固定的温度偏移一般指向线阻误差或者校准偏移。用二线制接法时假设两根线一共0.5Ω那在0℃时测出来的电阻会变成100.5Ω等效温度偏差约1.3℃非常明显。解决办法有两类硬件上改用三线制利用两个IDAC抵消线阻。软件上把线阻测出来在电阻值里做减法。方法是短路PT100端子读一次ADC码值换算成等效线阻R_wire然后每次测量时 R_true R_measured - R_wire。批量校准时如果发现每台板子的偏差范围一致直接把偏移量写死在标定参数里也行但前提是连接线长度和生产工艺必须稳定一致。5.3 SPI通信异常读回来全是0xFFFFFF或0x000000SPI时序错了最容易读到这种全0或全1的数据。ADS1247代码里有个容易忽略的点寄存器写命令是“命令字节寄存器数量-1数据字节”如果你只发命令和数据两个字节漏了中间的count字节那所有写入都会错位。另外ADS1247的RDATA命令在读数据时CS要保持低电平读三个字节之后再拉高CS不能在字节之间拉高。我刚开始写驱动时不熟悉每个字节读完后都把CS拉高了一下结果读回来的数据永远不对。如果要排查SPI通信是否正常可以在初始化后回读所有寄存器的内容和写入值比对一遍这个自检步骤能第一时间暴露物理连接或者时序问题。5.4 代码里常踩的其它坑DRDY引脚没有配置为输入模式导致一直读到高电平。这种情况多半是GPIO初始化时钟没打开或者引脚配置成了复用推挽输出。上电后忘记发START命令芯片处于待机状态DRDY永远不拉低。ADS1247上电默认是停止状态必须先写寄存器再发START。参考电阻虚焊导致REF和REF-之间没有电压ADC码值会直接顶到满量程。焊接后用万用表量一下RREF两端电压这是一个非常有效的硬件自检方法。6. 这套方案还能怎么扩展最后聊一点我自己项目里的扩展经验。ADS1247除了测PT100电阻还能利用片内温度传感器做冷端补偿配合热电偶使用。虽然本文主要讲PT100但理解这套寄存器配置和读数流程之后切换到热电偶测温只是改一下输入配置和换算公式的事情。另外如果以后要把测量温度通过4-20mA电流环传到PLC或者用RS485走Modbus协议ADS1247负责的高精度采集部分完全不用动只需要在STM32端加一个DAC或RS485芯片把结果转成标准工业信号就行。ADC采集的稳定性是整个系统的地基地基打好了上面接什么通讯外设都不慌。我自己在项目中最终实测的三线制PT100在0~200℃范围内配合两点校准和256次滑动平均滤波精度稳定在±0.2℃以内连续运行72小时漂移小于0.1℃这个结果对大多数工业采集场景已经非常够用。ADS1247用顺手之后再回头去看那些靠运放堆出来的测温电路是真的想感叹集成方案省心太多。
返回列表