1. 工业与HVAC温度测量项目的整体设计思路
工业现场和HVAC系统里的温度测量,跟我们在实验室拿个万用表测室温完全是两码事。工业场景往往要求-40℃到125℃甚至更宽的量程,HVAC水系统里PT100/PT1000铂电阻是绝对主力,而工业炉窑、蒸汽管道又大量使用K型、S型热电偶。这些传感器输出的信号极其微弱——PT100在0℃时只有100Ω,满量程变化也就几十到一百多欧姆;热电偶更夸张,K型在室温附近灵敏度只有约41μV/℃,冷端补偿没做好,误差直接起飞。
我这次选用的方案是ADS122U04IPWR搭配STM32F427ZI。ADS122U04是TI的一款24位ΔΣ ADC,内置PGA、双匹配电流源、UART接口,专门为小信号传感器设计。STM32F427ZI则是ST的Cortex-M4旗舰,180MHz主频、2MB Flash、256KB SRAM,带FPU和DSP指令,跑温度线性化、数字滤波、Modbus协议栈绰绰有余。
为什么不用STM32自带的12位ADC?原因很简单:分辨率不够。STM32F427的12位ADC在3.3V参考下,1LSB约0.8mV,而PT100在0.1℃时的电压变化可能只有几十微伏,根本分辨不出来。ADS122U04的24位分辨率配合PGA增益128,等效输入分辨率可以做到几十纳伏级别,这才是工业级温度测量该有的底子。
整个系统的信号链是这样的:传感器(PT100/PT1000/热电偶)→ 模拟前端(RC滤波+基准电阻)→ ADS122U04(PGA+24位ADC+激励电流源)→ UART → STM32F427ZI(数据处理+线性化+通信)→ 上位机/HMI。这个架构的好处是模拟部分和数字部分彻底解耦,ADC靠近传感器放置,UART差分信号抗干扰能力强,STM32可以放在控制柜里专心做运算和通信。
注意:ADS122U04是UART接口版本,不是SPI。很多人看到ADS1220(SPI版)的代码直接拿来用,结果发现根本通不了。UART版本需要严格按照波特率、帧格式来配置,后面我会详细讲。
2. ADS122U04IPWR核心细节与硬件设计要点
2.1 芯片关键特性与选型理由
ADS122U04IPWR的核心参数我列个表,方便你对照自己的需求:
| 参数 | 数值 | 对温度测量的意义 |
|---|---|---|
| 分辨率 | 24位 | 微伏级信号也能分辨 |
| PGA增益 | 1~128 | 小信号放大,适配PT100/热电偶 |
| 数据速率 | 最高2000SPS | 高速采样,适合多通道扫描 |
| 激励电流源 | 双匹配10μA~1.5mA | 直接驱动PT100,无需外部恒流源 |
| 接口 | UART | 抗干扰强,适合工业现场 |
| 封装 | TSSOP-16 | 手工焊接友好,散热OK |
| 工作温度 | -40~125℃ | 工业级宽温 |
选ADS122U04而不是ADS1220,主要是看中UART接口。工业现场SPI走线长了容易被变频器、继电器干扰,UART差分信号(配合RS485收发器)能传几百米。当然如果你板内通信,SPI版更省IO,这个看具体场景。
2.2 模拟前端电路设计
PT100三线制接法是HVAC和工业测温的标配,能抵消导线电阻影响。具体接法:PT100一端接AIN0,另一端接AIN1,第三根线接AIN2作为补偿。ADS122U04的内部电流源IDAC1从AIN0流出,经过PT100到AIN1,再经过基准电阻RREF到地。IDAC2从AIN2流出,经过另一根等长导线到AIN1,这样两根导线的压降在ADC输入上相互抵消。
基准电阻RREF的选择很关键。假设用IDAC=500μA,PT100在0℃时100Ω,在850℃时约390Ω。RREF上的压降要满足ADC的基准电压要求。ADS122U04的基准可以选内部2.048V或外部。如果用外部RREF,建议选精密电阻如Vishay的PTF系列,温漂<5ppm/℃。计算:RREF = VREF / IDAC = 2.048V / 500μA = 4096Ω。实际选4.02kΩ或4.12kΩ标准值,然后软件校准。
热电偶的话,需要冷端补偿。可以用ADS122U04的另一个通道接一个PT100或LM35测冷端温度,或者用STM32内部的温度传感器(精度差,不推荐工业用)。冷端补偿的精度直接决定整体误差,K型热电偶冷端误差1℃就导致测量误差1℃。
实操心得:PCB布局时,模拟部分和数字部分要严格分区。ADS122U04的AVDD和DVDD分别用磁珠隔离,模拟地数字地单点连接。基准电阻和PT100的走线要等长、对称,最好包地处理。我踩过的坑:一开始没做等长,三线制补偿效果很差,后来改成蛇形走线才把误差压到0.1℃以内。
2.3 电源与基准设计
ADS122U04的AVDD范围2.3V~5.5V,DVDD 1.65V~3.6V。工业现场建议AVDD用5V,DVDD用3.3V(和STM32一致)。电源纹波要控制在10mV以内,否则会直接调制到ADC输出上。我用的是TPS7A4901(超低噪声LDO)给AVDD供电,PSRR在1kHz有60dB以上。
基准电压的选择:内部2.048V基准温漂典型20ppm/℃,对于一般工业测温够用。如果要求0.05℃精度,建议用外部基准如REF5025(2.5V,3ppm/℃)。注意外部基准要接在REF引脚,并且配置寄存器切换到外部基准模式。
3. STM32F427ZI固件架构与ADS122U04驱动实现
3.1 硬件连接与UART配置
ADS122U04的UART接口是2线制,TX和RX。注意它的逻辑电平是DVDD,如果DVDD=3.3V,可以直接连STM32的UART引脚。连接方式:
- ADS122U04 TX → STM32F427ZI PA10 (USART1_RX)
- ADS122U04 RX → STM32F427ZI PA9 (USART1_TX)
- ADS122U04 DRDY → STM32F427ZI PB0 (外部中断,数据就绪)
- ADS122U04 CS → 接地(UART模式下CS无效,但建议接地)
UART配置:波特率建议用9600或19200,工业环境不要超过115200。数据格式8N1,无流控。ADS122U04的UART是自动波特率检测的,上电后发送0x55同步字,芯片会自动匹配。但为了可靠,我一般在初始化时手动发送同步序列。
// STM32 HAL UART初始化 huart1.Instance = USART1; huart1.Init.BaudRate = 19200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&huart1);3.2 ADS122U04寄存器配置
ADS122U04有4个配置寄存器,通过UART命令读写。命令格式:0x40+寄存器地址(读),0x60+寄存器地址(写)。关键寄存器配置:
- 寄存器0:MUX选择、增益、PGA使能
- 寄存器1:数据速率、工作模式、转换模式
- 寄存器2:基准选择、IDAC电流值、IDAC路由
- 寄存器3:IDAC2路由、DRDY模式
我的配置:MUX=AIN0/AIN1,增益=128,数据速率=20SPS(50/60Hz抑制),连续转换模式,IDAC=500μA,IDAC1→AIN0,IDAC2→AIN2。
// ADS122U04初始化序列 void ADS122U04_Init(void) { uint8_t cmd; // 复位 cmd = 0x06; // RESET命令 HAL_UART_Transmit(&huart1, &cmd, 1, 100); HAL_Delay(1); // 配置寄存器0: MUX=AIN0/AIN1, 增益128, PGA使能 cmd = 0x40; // 写寄存器0 uint8_t reg0 = 0x08; // 0000 1000: MUX=0000, GAIN=100(128) HAL_UART_Transmit(&huart1, &cmd, 1, 100); HAL_UART_Transmit(&huart1, ®0, 1, 100); // 配置寄存器1: 20SPS, 连续转换 cmd = 0x42; // 写寄存器1 uint8_t reg1 = 0x04; // 0000 0100: DR=0100(20SPS), 连续模式 HAL_UART_Transmit(&huart1, &cmd, 1, 100); HAL_UART_Transmit(&huart1, ®1, 1, 100); // 配置寄存器2: 外部基准, IDAC=500uA cmd = 0x44; // 写寄存器2 uint8_t reg2 = 0x50; // 0101 0000: VREF=外部, IDAC=500uA HAL_UART_Transmit(&huart1, &cmd, 1, 100); HAL_UART_Transmit(&huart1, ®2, 1, 100); // 启动连续转换 cmd = 0x08; // START/SYNC命令 HAL_UART_Transmit(&huart1, &cmd, 1, 100); }3.3 数据读取与DRDY中断处理
ADS122U04在连续转换模式下,每次转换完成会拉低DRDY。STM32用外部中断检测下降沿,然后在中断服务函数里读取数据。注意UART读取有延迟,建议在中断里置标志位,主循环处理。
// DRDY中断回调 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { data_ready = 1; } } // 主循环读取数据 void Process_ADC_Data(void) { if (data_ready) { data_ready = 0; uint8_t cmd = 0x10; // RDATA命令 uint8_t rx_buf[3]; HAL_UART_Transmit(&huart1, &cmd, 1, 100); HAL_UART_Receive(&huart1, rx_buf, 3, 100); // 24位数据拼接 int32_t adc_code = (rx_buf[0] << 16) | (rx_buf[1] << 8) | rx_buf[2]; // 符号扩展 if (adc_code & 0x800000) { adc_code |= 0xFF000000; } // 转换为电压 float voltage = (float)adc_code * VREF / 8388608.0f; // 2^23 // 转换为电阻 float resistance = voltage / IDAC_CURRENT; // 转换为温度(PT100公式) float temperature = (resistance - 100.0f) / 0.385f; // 简化线性 } }注意:ADS122U04的UART读取命令后,芯片会返回3字节数据。但如果你在连续转换模式下用RDATA命令,返回的是最近一次转换结果。更可靠的方式是等DRDY中断后立即读取,避免数据竞争。
4. 温度线性化算法与精度优化
4.1 PT100/PT1000的Callendar-Van Dusen方程
PT100的线性近似(R=100+0.385T)只在0~100℃范围内误差小,全量程必须用Callendar-Van Dusen方程:
- T ≥ 0℃:R(T) = R0 * (1 + AT + BT²)
- T < 0℃:R(T) = R0 * (1 + AT + BT² + C*(T-100)*T³)
其中A=3.9083e-3,B=-5.775e-7,C=-4.183e-12。反解温度用迭代法或查表法。STM32F427ZI有FPU,直接跑牛顿迭代3次就能收敛到0.01℃。
float PT100_ResistanceToTemperature(float R) { float R0 = 100.0f; float A = 3.9083e-3f; float B = -5.775e-7f; float C = -4.183e-12f; // 牛顿迭代 float T = (R - R0) / (R0 * A); // 初始猜测 for (int i = 0; i < 5; i++) { float f, df; if (T >= 0) { f = R0 * (1 + A*T + B*T*T) - R; df = R0 * (A + 2*B*T); } else { f = R0 * (1 + A*T + B*T*T + C*(T-100)*T*T*T) - R; df = R0 * (A + 2*B*T + C*(4*T*T*T - 300*T*T)); } T = T - f / df; } return T; }4.2 热电偶冷端补偿与多项式拟合
K型热电偶的电压-温度关系是非线性的,NIST提供了多项式系数。冷端补偿:T_total = T_measured + T_cold_junction。冷端温度用PT100或LM35测。多项式计算量大,建议用查表+线性插值,STM32的Flash存表绰绰有余。
// K型热电偶查表法(简化示例) const float K_Type_Table[][2] = { {0.0f, 0.000f}, {10.0f, 0.397f}, {20.0f, 0.798f}, // ... }; float K_Type_VoltageToTemperature(float mV) { // 查表+线性插值 for (int i = 0; i < TABLE_SIZE-1; i++) { if (mV >= K_Type_Table[i][1] && mV < K_Type_Table[i+1][1]) { float ratio = (mV - K_Type_Table[i][1]) / (K_Type_Table[i+1][1] - K_Type_Table[i][1]); return K_Type_Table[i][0] + ratio * (K_Type_Table[i+1][0] - K_Type_Table[i][0]); } } return 0.0f; }4.3 数字滤波与工频抑制
工业现场50Hz/60Hz干扰无处不在。ADS122U04在20SPS时自带50/60Hz抑制,但如果你用更高速率,就得在STM32里做数字滤波。我一般用滑动平均+IIR低通组合:先做16点滑动平均(去随机噪声),再过截止频率1Hz的IIR(去工频残余)。
// 滑动平均滤波器 #define FILTER_SIZE 16 float filter_buf[FILTER_SIZE]; int filter_idx = 0; float MovingAverage(float new_val) { filter_buf[filter_idx] = new_val; filter_idx = (filter_idx + 1) % FILTER_SIZE; float sum = 0; for (int i = 0; i < FILTER_SIZE; i++) { sum += filter_buf[i]; } return sum / FILTER_SIZE; } // IIR低通滤波器 float iir_last = 0; float IIR_LowPass(float new_val, float alpha) { iir_last = alpha * new_val + (1 - alpha) * iir_last; return iir_last; }实操心得:滤波不是越多越好。我试过用64点滑动平均,结果响应慢得离谱,温度突变要好几秒才跟上。后来改成16点滑动+IIR,响应时间和噪声抑制平衡得不错。另外,如果ADC速率是20SPS,其实不需要额外滤波,芯片自带的抑制已经很强了。
5. 常见问题与排查技巧实录
5.1 通信失败与数据异常排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| UART无响应 | 波特率不匹配 | 检查STM32和ADS122U04的波特率,用示波器看波形 |
| 数据全0或全1 | 传感器断路/短路 | 测PT100电阻,检查接线 |
| 数据跳变严重 | 电源纹波大 | 示波器看AVDD,加LC滤波 |
| 温度偏差大 | 基准电阻温漂 | 换精密电阻,软件校准 |
| DRDY不触发 | 转换模式配置错 | 检查寄存器1的连续/单次模式位 |
5.2 精度校准与长期稳定性
出厂校准只能保证基本精度,现场校准才是关键。我一般用两点校准:冰水混合物(0℃)和沸水(100℃,注意气压修正)。校准系数存在STM32的Flash里,每次上电加载。
长期稳定性方面,PT100本身年漂移<0.05℃,但电路板温漂可能更大。建议每半年用标准电阻箱校准一次。如果现场没法校准,至少做一次上电自检:测基准电阻的电压,反推IDAC电流,看是否在预期范围内。
注意:ADS122U04的内部IDAC电流值有±5%的初始误差,但温漂很小。如果要求高精度,必须用外部精密电阻做基准,然后软件计算实际电流值。
5.3 工业现场抗干扰实战
变频器、接触器、继电器是三大干扰源。我的经验:
- 传感器线用双绞屏蔽线,屏蔽层单端接地(接控制柜地)
- ADS122U04的模拟输入加RC滤波:100Ω+100nF,截止频率约16kHz
- UART线用磁珠+TVS管,防止浪涌
- PCB上模拟地和数字地分开,用0Ω电阻单点连接
- 软件上做数据合理性检查:连续3次采样偏差>5℃就丢弃,重新采样
有一次在注塑机车间,温度读数每隔几秒跳一次,后来发现是旁边的大功率加热器启停导致地电位波动。加了隔离电源模块和光耦隔离UART后,问题彻底解决。所以工业现场,隔离不是可选项,是必选项。
6. 系统集成与HVAC应用适配
6.1 Modbus RTU通信实现
HVAC系统里Modbus RTU是标配。STM32F427ZI的UART资源丰富,我用USART2做Modbus,USART1接ADS122U04。Modbus从站地址、波特率、校验方式都可以通过拨码开关或EEPROM配置。
// Modbus RTU帧处理(简化) void Modbus_Process(void) { if (modbus_rx_complete) { uint8_t addr = modbus_rx_buf[0]; uint8_t func = modbus_rx_buf[1]; if (addr == slave_addr) { if (func == 0x03) { // 读保持寄存器 uint16_t start_addr = (modbus_rx_buf[2] << 8) | modbus_rx_buf[3]; uint16_t reg_count = (modbus_rx_buf[4] << 8) | modbus_rx_buf[5]; // 温度值放大10倍存寄存器 uint16_t temp_reg = (uint16_t)(temperature * 10); modbus_tx_buf[0] = slave_addr; modbus_tx_buf[1] = func; modbus_tx_buf[2] = reg_count * 2; modbus_tx_buf[3] = temp_reg >> 8; modbus_tx_buf[4] = temp_reg & 0xFF; // ... CRC计算 HAL_UART_Transmit(&huart2, modbus_tx_buf, 7, 100); } } } }6.2 多通道温度扫描策略
HVAC系统往往要测多个点:送风、回风、冷冻水进/出、热水进/出。ADS122U04只有一个ADC,多通道需要外部多路复用器(如CD4051)或者多个ADS122U04。我的方案:4个ADS122U04,每个接2路PT100,通过UART地址区分(ADS122U04不支持地址,所以用STM32的4个UART分别接)。
扫描策略:每个通道20SPS,4个芯片轮流转换,STM32用DMA+空闲中断接收,CPU占用率<5%。温度更新周期200ms,完全满足HVAC控制需求。
6.3 低功耗与可靠性设计
如果做电池供电的无线温度变送器,功耗要精打细算。ADS122U04在20SPS时电流约200μA,STM32F427ZI跑180MHz时约100mA,所以不能一直全速跑。我的做法:STM32平时跑16MHz,定时唤醒采集,采集完切回低功耗模式。ADS122U04用单次转换模式,转换完自动休眠。
可靠性方面,看门狗必须开。STM32的IWDG独立看门狗,超时2秒。另外Flash里存两份校准参数,互为备份。UART通信加CRC校验,数据帧加序号,防止丢包重包。
这个方案我在三个HVAC项目里实际用过,最长的已经跑了两年多,温度漂移在0.2℃以内。工业现场电磁环境复杂,但只要模拟前端设计到位、软件滤波合理、隔离措施到位,ADS122U04+STM32F427ZI这套组合的稳定性和精度完全经得起考验。如果你也在做类似的项目,建议先从PT100三线制入手,把基准和IDAC配置调通,再逐步加滤波和通信,这样调试起来思路最清晰。