在工业测温和HVAC控制项目里,最常被问起的问题是:用MCU内置ADC采集PT100到底行不行?我的回答通常是不行。尤其当你把ADS122U04IPWR和MK22FN512VLH12这套组合跑完一个完整的夏天和冬天之后,会更确定这件事。这篇文章就来讲讲,我如何用一颗24位UART接口ADC加一颗Cortex-M4F MCU,搭出一套稳定、可校准、能上产线的温度测量链路,以及我在这条链路上踩过的那些坑。
先说结论:这套方案非常适合工业现场的多点温度采集、AHU/新风机组进出风温度测量、冷冻水管供回水温度监测这类场景。ADS122U04IPWR负责把PT100/PT1000的小信号转成高分辨率数字量,MK22FN512VLH12负责把数字量换算成温度、做报警判断和联网上报。如果你正在纠结“内置ADC噪声太大怎么办”“SPI隔离太麻烦”“RTD非线性怎么处理”,这篇文章应该能给你一个完整的参考答案。
1. 为什么是ADS122U04IPWR + MK22FN512VLH12:选型背后的逻辑
1.1 ADS122U04IPWR 到底是什么
ADS122U04IPWR是TI推出的24位低功耗ΔΣ型ADC,封装是TSSOP-16,后面带“I”表示工业级温度范围,能在-40℃到125℃环境下工作。这颗芯片最特别的地方在于,它把结果输出接口从常见的SPI换成了UART。注意,我说的不是通过UART转SPI再挂ADC,而是这颗ADC本身就带UART控制器,MCU可以直接用两三根线读写它的寄存器和转换结果。
它内部集成的资源非常够用:4通道输入的模拟多路开关,可配置的PGA增益(从1到128),两个用于给RTD传感器提供激励的电流源IDAC,内部1.25V/2.048V基准电压源,以及一个用于热电偶冷端补偿的片上温度传感器。这些模块凑在一起,意味着你不需要再单独设计恒流源电路和增益放大电路,一颗ADC就把模拟前端全包了。
在我早期的一个项目里,用分立运放搭仪表放大器,再加16位SPI ADC,PCB面积大、元器件多,而且长期温漂很难控制。换成ADS122U04后,模拟前端从“一堆运放”变成了“一个TSSOP芯片”,硬件设计工作量一下子降了一个量级。
1.2 MK22FN512VLH12 在这套方案里的位置
MK22FN512VLH12是NXP Kinetis K22系列里的中端型号,ARM Cortex-M4F内核,主频120MHz,512KB Flash,128KB SRAM,LQFP64封装,带着FPU。对温度测量这种“数据量小、计算精度敏感”的场景来说,这个配置算是“富余但合理”。
它在这套系统里做三件事。第一,通过UART和ADS122U04通信,发命令、收数据。第二,把ADC原始码换算成电阻,再换算成摄氏度,这中间有Callendar-Van Dusen方程和校准修正计算,FPU能省不少时间。第三,跑应用逻辑,比如PID控制、报警阈值判断、RS485组网协议等。
我选的版本是FN512,也就是说512KB Flash,这个容量对后期OTA固件升级、记录一段时间的温度日志来说非常从容。如果当时选了128KB Flash的型号,后面想塞进去一个断线检测自诊断模块就得翻来覆去省空间。
1.3 为什么不是内置ADC,也不是SPI接口ADC
先说说MCU内置ADC。Kinetis K22内部确实也有16位ADC,但那个“16位”在工业现场要打个大大的问号。内置ADC参考电压通常是MCU电源电压,而电源电压会随负载波动;PGA增益较小,对PT100在0℃时约100Ω、电流激励下只有几十毫伏的差分信号来说,信噪比完全不够看。常温下做Demo没问题,但温度一变,零漂和增益漂移会让你重新怀疑人生。
再看SPI接口的ADC,比如ADS1220。SPI四根线:SCLK、MOSI、MISO、CS,速度高,但是做隔离时每一根信号线都要过数字隔离器,而且SCLK是双向同步时钟,线缆一长,时序就容易乱,对PCB布局和线缆屏蔽要求特别高。UART只需要TX、RX两根线,隔离成本直接减半,而且UART对时序的要求远没有SPI苛刻,即使走1米左右的排线,问题也不大。这就是我最终选择ADS122U04IPWR的根本原因。
提示:如果你的传感器和MCU之间距离超过5米,建议把UART转成RS-485电平再传,而不是直接用TTL电平。ADS122U04的UART信号本质上是3.3V TTL,远程传输必须做电平转换。
2. 从传感器到MCU的完整硬件链路
2.1 传感器怎么选:PT100、PT1000还是热电偶
在HVAC领域,最常见的温度传感器是PT1000。原因很简单:PT1000在0℃时的阻值是1000Ω,比PT100高一个数量级,同样用250μA激励电流,PT1000产生的信号电压是250mV,PT100只有25mV,这意味着引线电阻和接触电阻的影响占比小得多。HVAC管道里的温度范围通常也就是-40℃到150℃,PT1000足够覆盖。
但工业现场要分情况看。如果测量范围超过300℃,比如锅炉排烟温度、热风管道,我一般会改用热电偶。热电偶信号更小,需要冷端补偿,ADS122U04内置的温度传感器正好可以做冷端参考。不过要注意,热电偶的电压只有几十微伏每摄氏度,必须把PGA增益开到64或128,对PCB漏电流的要求会变得很高,洗板不干净的话读数会飘。
在这篇文章的实战项目里,AHU进出风温度和热水供回水温度都用的PT1000,四线制接法。四线制虽然多两根线,但能把引线电阻误差彻底消除,是工业级精度的底线。
2.2 模拟前端电路:激励电流、参考电阻和滤波
用ADS122U04测RTD有一个非常优雅的做法,叫比率式测量。让IDAC电流源同时流过RTD和一颗精密参考电阻Rref,RTD上的电压经过PGA放大后接到ADC输入端,同时Rref上的电压直接作为ADC的参考电压。这样,如果IDAC电流因温度略有漂移,RTD电压和参考电压会同步变化,最终ADC码值基本不受电流源漂移影响。
具体参数我建议这样设:IDAC电流设为250μA,Rref选2.048kΩ(0.1%精度,25ppm温漂)。这样参考电压大约512mV,PT1000在0℃时1kΩ,电压250mV,PGA设成8倍,输入到ADC的电压约2V,接近满量程又不会削顶,动态范围利用得很充分。Rref一定要用低温漂精密电阻,哪怕其他元件都普通,这颗别省。
滤波器方面,在AIN0和AIN1引脚各加一个1kΩ电阻,然后并联一个1μF电容到引脚之间,构成截止频率约80Hz的低通RC滤波器,专门压制50Hz工频干扰和变频风机的高频噪声。这个电容不能太大,否则ADC前端开关电容会充放电不及,导致采样不准确。
2.3 与MK22FN512VLH12的UART接线细节和隔离设计
ADS122U04的UART和MK22的UART都是3.3V电平,板上连接很简单:TX接RX,RX接TX,共地。但如果传感器板和控制板物理上是分开的,比如一个在机房、一个在配电柜,就必须考虑隔离。我的做法是:ADC这边用隔离电源供电,UART信号经过数字隔离器再进MK22。隔离器选双向的型号,TX和RX各一个通道,静默时输出高电平,符合UART空闲电平要求。
有一个细节容易被忽略:ADC的UART_TX在复位期间可能是高阻态,如果MCU已经初始化完成并开始等待接收,是没问题的。但如果你开了MCU的UART接收中断,而ADC还没上电,线上噪声可能被误判成起始位,产生噪声中断。解决方式是在MCU接收中断里加帧错误判断,或者用一个GPIO做“数据准备好”握手信号,简单可靠。
另外,直接把PCB上的模拟地和数字地切成两个完整平面,而不是用一根细线连接,这是我在首版板子上吃过大亏后学到的。最开始为了省事,ADC的地和MK22的地混在一起,读数在低4位乱跳。后来把地平面切开,在ADC的PowerPAD下面单点汇聚,噪声立刻降了一个数量级。
3. 让ADC“开口说话”:UART配置与寄存器驱动
3.1 先理解ADS122U04的寄存器配置顺序
ADS122U04的配置是通过一组寄存器实现的,实际位定义要以TI数据手册为准,但配置逻辑是通用的。我的习惯是上电后先写一遍完整的配置,而不是依赖默认值,因为默认值通常不满足具体需求。
需要关心的第一件事是输入多路开关。把AIN0和AIN1配置成差分输入,用来接RTD的两端;剩下AIN2和AIN3可以留着做第二路温度采集,或者配置成断线检测。然后是PGA增益,刚才说了PT1000加250μA激励,PGA设成8倍比较合适。接着是基准源选择,比率式测量必须选择外部基准引脚,也就是REFP0/REFN0那组。
接下来是数据速率。ADS122U04支持多个数据速率档位,我实际用330SPS做动态响应快的场景,用20SPS做高精度平均场景。如果你主要测HVAC管道温度,温度变化本来就慢,建议直接选20SPS或50SPS,这样数字滤波对工频干扰的抑制效果最好。
寄存器里还要配置IDAC电流源:IDAC1输出接到RTD正端,IDAC2可以关掉,或者像我一样配置成互补输出,用于三线制RTD的引线电阻补偿。电流大小选250μA,既保证信号幅度够大,又不至于让PT1000自热超过0.01℃。
3.2 UART通信时序和命令设计
ADS122U04的UART协议不复杂,本质就是你给它发命令字节,它回状态和数据。初始化时我用MCUXpresso SDK里的UART驱动,配置成115200,8位数据,无校验,1位停止位。之后每次通信流程分三步:写配置寄存器、触发或等待数据就绪、读取转换结果。
下面是我实际项目里裁剪过的驱动代码,寄存器地址和命令码以TI官方手册为准,但调用框架可以直接参考:
static void ads122u04_write_reg(uint8_t reg, uint8_t val) { uint8_t cmd[3] = { 0x00, 0x00, 0x00 }; cmd[0] = ADS122U04_WRITE_REG | (reg << 1); cmd[1] = 0x00; cmd[2] = val; UART_WriteBlocking(UART1, cmd, 3); } static uint8_t ads122u04_read_reg(uint8_t reg) { uint8_t cmd[2] = { 0x00, 0x00 }; uint8_t val = 0; cmd[0] = ADS122U04_READ_REG | (reg << 1); cmd[1] = 0x00; UART_WriteBlocking(UART1, cmd, 2); UART_ReadBlocking(UART1, &val, 1); return val; } static int32_t ads122u04_read_data(void) { uint8_t buf[3] = { 0, 0, 0 }; int32_t raw = 0; UART_ReadBlocking(UART1, buf, 3); raw = ((int32_t)buf[0] << 16) | ((int32_t)buf[1] << 8) | buf[2]; if (raw & 0x800000) { raw |= 0xFF000000; /* 符号扩展,后文按偏移二进制处理则不需要 */ } return raw; }这段代码用SDK的阻塞式UART接口,原因是我们测温周期通常是秒级,完全不需要DMA和中断的复杂度。等数据转换完成再读,比一边采一边打断更稳定。
3.3 从原始码到电阻值的数学换算
ADS122U04输出的是24位数据,必须搞清楚它是不是二进制补码格式,我项目里用的是偏移二进制,也就是0x800000对应0V。这个细节直接决定你的温度显示是否正确,我最开始没有细看手册,结果0℃时读出来一个巨大的负数,排查了半天才发现是码制解释错了。
换算公式很简单。假设ADC读到的原始码为Code,满量程码为2^23-1,参考电压为VREF,PGA增益为Gain,则输入电压为:
Vin = (Code - Offset) × VREF / (2^23 × Gain)
又因为VREF = IIDAC × RREF,所以RTD电阻为:
Rtd = Vin / IIDAC = (Code - Offset) × RREF / (2^23 × Gain)
注意Offset是零输入时的码,理想情况等于0x800000。把这个公式写进固件后,建议先用一个精密电阻箱测几个已知阻值点,验证换算是否准确。比如0℃时PT1000应该是1000.00Ω,如果你读出来是999.72Ω,说明后面需要校准。
4. 温度换算:从电阻到摄氏度
4.1 RTD非线性补偿:Callendar-Van Dusen方程
RTD的电阻和温度并不是严格线性关系,PT1000在0℃附近大约是3.85Ω/℃,但到了200℃之后斜率会变化。工业标准常用的描述方式是Callendar-Van Dusen方程:
- 0℃以上:R(t) = R0 × (1 + A×t + B×t²)
- 0℃以下:R(t) = R0 × (1 + A×t + B×t² + C×(t-100)×t³)
其中R0是0℃时的电阻,PT1000就是1000Ω。A、B、C系数根据传感器标准选择,IEC 60751标准下A=3.9083×10⁻³,B=-5.775×10⁻⁷,C=-4.183×10⁻¹²。
如果把Rtd带进这个方程去反解温度,会涉及三次方程求根,固件里直接实现比较复杂。我的做法是分段逼近:在-40℃到150℃范围内,用C语言写一个二分法或者牛顿迭代法,误差控制在0.001℃以内。对MK22FN512VLH12来说,120MHz主频跑几次迭代也就是几十微秒的事,毫无压力。
4.2 用FPU和多项式优化计算速度
Cortex-M4F的FPU是单精度的,这带来一个陷阱:如果在代码里写double,编译器会调用软件浮点库,计算速度会慢几十倍。我们实际测试过,用double算一次牛顿迭代需要约十几微秒,用float只要几百纳秒。所以固件里面所有温度计算变量都用float,精度完全够。
为了进一步提速,我把CV方程直接改写成霍纳多项式形式,避免重复计算幂次:
float rtd_to_temp(float rtd_ohm) { float t = 0.0f; /* 先做线性猜测,再进行两次牛顿迭代 */ t = (rtd_ohm - 1000.0f) / 3.85f; for (int i = 0; i < 2; i++) { float r_calc = 1000.0f * (1.0f + A_F * t + B_F * t * t); float dr_dt = 1000.0f * (A_F + 2.0f * B_F * t); t = t - (r_calc - rtd_ohm) / dr_dt; } return t; }这个函数在release模式下编译后,MK22执行一次只需要几个微秒,完全可以做到每秒读取10个通道还富余大量CPU时间给PID控制。
4.3 两点校准:让误差从“可用”变成“可信”
哪怕你原理图设计再正确,只要Rref精度是0.1%,增益误差就至少是0.1%,对应PT1000大约0.26℃的误差,这还没算PGA失调和IDAC误差。所以产线上必须校准。
我用的方法是两点校准。准备两个精密电阻,一个接近量程下限,一个接近量程上限,比如800Ω和1600Ω。通过命令行或按键进入校准模式,分别记录ADC读到的Code1和Code2,计算出增益修正因子和偏置修正值,写进MK22的Flash末尾。之后每次上电,在温度换算前先把Code做一次线性修正。
实验数据能说明问题:未校准时,整条链路在100℃点的误差约0.4℃;校准之后,用六位半万用表比对,误差降到了±0.05℃以内。如果只做单点校准,会把PGA失调误当成斜率误差,效果远不如两点校准。
注意:校准电阻不能用电位器,要用Vishay或同类精密电阻,而且校准时的环境温度要尽量和实际工作环境一致,因为PGA失调会随温度漂移。精确的做法是每个温度区间做一套校准系数,但HVAC场景两点足够。
5. 工业和HVAC场景里最容易踩的坑
5.1 HVAC风机变频器带来的传导干扰
HVAC项目里最头疼的不是传感器的非线性,而是变频器。变频器在驱动风机时会产生几百kHz到几十MHz的开关噪声,这些噪声会通过电源线和接地系统传导到温度采集板上。有一次现场调试,AHU风机一启动,温度读数从26.3℃瞬间跳到29.8℃,后来又降到24.1℃,来回飘。
排查后发现问题是双重的:一是ADC的供电来自开关电源,纹波本身就有80mV;二是传感器线缆和变频器动力线走同一个线槽,共模噪声在长线上被放大。针对这两个问题,我在ADC电源前面加了一级LC滤波,电感和电容选型如下:4.7μH电感加10μF陶瓷电容,截止频率约23kHz,实测电源纹波降到了5mV以下。同时把传感器线缆换成双绞屏蔽线,屏蔽层只在控制柜侧单端接地,读数立刻稳定了。
5.2 工业现场的地电位差和隔离
工业环境里,很多传感器安装在几十米外的管道上,设备外壳通过不同路径接地,各个设备之间可能存在几伏甚至几十伏的地电位差。如果ADC和MCU直接共地,这个电位差会形成地环路,轻则读数不准,重则烧毁接口电路。
这也是当初我选ADS122U04的另一个原因:UART只有两根数据线,加数字隔离器非常方便。我的方案是:MK22这一侧属于主控板,电源由PLC侧提供;ADS122U04单独放在传感器端子板上,用隔离DCDC供电,UART信号穿过数字隔离器。两边严格不共地,地环路彻底断开。
如果现场没有隔离需求,也建议在UART_TX串一个330Ω电阻,在RX对地接一个5.1kΩ下拉电阻。这样万一ADC输出的电平状态异常或者线缆短路,不会直接把MCU的IO口打坏。
5.3 RTD断线检测和自诊断
温度传感器在现场用久了,断线是迟早的事。PT1000断线后,ADC输入悬空,读出来的Code通常会偏向满量程或者不定值,如果固件里没有检测逻辑,控制器就会把断线误判成超高温,触发机组停机。
最简单的检测方法是看电阻值是否超出合理范围。比如PT1000在-40℃时约842.7Ω,在150℃时约1573.3Ω,如果换算出来的电阻值小于800Ω或者大于1700Ω,可以直接判定为断线或短路。但这种方法在极端工况下可能会误判,更可靠的方法是用IDAC开路检测:断线时IDAC电流无法形成回路,ADC输入会被内部偏置网络拉到某个特定电压。
我实际用的逻辑是:
float rtd = raw_to_resistance(code); if (rtd < 700.0f || rtd > 1900.0f) { sensor_fault = SENSOR_FAULT_OPEN; } else if (rtd > R_MAX_THRESHOLD) { sensor_fault = SENSOR_FAULT_SHORT; }同时每个采样周期对状态做去抖处理,连续3次判定断线才上报故障,避免瞬时噪声导致误报警。这个逻辑在HAVC项目里挽救过两次半夜的电话骚扰,值得放进每个测温固件。
6. 实测数据、调优过程和我的经验总结
6.1 一套AHU系统上的实际测量结果
最后分享一组在真实AHU系统上的数据。传感器是四线制PT1000,ADS122U04配置为PGA=8、数据速率50SPS、IDAC=250μA,参考电阻2.048kΩ,经过两点校准后,与现场标准水银温湿度计对比:
| 被测点 | 传感器安装位置 | 本方案读数℃ | 标准表读数℃ | 偏差℃ |
|---|---|---|---|---|
| AHU新风 | 新风入口 | 28.42 | 28.45 | -0.03 |
| AHU送风 | 风机出口 | 34.18 | 34.22 | -0.04 |
| 冷冻水供 | 水管表面 | 11.56 | 11.60 | -0.04 |
| 冷冻水回 | 水管表面 | 12.83 | 12.86 | -0.03 |
这个精度对HVAC控制来说已经绰绰有余,甚至可以直接替代一部分仪表级温度变送器。在连续运行7天的压力测试中,最大累计偏差没有超过±0.08℃,长稳表现让我比较满意。
6.2 几个试过才知道的细节
第一,DATAREADY信号不要忽略。ADS122U04虽然能主动读取,但更稳的做法是接一个DRDY引脚到MK22的GPIO,等它拉低再读。我在第一版程序里全靠延时猜时间,结果每次读数之间偶尔会混入上一次的旧数据。后来还是老老实实加了握手GPIO,数据时序才真正对齐。
第二,参考电压旁路电容一定要给够。ADC的VREF引脚建议放10μF和0.1μF并联的电容,而且要靠近引脚放。我初版图省事只放了一个0.1μF,结果读数在最后两个bit上不停跳。换成10μF后,问题消失。
第三,如果用了UART转RS485再转回UART的链路,注意RS485收发器的方向切换。ADS122U04是主动输出数据的,单片机读取时会出现拉低方向控制线的时刻;处理不好会偶尔丢掉一字节,导致整包数据错位。解决方法是在读取每一个字节前先拉高RS485方向控制再拉低,或者直接用自动方向切换的收发器。
6.3 这套方案还能怎么扩展
目前这套采集链路至少能扩展到8路温度输入。ADS122U04是4通道MUX,一片不够就多挂一片,因为UART协议天然支持多点总线。只需要给每片ADC配置不同的地址,MK22用同一个UART轮询即可。我们后来在冷冻站项目里就用两片ADS122U04采集了7路PT1000加1路热电偶,一块MK22完全扛得住。
如果做电池供电的无线温度采集器,功耗也是可以压的。MK22进入VLPS低功耗模式,ADS122U04进入power-down模式,用RTC定时器每5秒唤醒一次,完成一次转换后立即休眠。实测整机平均电流能做到20μA左右,四节五号电池跑一个供暖季没有压力。
测到现在,我最真实的体会是:温度测量这个事,传感器和ADC的选型只决定天花板,地线处理和校准逻辑才决定你最终能不能拿到稳定的数据。ADS122U04IPWR这颗芯片把模拟前端做得很完整,MK22FN512VLH12提供了足够算力和外围资源,但真正让它能在工业现场站住脚的,是你在硬件布局、隔离、以及固件诊断上下的功夫。如果现在让我再重新选型,我依然会从“隔离成本低”和“校准可追溯”这两个角度出发,优先考虑这一套组合。