1. ADS1247与PT100测温系统概述
第一次接触高精度温度测量项目时,我被PT100传感器的线性度和稳定性所吸引,但很快发现要发挥它的全部性能并不容易。传统方案使用普通ADC配合运放电路,不仅电路复杂,还容易引入噪声。直到发现了TI的ADS1247这颗神器,才真正体会到"工欲善其事,必先利其器"的含义。
ADS1247是一款24位Δ-Σ型ADC,集成了可编程增益放大器(PGA)、可编程激励电流源和精密参考电压输入。最让我惊喜的是它内置的电流源可以直接驱动PT100,省去了外部恒流源电路。实际测试中,在-50℃~200℃范围内,系统精度能达到±0.1℃,完全满足工业级应用需求。
这个方案特别适合两类开发者:一是需要快速实现高精度测温的嵌入式工程师,二是对电路简洁性有要求的硬件设计师。通过SPI接口,我们可以灵活配置ADC参数,读取转换结果。下面我就从硬件设计到软件实现,详细分享整个开发过程。
2. 硬件设计关键要点
2.1 原理图设计解析
我的原理图设计经历了三次迭代才最终定型。第一次尝试时,忽略了参考电压的稳定性,导致测量结果漂移严重。第二次改进了参考电路,但没处理好地线布局,引入50Hz工频干扰。最终版电路如图所示:
核心设计要点包括:
- 使用IDAC1输出500μA激励电流,流经PT100和10Ω精密参考电阻
- AIN2/AIN3作为差分输入测量PT100电压
- REFP0/REFN0测量参考电阻两端电压
- 采用星型接地,模拟地与数字地在ADC下方单点连接
- 所有信号线尽量短,避免平行走线
实际布线时有个容易忽略的细节:DRDY信号线要远离SCLK等高频信号。有次我的读数总是不稳定,最后发现是这两根线平行走线过长导致的串扰。
2.2 元器件选型建议
在BOM选择上我踩过不少坑,这里分享些经验:
- 参考电阻:必须选用5ppm/℃以下的金属箔电阻,我用的Vishay的PTF系列
- 退耦电容:ADS1247的AVDD和DVDD需要分别用10μF钽电容+0.1μF陶瓷电容组合
- PCB材质:普通FR4即可,但建议使用2oz铜厚以提高热稳定性
- 连接器:PT100建议使用镀金端子,普通接线端子接触电阻会导致明显误差
特别提醒:PT100有三线制和两线制之分。三线制能补偿引线电阻,但需要占用更多ADC通道。我的项目空间有限,最终选用两线制方案,通过软件补偿引线电阻。
3. 软件实现详解
3.1 SPI通信配置技巧
ADS1247的SPI接口看似简单,但时序要求严格。我最开始用软件模拟SPI,发现读数总是不准,后来改用硬件SPI才解决问题。以下是关键配置参数:
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; // CPOL=0 hspi2.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=1 hspi2.Init.NSS = SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 约1MHz hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;寄存器配置中最容易出错的是MUX1寄存器。有次我把增益设成128倍,结果输入超出量程导致读数全零。建议初次使用时先设置为PGA=1,待系统稳定后再调整。
3.2 数据读取优化方案
原始方案是查询DRDY引脚状态,但频繁查询会占用CPU资源。后来我改进为中断方式:
// 中断回调函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == DRDY_Pin) { adc_data_ready = 1; } } // 主循环处理 while(1) { if(adc_data_ready) { uint32_t raw = ADS1247_ReadData_RTD(); float temp = ConvertToTemperature(raw); adc_data_ready = 0; } }对于实时性要求不高的应用,可以采用定时读取策略。我发现ADS1247在20SPS速率下噪声最低,所以设置定时器每50ms读取一次。
4. 温度换算算法优化
4.1 PT100特性与分段计算
PT100的电阻-温度关系是非线性的,直接使用二次多项式计算会有误差。我采用分段计算策略:
- 低于0℃时:使用ITS-90标准公式
- 0℃~100℃:线性近似,误差小于0.1℃
- 高于100℃:二次多项式拟合
实测发现,在-20℃~150℃范围内,分段算法比单一多项式精度提高3倍。关键代码如下:
float CalculateTemperature(float resistance) { if(resistance >= 100.0 && resistance < 139.0) { // 0~100℃范围 return (resistance - 100.0) / 0.385; } else if(resistance >= 139.0 && resistance < 390.0) { // 100~300℃范围 float t = (resistance - 100.0) / 0.385; return t - 0.0002*(t-100)*(t-100); } else { // 其他范围使用完整ITS-90公式 return ITS90_Formula(resistance); } }4.2 软件滤波处理
ADC读数难免会有噪声,我测试了几种滤波算法:
- 移动平均:简单但响应慢
- 卡尔曼滤波:效果好但计算量大
- IIR低通滤波:平衡了性能和复杂度
最终选择一阶IIR滤波器,只需一行代码实现:
filtered_value = 0.1 * raw_value + 0.9 * filtered_value;参数选择很有讲究:系数太大会导致响应迟缓,太小则滤波效果差。我的经验是,对于10SPS采样率,0.1~0.3的系数比较合适。
5. 调试经验与问题排查
5.1 常见问题解决方案
在实验室调试时遇到几个典型问题:
- 读数跳变大:检查电源纹波,我的案例中是LDO输出电容ESR过大导致
- 温度漂移:重新校准参考电阻,发现是电阻温漂系数不匹配
- SPI通信失败:用逻辑分析仪抓波形,发现是CS信号保持时间不足
特别分享一个隐蔽的bug:有次所有读数都是满量程,最后发现是原理图中AIN2和AIN3接反了。现在我的检查清单里一定会包含引脚连接验证。
5.2 校准流程建议
高精度测量离不开校准,我的三步校准法:
- 零点校准:将PT100置于冰水混合物中,记录读数
- 满度校准:使用标准100Ω电阻代替PT100
- 中间点验证:用50℃恒温槽检查线性度
校准数据建议存储在MCU的Flash或EEPROM中。我设计了一个简单的校准协议:
typedef struct { float offset; float gain; uint32_t crc; } CalibrationData;每次上电时读取校准参数,大大提高了系统长期稳定性。
6. 性能优化进阶技巧
6.1 低功耗设计
对于电池供电应用,我优化后的方案:
- 将采样率降至5SPS
- 关闭未使用的IDAC电流源
- 使用START引脚控制转换周期
实测电流从3.5mA降至800μA,而精度仅下降约0.2℃。
6.2 多通道扩展
虽然我只用了一个PT100,但ADS1247支持多路复用。扩展方案:
- 使用IDAC0和IDAC1驱动两个PT100
- 通过MUX寄存器切换测量通道
- 软件中实现分时采样
注意要留出足够的稳定时间,我的经验是切换通道后等待3个转换周期再读数。
7. 代码架构设计建议
好的代码结构能大幅降低维护成本。我的项目采用分层架构:
/Drivers /ADS1247 ads1247.c // 底层驱动 ads1247.h /Application /Temperature temp_sensor.c // 应用逻辑 temp_sensor.h /Utilities filters.c // 通用工具 filters.h关键设计原则:
- 硬件相关代码集中管理
- 业务逻辑与驱动分离
- 提供清晰的API接口
比如温度读取接口设计为:
TempSensor_Init(); // 初始化 float temp = TempSensor_Read(); // 读取温度这种架构方便移植到不同平台,我已经成功将其应用到STM32和ESP32项目中。