去年接一个中央空调恒温控制项目时,需求表面看起来挺简单:把风管和水管温度测准,PID 去推电动阀。真正动手才发现,工业现场想做到 0.1℃ 级别的长期稳定,靠 MCU 内置 ADC 加一颗 NTC 基本是做梦。最后落地的方案是 TI 的 ADS122U04IPWR 这颗 24 位 ΔΣ ADC,配上 Microchip 的 PIC24HJ256GP610 做采集和逻辑控制,一套链路同时覆盖车间管道和 HVAC 风管两类场景。这篇文章把选型逻辑、硬件电路、固件算法、PID 调节以及调试踩坑完整记下来,给准备做工业温度采集、楼宇自控或嵌入式仪表的朋友一份可以直接复现的参考。
先说明白,ADS122U04 这颗芯片和常见的 ADS1220 不一样,它是 UART 接口的 24 位 ADC,不是 SPI。很多兄弟拿 ADS1220 的驱动硬改,第一步就会卡住。整篇文章会围绕这颗 ADC 和 PIC24HJ256GP610 的实际配合讲,从画 PCB 到写代码再到现场调 PID,尽量把该填的坑都填上。
1. 先想清楚再选型:24位ADC + 16位MCU 的组合逻辑
1.1 为什么外挂 ADC,而不是直接读 MCU 内置采样
现在稍微好一点的 MCU 都内置了 12 位甚至 16 位 ADC,很多人第一反应是省一片芯片。但工业温度测量里,“分辨力”和“精度”是两回事。拿 Pt100 来说,0℃ 时电阻是 100Ω,温度每变化 1℃,电阻变化大约 0.385Ω。如果用 1mA 恒流激励,这个变化转换成电压只有 385μV。想在 0.1℃ 级别上读数稳定,ADC 至少要能分辨 40μV 左右的电压变化,这还没算噪声、漂移和引线压降。12 位 ADC 配上 3.3V 参考电压,一个 LSB 大约是 800μV,几毫伏的偏移就能让温度读数偏好几度,根本没法用。
还有更现实的问题:工业现场有压缩机启停、变频器、继电器等强干扰源,模拟信号很容易被拉偏。集成在 MCU 内部的 ADC,模拟前端通常没有独立的 PGA(可编程增益放大器),也没有专门给 RTD 设计的恒流激励源。你若想靠外部电阻搭恒流源,电流温漂和基准电压温漂叠加起来,标定都标不回来。
PIC24HJ256GP610 的内置 ADC 并不是不能用,我把它留给辅助任务:监测电源电压、粗略看环境温度、做数字 I/O 诊断。真正的温度和 PID 闭环控制数据,全部走外挂 ADS122U04。
1.2 ADS122U04 的核心参数与选型注意点
ADS122U04IPWR 是 TI 推出的一款面向传感器发送器的 24 位 ΔΣ ADC,TSSOP-16 封装,工作温度范围覆盖工业级。几个关键特性足够撑起一套测温系统:
- 4 个模拟输入通道,可以组 2 路差动或 4 路单端,分时测量多个传感器的基本盘够了。
- 内置 PGA,增益从 1 到 128 可调,热电偶微伏级信号也可以直接对接。
- 内置两个可配置 IDAC 恒流源,专门为 RTD 激励设计,省掉外部精密恒流源电路。
- 内置 2.048V 参考电压,要求更高时也可以切换到外部基准。
- 内置温度传感器,热电偶冷端补偿可以先用它顶上。
- 通信接口是 UART,不是 SPI/I2C。主机通过地址字节加命令帧的方式访问。
选型时真正需要注意的反而是这个 UART 接口。好处是两线制远距离传输比 SPI 稳,线长了不容易受到时钟串扰;坏处是帧解析要比 SPI 麻烦一些,必须做超时重发和校验。我的建议是 MCU 的 UART 用 9600bps、8N1,开启地址字节匹配和帧校验位,具体寄存器和波形以数据手册为准。这个速度虽然不快,但对温度采集 10-20SPS 的更新率绰绰有余,而且低波特率在干扰环境下的误码率更低。
1.3 PIC24HJ256GP610 在系统里干什么活
选择 PIC24HJ256GP610,原因也比较直接。它是 Microchip 的 16 位 MCU,主频能到 40 MIPS,跑 PID、查表、滤波这些计算非常轻松;256KB Flash 对于存校准参数和多组传感器标定表也够用;还有硬件 I2C/SPI/UART 和 DMA 控制器,带 Modbus 从站时硬件 CRC 还能省 CPU。I/O 支持 5V 容忍,和 24V 工业信号电路做电平转换时不用加一堆次级缓冲。
在这套系统里,它的分工很明确:通过 UART 周期读取 ADS122U04 的转换结果,跑数字滤波和传感器线性化算法,得出温度值以后送给 PID 控制器,PID 输出驱动 PWM 或阀门执行器,同时将温度值用 Modbus RTU 发到上位机。ADS122U04 专注做模拟信号采集,PIC24HJ 专注做逻辑、通信和控制,各管一摊,调试和排障都清晰。
2. 硬件设计实操:把微伏级信号安安全全送进ADC
2.1 供电、基准和PGA设置为什么决定成败
ADS122U04 的 AVDD 我建议单独用低噪声 LDO 供电,不要在数字电源上直接就取。工业现场的大电流设备启动瞬间,电源线上会有很大的毛刺,如果模拟电源和数字电源混在一起,24 位 ADC 的量化精度再高都会被电源噪声毁掉。常见的做法是一颗 LDO 给 MCU 数字部分,另一颗 LDO 给 ADC 模拟部分,ADC 的 AVDD 引脚加 0.1μF 陶瓷电容和 10μF 钽电容并联去耦,电容尽量贴近芯片电源引脚。
参考电压的选择要看精度需求。内置 2.048V 基准的好处是省事,温漂在几十 ppm/℃ 级别,对 HVAC 里 0.2℃ 精度已经够用。如果要做计量级仪表,就外接 REF5025 这类低温漂基准,再把基准电压接到 ADC 的 REF 引脚。外接基准时要注意:基准芯片的输出噪声要低,因为在 24 位级别下,参考电压上的噪声会直接变成测量误差的一部分。它的原理和你用一把不准的尺子量物体一样,尺子本身热胀冷缩,量出来的数自然不准。
PGA 增益的设置需要和传感器输出范围匹配。RTD 信号经过恒流源激励后通常是几十毫伏到几百毫伏,PGA 选 1 或 2 就合适;热电偶信号只有几毫伏甚至几百微伏,必须把 PGA 打开到 32 或 64 甚至 128,才能把小信号抬到 ADC 满量程附近。一个通用的判断标准是:让最大期望信号对应到 ADC 满量程的 50% 到 90%,这样既能充分利用分辨率,又不会因为过载导致饱和失真。
2.2 RTD、热电偶、NTC 三种植感器接线方案
RTD 的测量一定要用比例法,这是整套电路的精髓。我用 ADS122U04 的两个 IDAC 恒流源去激励 RTD 和一个精密参考电阻 RREF,ADC 分别测量 RTD 上的电压和 RREF 上的电压。因为流过 RTD 和 RREF 的是同一个电流源,电流的任何漂移都会同时影响两个电压,最后做除法就把电流误差抵消掉了。这就像两个人用同一根被拉伸过的皮尺分别量东西,皮尺不准没关系,两者的比例是准的。
三线制 Pt100 的接线要这样处理:IDAC1 电流从 RTD 的一端进去,RTD 另一端出来经过引线到地;IDAC2 用于抵消那条引线的压降,第三条线送回到 ADC 的测量地。双 IDAC 配合得当,导线电阻的影响可以压到很低。若是两线制接法,几十米导线的电阻直接串联进 RTD,读数会系统性偏高,HVAC 风管里不推荐。
热电偶的接线相对简单,差分输入直接接热电偶正负极,但冷端补偿绕不开。T 型或 K 型热电偶的塞贝克系数大约在 40-50μV/℃,如果冷端温度变化 1℃ 而不做补偿,温度读数就会直接误差 1℃,这比 ADC 本身的误差大得多。我用 ADS122U04 内置温度传感器读取冷端温度,简单场景够用;遇到冷端靠近热源的场合,就换成外置 NTC 贴在接线端子上,再按 NTC 的标定曲线算出冷端温度。
NTC 热敏电阻在 HVAC 里也常见,贴片 NTC 的计算建议直接用 B 值公式:
T = 1 / (1/T0 + (1/B) * ln(R/R0)) - 273.15其中 T0 通常取 25℃,R0 是 25℃ 时的标称阻值,B 值由厂家给出。要注意的是 B 值公式在中温段精度尚可,温度范围拉宽以后误差变大,要求高的还是用 Steinhart-Hart 方程:
1/T = A + B*ln(R) + C*(ln(R))^3三种系数可以通过厂商表格反推,写进单片机查表也行。
2.3 布局布线防干扰的几条铁律
PCB 布局是很多项目翻车的地方。我总结了几条直接能用的经验,都是实际项目中踩出来的:
- 模拟地和数字地要单点连接,ADC 芯片下方的地平面要连续,不要让数字信号线横穿模拟区域。
- UART 通信线和模拟输入线距离至少保持 5 倍线宽,能包地就包地,信号线别走长平行线。
- 传感器信号线进板子以后,先经过 RC 滤波再进 ADC。一阶 RC 的截止频率放在几十赫兹,对工频干扰和射频干扰都有衰减。
- AD 芯片的模拟输入脚旁边放 1nF 到 10nF 的共模电容,差分输入两脚对称放置,不然共模转差模噪声会很严重。
- 屏蔽电缆在传感器端单端接地,不要在两端都接,避免形成地环路电流。HVAC 现场变频器干扰大时,屏蔽层接控制柜的地,效果比浮空好。
- IDAC 激励电流走线要短而粗,电流回路面积尽量小,否则激励电流的噪声会直接叠加到被测电压上。
3. 固件实现:寄存器配置、RTD比例测量与断线诊断
3.1 UART通信时序和寄存器配置流程
ADS122U04 的数据手册阅读重点不在“怎么读出数据”,而在“帧格式”。MCU 向 ADC 发命令时,先发地址字节选中 ADC,再发命令字节,需要写配置时再带上寄存器值和校验字节。我的固件流程如下:
- UART 初始化:9600bps,8N1,接收中断启用。
- 发送地址字节 + RREG 命令,读取设备 ID 或寄存器内容,确认通信链路正常。
- 写配置寄存器组:设置输入多路开关、PGA 增益、数据速率、转换模式、IDAC 电流值、IDAC 输出引脚。
- 发送 START 命令启动连续转换。
- 在中断中接收转换结果,一般读取 3 个字节拼成 24 位有符号数,再做后续算法。
伪代码值得放在这里:
static const uint8_t ADDR_ADC = 0x42; // 按手册定义 static const uint8_t CMD_RDATA = 0x10; // 读转换数据命令 int32_t ads122u04_read_raw(void) { uint8_t tx[3] = { ADDR_ADC, CMD_RDATA, 0x00 }; uint8_t rx[3] = { 0 }; send_uart_bytes(tx, 3); if (receive_uart_bytes(rx, 3, 100) != 3) { return INT32_MIN; // 超时错误标志 } int32_t raw = ((int32_t)rx[0] << 16) | ((int32_t)rx[1] << 8) | rx[2]; return raw; }注意数据手册里过采样数据和通道 ID 的摆放位置略有差异,代码要在拿到芯片后先用一个固定电压验证字节序。这里我写的是通用流程,具体寄存器地址各位含义,建议打开数据手册对照。
3.2 比例测量:RTD温度还原算法
有了原始码值,接下来是核心计算。比例测量的式子很短:
R_RTD = (code_rtd / code_ref) * R_REFcode_rtd 是 RTD 通道的 ADC 原始码,code_ref 是参考电阻通道的原始码,R_REF 是参考电阻的标称值。只要两个通道用的是同一个 IDAC 电流,电流变化就会在分子和分母里抵消,这个式子在很宽的温度范围内都成立。
得到 R_RTD 之后,查 Pt1000 温度对照表做线性插值,是最稳妥的工程做法,因为 IEC 60751 的标准表数据可以直接固化在 Flash 里。我建议表保留 1℃ 间隔,用二分查找快速定位区间,最后做线性插值,速度足够快,误差也远小于传感器本身的偏差。查表函数写成这样:
typedef struct { float r_low; float r_high; float t_low; float t_high; } PtEntry; float pt1000_lut_interp(float res) { // 二分法找到 res 所在区间 // 返回 t_low + (res - r_low) / (r_high - r_low) * (t_high - t_low) }热词里有人搜 “pt1000温度对照表”,实际上表不用自己算,IEC 标准表网上有现成 CSV,导入脚本生成头文件即可。千万别手敲,容易出错。
3.3 冷端补偿与NTC计算
热电偶的冷端补偿在固件里的写法也不复杂。先通过内置温度传感器或外部 NTC 得到冷端温度 T_cold,再查找热电偶分度表,得到 T_cold 对应的热电势 E_cold,加上实际测得的热电势 E_meas,得到修正后的热电势 E_total = E_meas + E_cold,再用 E_total 查表得到真实温度。这个查表过程要调用两次,一次查冷端电势,一次查总电势,不过查表本身很便宜,耗时可以忽略。
如果外部冷端用 NTC,则先按 3.2 节的反向公式把电阻换算成温度。贴片 NTC 的 B 值参数通常标注在封装资料里,比如 10kΩ 3950B,意思是 25℃ 标称 10kΩ,B 值是 3950K。换算后要注意施加电压不能过大,自热效应会让读数偏高,建议激励电流设计在 100μA 以下。
3.4 断线、短路检测与软件校准
工业应用必须做故障诊断,不能传感器断了还在输出离谱温度。ADS122U04 支持在输入端接上拉或下拉电阻,断线时 ADC 会读到接近正满量程或负满量程的码值。我在每个模拟输入通道对 AGND 接 10MΩ 下拉电阻,软件里设置断线阈值:
- 原始码接近正满量程,判断为开路。
- 原始码接近 0 且长时间不动,判断为短路。
- 连续 5 次采样都在阈值以上,才输出报警,防止瞬间脉冲误触发。
软件校准也很重要。两块板子即使器件同批次,电阻公差和基准电压差异也会让读数有几十分之一度的偏差。我在生产测试流程里让设备分别进入冰水槽和 35℃ 恒温槽,记录两点误差,然后线性校正:gain_cal = (T2 - T1) / (raw2 - raw1),offset_cal = T1 - gain_cal * raw1。校准后的值写进 Flash,设备后续重启直接装载。
4. 从温度数值到PID控制:波动大怎么查、怎么调
4.1 先查测量链路还是先调PID
很多同行一看到 “plc温度pid波动温差大如何调节” 就直接查参数表,把 P 调小、I 调小,折腾半天还是抖。我的经验是:先确认你看到的温度波动是真实温度波动,还是测量系统自己引入的噪声。判断方法很简单——用一个高精度电阻箱模拟传感器,设置在同一温度点连续记录 10 分钟。如果电阻箱恒定时,系统输出的温度仍然来回跳,那是测量链路的问题;如果读数稳定,再谈 PID 参数。
在实际项目里,我碰到过好几回 ADC 输出噪声和 PID 振荡叠加在一起,导致阀门像抽风一样来回动。温度测量每 100ms 做一次,PID 输出却从 30% 跳到 70%,阀杆都被磨出异响。解决方案先做软件滤波:对 ADC 读数做一阶低通滤波,比如 EMA 滤波:
flt = alpha * raw + (1 - alpha) * flt_prev;alpha 取 0.2 到 0.3,采样周期 100ms 时,等效时间常数约 300-500ms。这样滤掉高频抖动,又不会滞后太多。滤完再进 PID,微分项的噪声也会明显小。
4.2 滤波窗口、PID参数与抗积分饱和
温度系统的惯性很大,纯比例控制一定会留静差,所以积分项不可少。但积分项用不好就会过冲和振荡。我推荐增量式 PID,输出变化量直接控制阀位增量:
typedef struct { float kp, ki, kd; float e_prev, e_prev2; float out_min, out_max; } IncPid; float inc_pid_update(IncPid *p, float setpoint, float meas) { float e = setpoint - meas; float output = p->kp * (e - p->e_prev) + p->ki * e + p->kd * (e - 2.0f * p->e_prev + p->e_prev2); p->e_prev2 = p->e_prev; p->e_prev = e; return output; }增量式的好处是误动作小,输出是增量,系统天然有记忆性。再加上积分分离,偏差大于 5℃ 时把积分清零,等到偏差小下来再积分,可以避免大偏差时积分饱和。输出限幅同样别忘,我一般在 10% 到 90% 之间。
初始参数建议这样给:采样周期 T=500ms,P=5~10,I=0.1~0.5,D=0。温度过程响应慢,D 初始必须为 0,因为微分作用会放大测量噪声,等确认测量链路已经很干净了,再一点点加 D。加 D 的量可以这样判断:设定值突变时,若输出出现高频毛刺,说明 D 过大,需要减小。
4.3 简单可靠的上位机可视化
热词里提到“省份温度可视化”“温度可视化”,大数据展示和我们现场调试不是一回事。现场调试阶段,我习惯让 PIC24HJ 通过 UART 每 100ms 输出一行字符串:“TEMP:23.45”。然后用 Python 写个小脚本沉帧绘图,源码很短:
import serial import matplotlib.pyplot as plt ser = serial.Serial('COM3', 9600, timeout=1) temps = [] for line in ser: line = line.decode().strip() if line.startswith('TEMP:'): temp = float(line.split(':')[1]) temps.append(temp) plt.clf() plt.plot(temps[-200:]) plt.pause(0.05)这个方法比任何上位机软件都直接,能快速判断测量链路和 PID 响应。如果对接的是 PLC/DCS 系统,则用 Modbus RTU 更合适,PIC24HJ 做从站,把温度值挂到保持寄存器,上位机轮询读取。Modbus 里的输入寄存器建议映射成 0.01℃ 为单位的有符号整数,这样上位机不用浮点解析。
5. 调试实录:我踩过的坑和排查速查表
5.1 故障排查速查表
把现场最常遇到的问题做成了表格,方便直接对照:
| 症状 | 可能原因 | 排查手段 |
|---|---|---|
| 温度读数快速跳动 ±0.5℃ | 屏蔽层接地不良、PGA 增益过高、采样速率过快 | 检查屏蔽层单端接地;降低数据速率;增强 EMA 滤波 |
| 温度读数缓慢漂移,今天高明天低 | 参考电阻温漂、AVDD 电压随负载变化、电路板受热 | 用低温漂参考电阻;检查 LDO 输出稳定性;热源的 PCB 区域远离模拟前端 |
| 偶发满量程跳变,出现 -999℃ | 传感器引线接触不良、IDAC 断线、外部强脉冲干扰 | 检查连接器压接;开启断线诊断;在输入端加 TVS 管 |
| 长时间运行后读数卡死 | UART 通信异常、命令帧错位 | 增加看门狗复位;开启帧校验;命令超时后重发 |
| 多块板子装上去偏差不一致 | 传感器探头位置不同、未做两点校准 | 统一探头安装深度;生产线下线和上线两轮校准 |
| 温度响应慢,PID 调不上去 | 滤波时间常数过大、采样周期过长 | 把 EMA 的 alpha 调大;采样周期缩到 200ms |
5.2 三个实测小技巧
最后分享三个常规文档里不会写的经验。
第一个是上电后不要立刻取数。ADS122U04 和外部电路上电瞬间会有建立过程,我给系统加了 2 秒预热时间,预热期间只读数据不更新温度值。虽然浪费了两秒,但换来的是启动阶段不出现虚假高温跳变,对 PID 系统来说这很重要,因为阀门不会因为启动假读数猛开。
第二个是标定过程一定要把端子排和连接器带入校准回路。之前我直接在 PCB 测试点校准,装到现场接上延长线后读数整体偏了 0.3℃,就是因为端子接触电阻和线阻进入测量回路。正确的做法是从端子排进线口开始灌电流,把整个通道的误差都标进去。
第三个是在 PID 输出端加死区。温度控制到设定值附近后,微小偏差会让执行机构频繁动作,阀门寿命会受很大影响。我在输出环节加了一个 ±0.3℃ 的死区:偏差小于这个范围,输出保持不动。实测下来,电动阀动作次数从每小时几十次降到几次,控温精度并没有明显变差,HVAC 系统里这种“故意不响应”带来的收益远大于损失。
这套 ADS122U04 + PIC24HJ 的组合目前已跑了三个采暖季,温度稳定性依然能保持在 0.2℃ 以内。中间换过传感器、改动过 PID 参数,但电路和固件框架一直没动,说明当初的冗余设计和算法选择是对的。如果后面有朋友做类似方案,建议先把电阻箱校准环境准备好,再上手调 PID,顺序别反。遇到具体问题,欢迎带着波形图一起聊。