做温度采集这些年,我见过不少同行在第一版就栽在同一个地方:直接用 MCU 内部 ADC 去采传感器,分辨率不够、温漂压不住、校准没两天又飘回去,项目只能返工。今天想聊的是另一条路——拿 ADS122U04IPWR 这种 24 位 Δ-Σ ADC 当模拟前端,配 PIC18F86J15 当控制器,专做工业和 HVAC 应用里的温度测量。这套组合能覆盖 PT100/PT1000、K 型热电偶、NTC 几种最常见的探头,做到 0.1℃ 级别的稳定测量并不是难事。如果你正在选平台、画原理图、写驱动,或者被现场温漂问题折腾得头大,这篇文章应该能帮你少走不少弯路。
1. 为什么偏偏是"24 位 ADC + 8 位 MCU"这个组合
1.1 先看需求:工业与 HVAC 测温到底难在哪
很多人觉得测温就是"读个数",接个热敏电阻分压、单片机 AD 采一下不就完了?放在空调面板这种场合确实够用,但放到工业和 HVAC 现场,问题马上不一样。
HVAC 场景里,冷冻水供回水温差测量要求两个通道的一致性非常高,温差 0.1℃ 的变化就能影响整个控制逻辑。风管温度虽然量程窄,但传感器电缆可能从控制箱拉到几十米外的空调机组,一路上的工频干扰、变频器谐波全都耦合进信号线。工业场景更苛刻,有的测点要求连续运行几年不校准,环境温度能从 -40℃ 到 85℃,ADC 的基准电压温漂、PGA 失调温漂会被直接折算成测量误差。
MCU 内部 ADC 的问题不是位数不够,而是参考电压和增益级都集成在 MCU 里,温漂特性没法单独优化,而且量程、偏置都跟着电源电压走。这时候把"模拟测量"和"数字处理"拆开,用一颗专用测量 ADC 做前端,MCU 只负责协议和逻辑,反而是更稳妥的架构。
1.2 ADS122U04IPWR 的优势:独走 UART 的高精度 ADC
ADS122U04IPWR 这颗料我第一眼看中的不是 24 位分辨率,而是它的"UART 接口"这个差异化设计。常见的高精度 ADC 像 ADS1220、ADS1248 都是 SPI 接口,SPI 在板内没问题,但如果你想把 ADC 和 MCU 拉开距离、或者想让多个节点挂在同一条总线上,SPI 的时钟线就成了麻烦事。UART 版只要两根线,TXD/RXD 就能跑,主机侧还不用纠结时钟极性和相位。
这颗芯片内部集成了 4 通道输入 MUX、可编程增益放大器 PGA(1~128 倍)、内部基准源、内部振荡器,还带了两个匹配的 IDAC 恒流源。这意味着做 RTD 测量时基本不用外扩精密恒流源电路,做热电偶时内置温度传感器还能直接做冷端补偿。电源范围 2.3V 到 5.5V,输出数据速率可以到 1000 SPS,覆盖工业测温完全够用。
我选它还有一个现实原因:TSSOP-16 封装,手工焊接和回流焊都友好,IPWR 后缀是卷带包装,小批量打样也容易买到。
1.3 PIC18F86J15:够用、耐造、供应链稳的"下位机"
PIC18F86J15 属于 Microchip 的 J 系列,80 引脚封装,程序空间在几十 KB 级别,RAM 也有数 KB,跑 Modbus RTU、维护一张温度线性化查表,绰绰有余。这还不是重点,重点在它的外设配置:好多个 EUSART 可用,其中一路专门跟 ADS122U04IPWR 通信,另一路做 RS-485 跟 PLC 或楼宇自控系统对接,SPI、I2C 也都有,后面要挂数码管、LCD 或者 EEPROM 都方便。
有人问我为什么不上 32 位 MCU,成本、功耗、供应链稳定性都要算账。HVAC 控制器常年 24V 直流供电,整板功耗按毫瓦级控制,8 位 MCU 在轻度负载下电流只有几毫安,而且这颗料工业级温度范围、市场流通量大、替代渠道多,备胎好找。对做产品的人来说,"不容易断供"本身就是巨大的优势。
还有一个很容易被忽略的点:ADS122U04IPWR 和 PIC18F86J15 都是 3.3V 供电,IO 电平直接兼容,中间不需要电平转换芯片,省两颗料、少一层风险。
2. 模拟前端与传感器接口:RTD、热电偶、NTC 三路怎么接
2.1 RTD 的比例测量:用 1mA 激励和 RREF 消灭温漂
RTD 是最适合 ADS122U04IPWR 发挥的传感器。PT100 在 0℃ 时电阻 100Ω,到 100℃ 大约 138.5Ω,变化率约 0.385Ω/℃。如果用普通恒压源激励再测电压,激励源的稳定性会直接变成测量误差;如果用恒流源激励,电流源的温漂也必须做得很低。
ADS122U04IPWR 提供了一个更聪明的做法:比例测量。让 IDAC 恒流源同时流过 RTD 和一个精密参考电阻 RREF,RTD 上的电压接到 ADC 的模拟输入端,RREF 上的电压接到 ADC 的基准输入端。这样换算温度时用的是两个电压的比值,激励电流的变化在比值里被约掉了。
我常用的配置是:IDAC 设 1mA,RREF 选 2kΩ 精密电阻(温漂 10ppm/℃ 以内的),RTD 用三线制接法。之所以用三线制,是因为 RTD 本体在设备端,导线可能有几米长,两根引线电阻不对称会在测量回路里引入误差,三线制配合 IDAC 的差分输入能把引线电阻的影响抵消大半。四线制当然更好,但布线成本高,HVAC 场景一般用不到。
VREF = 1mA × 2kΩ = 2V,PT100 在 100℃ 时电压也就 0.1385V,PGA 设 8 倍后仍有很大裕量,24 位下分辨率做进 0.001℃ 级别是常态。
2.2 K 型热电偶与小信号放大:PGA 增益和冷端补偿
热电偶看着简单,实际最容易翻车。K 型热电偶的塞贝克系数约 41μV/℃,在 0~1000℃ 量程下满量程输出也就 40mV 上下。这么小的信号如果不放大,ADC 的低位码值基本被噪声淹没。
这里 PGA 就派上用场了。我一般把 PGA 设成 32 或 64 倍,视量程而定。但放大倍数上去了,失调电压也跟着放大,所以零点校准必须做。另外热电偶测量的是"热端和冷端的温差",冷端(也就是接线端子那一边)必须做补偿,否则夏天和冬天读同一根炉子上的热电偶,会差出十几度。
冷端补偿怎么做?ADS122U04IPWR 内部自带温度传感器,先把内部温度读出来,当作冷端温度;再用 K 型热电偶的标准多项式或者查表,算出冷端温度对应的热电动势,加到实测电动势上,再反查温度。这个方法成本最低,精度取决于内部温度传感器的误差,通常在 ±1℃ 以内。要求高一点的话,可以在端子排附近贴一颗高精度 NTC,用外部温度传感器通道做冷端感知。
2.3 NTC 与保护电路设计
NTC 是 HVAC 里绕不开的存在,风管温度、回风温度、面板温度到处都是 10kΩ NTC。NTC 的好处是灵敏度高、价格极低,坏处是非线性极强、互换性差。我用 ADS122U04IPWR 接 NTC 时一般不自找麻烦去用 IDAC,直接用一个 0.1% 的 10kΩ 精密电阻分压,把分压点接到 ADC 输入,24 位分辨率对 NTC 这种大信号来说绰绰有余。
保护电路必须给,尤其是传感器从现场拉回来那种。我在每个输入端到地并一个 TVS 管,再串 1kΩ 到 10kΩ 的限流电阻,之后接一个 100nF 到 1μF 的差分电容做 RF 滤波。这里有个细节:RC 滤波器的电阻值太大会跟输入漏电流相互作用,产生固定的偏置误差。ADS122U04IPWR 的输入漏电流在皮安级,几千欧的串联电阻没问题,但如果是廉价的运放做前端就要小心了。
3. UART 链路与寄存器配置:让 ADS122U04IPWR 稳定出数
3.1 硬件连线上容易被低估的细节
ADS122U04IPWR 走 UART 和 MCU 通信,硬件上只需要连接 TX/RX 两根线,外加共地。但有一个坑:UART 是异步通信,两边波特率必须一致,而且这个 ADC 的波特率由器件自身的振荡器分频产生,不是随便配的。我强烈建议你先去手册里查波特率配置表,选择一个两边都容易达到的速率,比如 9600 或者 115200,然后 MCU 侧直接用内部振荡器校准到同一频率。
另一个要注意的是,ADS122U04 支持多地址模式,可以通过地址位区分命令帧和数据帧。如果总线上只挂一颗 ADC,地址功能可以简化关闭,但如果将来要挂多颗,从一开始就把地址位处理逻辑写好,后面升级不用改太多代码。
供电方面,AVDD 和 DVDD 我用同一个 3.3V,但中间串了磁珠隔离,模拟部分和数字部分的地去耦电容分开布。这个细节在读数稳定性的作用远比你想象的大。
3.2 寄存器配置顺序与代码骨架
我习惯的上电流程是这样:MCU 先初始化 UART,拉硬复位或发送复位命令,等 ADC 完成上电稳定(一般要几十毫秒),然后依次配置配置寄存器,最后启动连续转换。
伪代码大概是:
// PIC18 EUSART 初始化 void uart_init(void) { // 设置波特率 SPBRG // 使能发送、接收、中断 TXSTA = 0x24; // 8位模式, 使能发送 RCSTA = 0x90; // 串口使能, 8位接收 SPBRG = ...; // 根据振荡器频率计算 } // 写 ADC 配置寄存器 void ads122u04_write_reg(uint8_t reg, uint8_t value) { uart_send_cmd(CMD_WREG); // 写寄存器命令 uart_send_byte(reg); // 寄存器地址 uart_send_byte(value); // 寄存器内容 delay_ms(1); // 等待命令执行 } void ads122u04_init(void) { // 复位 uart_send_cmd(CMD_RESET); delay_ms(50); // 配置输入MUX、PGA增益 ads122u04_write_reg(REG_CONFIG0, 0b10000000); // 配置数据速率、滤波 ads122u04_write_reg(REG_CONFIG1, ...); // 配置IDAC电流源 ads122u04_write_reg(REG_CONFIG2, ...); // 启动连续转换 uart_send_cmd(CMD_START); }这里有个重要事项:PGA 增益和输入通道的 MUX 切换会影响输入的建立时间,增益越高,需要的稳定时间越长。我习惯在通道切换或者增益切换后,主动丢弃头两三个转换结果,再取后续数据。这个"丢点"操作是很多第一次用高精度 ADC 的人漏掉的。
3.3 读数据帧与 PIC18 侧的解析状态机
读结果时,我推荐在 PIC18 侧用一个简单的状态机来解析 UART 字节流,不要在主循环里傻等。ADS122U04IPWR 的输出帧有固定格式,通常包含状态字节和 24 位转换结果。状态字节里的 ERR 位、PDR 位一定要检查,尤其是刚上电或者寄存器配置错误时,数据可能根本无效。
uint8_t rx_state = 0; uint8_t rx_buf[4]; int32_t raw_code; void uart_rx_isr(void) { uint8_t byte = RCREG; switch(rx_state) { case 0: rx_buf[0] = byte; rx_state = 1; break; case 1: rx_buf[1] = byte; rx_state = 2; break; case 2: rx_buf[2] = byte; rx_state = 3; break; case 3: rx_buf[3] = byte; raw_code = ((int32_t)rx_buf[1] << 16) | ((uint32_t)rx_buf[2] << 8) | rx_buf[3]; // 检查 rx_buf[0] 的状态位 new_data_ready = 1; rx_state = 0; break; default: rx_state = 0; } }PIC18 的硬件 UART 中断里做这种轻量级状态机完全没压力。主循环里只需要轮询标志位,拿到原始码之后再做处理,处理完记得清标志。
4. 从原始码到摄氏度:线性化查表与两点校准
4.1 24 位码值如何换算成工程单位
ADC 输出的原始码是一个 24 位的有符号数,范围大约是 -8388608 到 8388607。要把这个码变成电压,公式是:
V = raw_code / 8388608 × VREF / Gain
注意这里的 VREF 是我们外部接的 RREF 上的电压,不是 2.048V 内部基准。比例测量的好处前面说了,这里再强调一遍:整个链路里唯一要求高精度的元件是 RREF,只要它稳,其他电源、基准、电流源的小漂移都影响不大。
换算成电压后,RTD 的电阻就是 R = V_RTD / I_IDAC。但因为比例测量的关系,也可以直接写成 R = raw_code / 8388608 × RREF / Gain,这比先算电压再算电阻更直接,也少一道舍入误差。
4.2 PT100 的线性化:查表插值 + Callendar-Van Dusen 方程
PT100 的电阻-温度关系不是线性的,0℃ 到 100℃ 之间用一次函数会产生 0.4℃ 以上的误差,必须补偿。补偿有两个常用办法。
第一种,查表线性插值。把 IEC 60751 标准温标的电阻-温度表按 10℃ 间隔存成一个数组,得到电阻后二分查找落在哪两个表项之间,再线性插值。10℃ 间隔下插值误差小于 0.01℃,在 8 位 MCU 上算起来也快,就是几十个常数的查表,几毫秒内能完成。
第二种,用 Callendar-Van Dusen 方程直接算。0℃ 以上:
R(t) = R0 × (1 + A×t + B×t²)
其中 A = 3.9083×10⁻³,B = -5.775×10⁻⁷(IEC 60751 系数),R0 是 0℃ 时电阻 100Ω。反过来由电阻求温度需要解二次方程,PIC18 上跑浮点库有点吃力,我的做法是先用几轮牛顿迭代或者直接套求根公式算个近似值,精度足够。0℃ 以下那个带三次项的完整公式,只在冷藏、冷冻应用里才需要。
4.3 两个校准点:零点漂移和增益误差怎么一起消掉
不管芯片出厂特性多好,系统级总会引入偏置:PCB 走线电阻、放大器失调、串联保护电阻、基准电阻误差,这些会叠成系统误差。我每次做批量产品都坚持两点校准,一点都不省。
校准时用精密电阻箱代替传感器,比用冰水混合物更可控。先给一个 100Ω(对应 0℃),记下 ADC 原始码 offset_code;再给一个 138.5Ω(对应 100℃),记下另一个码 span_code。然后算两个系数:
温度_raw = (code - offset_code) × 100 / (span_code - offset_code)
这样零点漂移和增益误差一起就消掉了。校准的合理性在于:ADC 链路本质是线性的,只要 ADC 的传递函数是直线,两点就能确定这条线。实际操作中我会把校准系数存到 EEPROM 里,而且存两副本加校验位,防止写一半掉电把系数搞坏。
4.4 滤波与平滑:在 8 位 MCU 上怎么省钱
ADS122U04IPWR 内部已经有数字滤波,输出数据本身很干净,但现场总有不听话的干扰,比如变频器的共模噪声耦合进来。我在 MCU 侧加了两级平滑:先做一个 4 次滑动平均,把突发的毛刺磨平,再做一个一阶低通,也就是 y = y_old + (x - y_old) / N,N 取 4 或 8。
两个滤波的代价都不高,全部用整数移位完成,不碰浮点。注意滑动平均数组里的数据宽度要用 32 位,不然累加会溢出。我当初用 16 位变量存平均值,读出的温度在高干扰环境下偶尔跳一个很大的值,排查了半天才发现是溢出。
5. 实测中踩过的坑:建立时间、PCB 漏电与 ESD 反杀
5.1 通道切换后的"脏读数"
我把四路输入接成两路 RTD、一路热电偶、一路 NTC,调试时发现每次切完通道,前两个读数都偏大,要等三四拍才恢复正常。一开始怀疑是传感器的问题,后来定位到是 ADC 内部输入电容和 PGA 的建立时间:MUX 切换瞬间,采样电容里的残留电荷要经过输入源内阻放电,内阻越大,建立时间越长。
解决办法有两个方向。硬件上降低源阻抗,RTD 和 NTC 的串联电阻别乱加,必要的时候把 1kΩ 保护电阻降到 470Ω。软件上更直接:MUX 或 PGA 配置变了之后,先执行一次无用的转换,丢弃结果,再开始采。这个方法成本为零,强烈推荐。
5.2 助焊剂残留和潮湿环境带来的漏电漂移
这块板子我在实验室跑了两周,数据非常好,结果拿到现场一个月后,有个通道的温度读数缓慢漂了 0.3℃。百思不得其解,后来拆开板子看到 ADC 引脚附近的焊盘有点发暗,拿洗板水洗完再测,漂移消失。
高精度 ADC 最怕的不是噪声,是漏电。TSSOP-16 引脚间距本来就小,助焊剂没有洗干净的话,在潮湿环境下会形成微小的漏电通路,几个兆欧的漏电电阻就能把纳安级别的输入电流污染掉。从此我的规矩是:凡是做高精度采集的板子,回流焊后必须洗板,然后有条件就涂三防漆。三防漆对焊盘的保护是长期的,尤其 HVAC 设备装在管道井、机房这类高湿环境里,这一步不能省。
5.3 ESD 打坏 ADC:HVAC 现场线的防护
还有一次更惨,调试现场用了一根十几米的传感器延长线,正好旁边走过一条动力电缆,结果某天 ADC 的输入通道电压测出来完全不对了,芯片内部寄存器读出来全是乱码,基本可以断定是 ESD 打穿了内部保护二极管。
温度传感器线在 HVAC 机房里的长度动辄几十米,而且和强电电缆走同一个桥架,感应雷、动力柜合闸瞬间的浪涌都能进来。我的防护策略是三级:
- 输入端串联 1kΩ~4.7kΩ 限流电阻,配合 TVS 管泄放大部分能量;
- TVS 后并联差分电容和共模电容,把残余高频滤波;
- 外壳地和信号地之间用高压电容连接,切断地环路的同时泄放共模干扰。
这套组合下来,实测能过 ±4kV 接触放电和 ±2kV 浪涌,ADC 再无损伤。顺便提醒一句:TVS 管的结电容很大,如果选大功率型号,会把差分信号的高频成分吃掉,所以选 TVS 要看结电容参数,ppm 级别的信号链路里不能马虎。
5.4 布局与去耦的细节
模拟电路的设计到最后拼的都是布局。我给 ADS122U04IPWR 布置的规矩是:RREF 基准电阻离 REFP/REFN 引脚越近越好,而且底下不要走数字信号线,否则数字沿耦合进基准就等于直接调制了满量程。去耦电容 0.1μF 和 10μF 并联,每个电源引脚都有一份,电容地端汇聚到模拟地,再单点连接到数字地。
数字地与模拟地在 ADC 芯片下方汇合,我用一个 0Ω 电阻连接,而不是把两个地大面积直接打通。这样做虽然土,但在两层板上最实用,能有效阻止数字地噪声注入模拟地。
6. 从样板到 HVAC 现场:隔离、布线与长期稳定性
6.1 把信号从控制箱送到几十米外:RS-485 与 Modbus
如果你做的是一台独立仪表,前面这些已经够了。但如果要做 HVAC 系统里的现场设备,板子算出温度之后还要往 PLC、楼宇自控系统上报,这个环节就是 RS-485 和 Modbus RTU 的天下。
PIC18F86J15 本身有多路 EUSART,我拿一路接 ADS122U04IPWR,另一路接 RS-485 收发器,典型选型是 ISL83485 或 MAX3485 这类 3.3V 全双工/半双工芯片,速率 9600 波特率跑 Modbus RTU 完全没问题。软件上建议 Modbus 地址、波特率、奇偶校验做成可配置项,HVAC 现场不同项目的上位机要求五花八门,没有配置功能后期麻烦无穷。
6.2 电源与隔离:24V AC/DC 场景下的供电设计
HVAC 控制箱一个常见情况是只有 24V AC 或 24V DC 电源。24V AC 需要先整流,再经过 DCDC 降到 5V,最后用 LDO 得到 3.3V 给 MCU 和 ADC 供电。这里我踩过一个教训:LDO 之后直接用长走线给 ADC 供电,结果读数跟着负载电流波动,后来才想到 ADC 的电源引脚旁边不仅要用大电容,还要用磁珠把数字负载和模拟供电隔开,让 ADC 看到的是"干净"的局部电源。
如果现场信号线很长、且控制箱接地不良,我建议把整个模拟测量部分和 MCU 部分做隔离。做法是 ADC 的 SPI/UART 信号过数字隔离器(比如 ISO7741),电源侧用隔离 DCDC。这样即便现场地电位漂移几伏,也不会把板子上的芯片烧掉。代价是成本和功耗上升,所以是不是要隔离,取决于你面对的系统有没有"多点接地、地电位不相等"的问题。我一般建议:给楼宇控制柜用的板子,优先做隔离;给独立设备内部用的,可以省。
6.3 现场校准周期与故障排查思路
最后说说产品上线后的维护。RTD 和热电偶本身老化很慢,但信号链路的偏置会随时间和温度缓慢漂移,尤其冷端补偿用的内部温度传感器,年漂移不可忽视。我给用户的维护建议是:关键测点每年做一次在线校准,用精密电阻箱注入标准电阻,看读数偏差超过 0.2℃ 就重新校准;便宜的非关键测点就靠定期比对,发现异常再处理。
现场故障排查,我自己的顺序是:先看电源电压和纹波,再看 ADC 状态寄存器的错误标志位,然后用万用表量传感器端电阻和 PCB 入口电阻做对比,最后才考虑拆芯片。绝大多数 "读数乱跳" 的问题,最后都落在接线端子松动、传感器线进水、地线虚接这三类物理故障上,芯片本身反而很少成为元凶。
这套方案从画原理图到批量生产我完整走过一遍,最大的体会有两个:一个是对省成本的执念适度放手,模拟前端的保护电路和去耦电容别抠门,省下的几毛钱后期会让售后加倍还回去;另一个是一定要舍得在软件里丢几个点、多等几十毫秒,建稳定时间的账要算清楚,不然数字跳动的时候你根本分不清是算法问题还是硬件问题。
最后再分享一个实用小技巧:给 ADS122U04IPWR 的每个配置寄存器都做一个"影子变量",每次写入后读回来校验一遍。UART 在强干扰环境下偶尔会把字节弄丢,导致 ADC 寄存器被设成乱七八糟的值,而你自己还不知道。影子变量加定时巡检,能在问题扩大之前救你一次。