做温度测量方案这么久,我最常被问到的问题就是:能不能用一套简单、稳、成本还不离谱的电路,同时搞定HVAC里的风管温度和工业现场的设备测温。ADS122U04IPWR加PIC18F86J50这个组合,是我在几轮改版之后真正定下来用的一对搭档——前者是带UART接口的24位Delta-Sigma型ADC,内部集成了PGA、基准源和激励电流源,后者是Microchip的J系列8位单片机。整个方案做下来最直观的感受是:以前调SPI时序、折腾片选和时钟线的功夫,现在用两根UART线就完成了,模拟前端的外围元件也少了一大半。
这篇东西不是芯片数据手册的翻译,也不是开发板的Demo复刻,而是我从原理图、布局、固件到现场调试,完整走完一套测温链路后的记录。文章适合正在做温度采集、暖通控制、工业数据采集的嵌入式工程师,也适合那些在ADC选型和模拟前端设计上犹豫不决的朋友。我会把选型逻辑、硬件接线、通信初始化、温度换算算法、抗干扰与校准,以及我实际踩过的几个坑都摊开来讲,你可以把它当成一份可以直接开工的参考。
1. 为什么这个组合把温度采样做成了"搭积木"
1.1 ADS122U04IPWR:UART口版本的精密ADC到底改变了什么
ADS122U04IPWR这个型号,IPWR后缀对应TSSOP-16封装、工业温度范围-40℃到+125℃。它本质上是一颗24位Delta-Sigma型ADC,但和常见的ADS1220那种SPI接口版本不一样,它把数字接口换成了UART,这是它最特别的地方。
在工业现场,MCU和ADC之间的数字接口越简单越好。SPI接口虽然快,但四根线(SCLK、MOSI、MISO、CS)在恶劣电磁环境下容易受干扰,而且很多MCU的SPI外设数量有限,一旦通道多、传感器多,就得用GPIO模拟片选,软件压力立刻上来。UART接口只需要DIN和DOUT两根线,很多8位MCU天然就有两三个UART模块,直接把收发脚一对就能通信,这对PCB布局来说是非常大的解放。
这颗芯片内部还集成了很多以前需要外部搭电路的东西:可编程增益放大器、低漂移基准源、两路匹配的激励电流源,还有专门为RTD和热电偶设计的内部温度传感器。换句话说,一颗芯片把模拟前端最麻烦的部分全包了,外置只需要放几个滤波电容和精密电阻。对做HVAC控制器或者工业测温板的人来说,这几乎是"开箱即用"的体验。
1.2 PIC18F86J50在整套系统里的分工
PIC18F86J50是Microchip的中档8位MCU,程序空间和RAM对温度采集这种任务来说相当宽裕。我在这套方案里让它专职做三件事:UART协议解析、温度换算算法、对外通信与告警输出。8位内核处理24位ADC数据和多项式计算完全没有性能压力,甚至在跑浮点温度换算的同时还能兼顾看门狗和通信超时检测。
选择它还因为几个很实在的理由:宽工作电压范围,适合和传感器调理电路共用一个3.3V或5V电源域;I/O引脚带5V容忍能力,和外部逻辑接口时不用到处加电平转换;片上EUSART模块多,一路给ADC用,另一路还能留给RS485或者Modbus等上位机通信。对于HVAC这种要长期无人值守运行的场景,8位MCU的稳定性和低故障率反而是优点,杀鸡确实不需要牛刀。
1.3 工业现场与HVAC控制器对这套方案的需求差异
虽然标题把工业和HVAC放在一起,但这两个场景的需求其实很不相同,我在设计里是分开考虑的。
| 需求维度 | HVAC控制器 | 工业现场测温 |
|---|---|---|
| 测温范围 | -40℃~+120℃为主 | 可能覆盖-200℃~+850℃ |
| 传感器类型 | NTC热敏电阻、PT1000居多 | PT100三线制、K型热电偶常见 |
| 精度要求 | 通常在±0.5℃以内 | 部分场景要求±0.1℃~±0.2℃ |
| 电磁环境 | 变频风机、水泵干扰较多 | 动力线、变频器、大功率设备并存 |
| 长期可靠性 | 数年免维护 | 恶劣环境下的连续运行 |
| 成本敏感度 | 很高,整机成本竞争激烈 | 中等,更看重稳定 |
这个对比表直接决定了电路怎么搭:HVAC方向我会优先压低成本,用PT1000配合高阻值分压或者低电流激励,减少精密电阻的开销;工业方向则认真做三线制或者四线制RTD接法,用比率测量消除基准漂移,把抗干扰和长期稳定性放在第一位。ADS122U04IPWR的可配置激励电流源和PGA恰好两边都能满足,硬件上预留几个0欧电阻位,固件里改一下配置寄存器就能切换场景,这一点在我的项目里非常实用。
2. 硬件链路:从传感器到MCU的每个环节都要较真
2.1 RTD三线制接法和激励电流源设计
RTD是工业测温绕不开的传感器,PT100在0℃时名义电阻100欧姆,温度每升高1℃大约变化0.385欧姆,信号非常微弱。如果用简单的分压法,电源波动和引线电阻都会直接进入测量结果,所以我的做法是让ADC内部的两个激励电流源(IDAC1和IDAC2)分别从RTD两端注入,再用差分输入测量电压。
三线制接法是最常见的工程折中:两根引线在RTD两端各走一条激励电流,第三根线作为参考电位。当IDAC1和IDAC2的电流匹配时,两根引线电阻上的压降相互抵消,测量结果基本不受线长影响。四线制更彻底,但需要传感器多一根引出线,HVAC的盘管和风管里往往没有那么方便的走线条件,所以我默认用三线制。
实际接法上,我会把RTD的一侧接AIN0正输入,RTD另一侧接AIN1负输入,IDAC1从正输入脚注入电流,IDAC2从负输入脚拉出电流,第三根引线接到AIN2作为参考地。这里有一个细节:IDAC电流值不要选太极端。电流大了自发热明显,RTD本身温度会被抬升;电流小了信噪比又不够。我实际用250µA或者500µA,PT100在250µA下自发热可以忽略,配合PGA增益64,满量程能覆盖常用测温范围。
激励电流源的稳定性直接决定测量精度,所以IDAC的电源路径上我会加一个小容量的去耦电容,而且这个电容的位置要尽量贴近ADC的电源引脚。很多人只注意基准电压去耦,忽略了IDAC本身的电源纹波,结果读数的跳动怎么也压不下去,我在这上面吃过亏。
2.2 热电偶输入与冷端补偿的板级实现
热电偶的情况和RTD完全不同,它输出的是毫伏级热电势,而且必须知道冷端温度才能算出真实温度。ADS122U04IPWR内部自带一个温度传感器,配合PGA的高增益,可以直接做冷端补偿,省掉外面单独贴一片温度传感器芯片的麻烦。
K型热电偶在常用温度范围内灵敏度大约41µV/℃,信号非常小。我习惯把PGA增益设置到128,让ADC的24位分辨率充分压榨出来。热电偶的测量端接AIN0和AIN1差分输入,接的时候要特别注意热偶丝和铜走线之间形成的"第二热电偶"效应——PCB上用铜走线连接热电偶引脚的位置,一定要让两个接触点尽量靠近,保持热平衡,否则会产生意想不到的偏移电压。
冷端补偿的具体做法是:先用内部温度传感器测出接线端子附近的温度,把它作为冷端温度,然后查热电偶分度表得到对应的热电势,再加上实测的热电势,最后反查温度。这个算法看起来很绕,但实际也就是查表和加法的关系,固件里实现并不复杂。我在第三轮改版时把冷端补偿用的查表数据从浮点数组换成了缩放后的整数表,MCU的浮点运算压力小了很多,温度的稳定度反而更好。
2.3 UART连接、电源去耦和PCB布线的具体做法
AD122U04IPWR的供电我建议直接用3.3V单电源,模拟电源和数字电源分别接,中间用磁珠隔开,模拟地(AGND)和数字地(DGND)在芯片下方单点相连。PIC18F86J50如果也用3.3V供电,UART两线直接对接即可,不需要电平转换。
PCB布局的顺序很重要:模拟输入和激励电流走的线放在顶层,数字UART和电源放在底层,中间留完整的参考地平面。RTD和热电偶的输入线一路过来都不要打过孔,尽量短,旁边不要走高频数字信号。我在第一版就是因为把ADC输入走线和UART信号挤在同一层挨着走,实测的跳动始终在几微伏量级,怎么改寄存器都压不下去,后来重排布局才解决。
电源去耦方面,ADC的AVDD、DVDD各放一个10µF钽电容和0.1µF陶瓷电容,参考基准引脚还要额外加一个低ESR电容。工业现场如果供电质量差,最好在前面加一级LDO,开关电源直接供电会让ADC的差分非线性变差,这个我在后面踩坑章节还会细说。
3. 通信初始化:让ADS122U04在UART总线上"开口说话"
3.1 UART协议的基本工作方式
ADS122U04IPWR的UART接口和普通串口设备不太一样,它没有发送使能和忙标志这种通用外设的概念,而是采用命令应答的方式:MCU通过DIN发送命令字节,ADC通过DOUT回送结果。空闲状态下两根线都保持高电平,每次通信从起始位开始,典型的帧格式是8个数据位、无校验、1个停止位。
主机可以发送的命令主要有复位、读取数据、读寄存器、写寄存器这几类。读取数据命令会带回一个24位的有符号转换结果,而读写寄存器命令可以逐个配置ADC的内部寄存器。这里有个经验:虽然UART是异步的,但ADS122U04的数据输出速率是固定的,读数据命令发出去之后,DOUT引脚会按照芯片内部时钟逐个字节送回来,所以MCU侧不需要额外的握手信号,只要波特率完全一致就不会错位。
我做一个建议:用UART通信时把波特率尽量压低。ADS122U04支持的波特率很宽,但我在现场项目里稳定用9600bps,原因是工业环境里高速UART对时序容差的要求更高,而9600bps对MCU的时钟误差容忍度最好。测温和暖通控制本来就不是高吞吐场景,每秒几十个采样点完全够用。
3.2 寄存器初始化流程和关键配置项
上电之后芯片会先完成内部复位,我建议MCU在发任何命令之前先给ADC一个确定的复位命令,避免上电竞态。初始化顺序如下:
- 发送复位命令,等待芯片内部复位完成。
- 写配置寄存器0:选择输入多路复用通道和PGA增益。
- 写配置寄存器1:选择基准电压源、转换模式和数据速率。
- 写配置寄存器2:使能内部温度传感器,配置两个IDAC电流源。
- 回读所有寄存器确认写入值。
寄存器配置的实际位定义在每个版本的规格书里可能有细微差别,我强烈建议以你手上那版数据手册的寄存器表为准。这里给一组我项目里验证过的典型值作参考:PGA设为64,数据速率设为100SPS左右,基准源选内部基准,IDAC一个是250µA输出,另一个作为回流。写寄存器命令和读寄存器命令的编码也遵循TI ADC常见的约定:写寄存器是0x40加寄存器地址,读寄存器是0x20加寄存器地址,我把这些命令用宏定义集中放在一个头文件里,方便移植和核对。
提示:命令字和寄存器位定义务必和最新规格书对照一遍。TI芯片不同批次的手册偶尔会补充新的位定义,但基本命令格式是稳定的。我吃过想当然的亏,后来一律以手册为准。
3.3 MCU侧UART配置与示波器实测时序
PIC18F86J50的EUSART配置成普通异步模式即可,下面这段是我用在项目里的初始化代码骨架,用的是16MHz时钟源,目标波特率9600bps:
// PIC18F EUSART1 初始化,16MHz,9600bps,8N1 void uart1_init(void) { // 注意:TX/RX引脚号以你自己的原理图为准 TRISBbits.TRISB2 = 0; // TX1 设为输出 TRISBbits.TRISB3 = 1; // RX1 设为输入 BAUD1CONbits.BRG16 = 1; TXSTA1bits.BRGH = 1; // 高速波特率模式 SPBRG1 = 416; // 16MHz / (4 * 9600) - 1 TXSTA1bits.SYNC = 0; // 异步模式 TXSTA1bits.TXEN = 1; // 使能发送 RCSTA1bits.SPEN = 1; // 使能串口 RCSTA1bits.CREN = 1; // 使能连续接收 }初始化完成后,我习惯先用示波器或者逻辑分析仪看一遍DOUT引脚上的波形,确认起始位、停止位和字节间隔正常。第一次调通时DOUT上应该能看到周期性的数据字节流,每个字节之间的空闲高电平清晰,帧间隔稳定。如果波形上的起始位前沿有抖动,优先怀疑波特率失配,而不是ADC的问题。
另外一个值得做的操作是上电后主动回读一次寄存器,把读到的值和写入值逐位比对。UART通信偶尔因为上电时序丢第一个字节,回读能第一时间发现问题,省得后面数据错了还找不到原因。
4. 从ADC原始码到温度读数:固件算法与数据处理
4.1 原始数据的读取、符号处理和电压换算
ADS122U04IPWR返回的24位数据是二进制补码格式,先要把它正确扩展成有符号数,否则后面的计算全乱。读取三个字节后,把它们组合成一个32位整数,再通过左移和右移做符号扩展:
// 组合24位原始数据并做符号扩展 int32_t adc_read_signed(void) { uint32_t raw = adc_cmd_read_data(); // 读取RDATA命令返回的3字节 raw <<= 8; // 移到32位整型的最高24位 int32_t sign = (int32_t)raw; sign >>= 8; // 算术右移,完成符号扩展 return sign; }得到有符号的原始码之后,换算成实际电压用这个公式:
电压 = 原始码 × (2 × VREF) / (PGA × 2^23)
比如PGA为64、内部基准2.048V时,一LSB对应的电压大约是2×2.048/(64×8388608),换算出来不到8纳伏级别,完全覆盖了温度传感器微伏信号的需求。这个公式我习惯封装成一个函数,输入原始码和PGA,输出浮点电压,后续的电阻和温度计算都在电压基础上做,代码结构清楚一些。
4.2 RTD电阻计算与IEC 60751温度转换
RTD的电阻值由激励电流和测量电压决定,理论上直接用电压除以电流就行,但电流源的绝对值会随温度漂移,直接除会在宽温场景下引入误差。更好的做法是比率测量:在激励电流路径上串一个精密基准电阻,同时ADC在基准电阻和RTD之间轮流测电压,RTD的电阻等于基准电阻乘以两者原始码的比值,这样电流源的绝对值漂移被完全消掉。
我板上放了一个1kΩ、温漂系数低于5ppm/℃的精密电阻作为比率参考。每次测量时多花一次转换读取基准电阻两端的电压,然后用下面的关系换算:
R_rtd = R_ref × (CODE_rtd / CODE_ref)
这个式子里的CODE_rtd和CODE_ref都是同一套PGA配置下的原始码,所以放大器的增益误差也被抵消了,这是我在工业应用中特别看重的一点。
得到RTD电阻之后,温度换算按照IEC 60751标准的Callendar-Van Dusen公式。0℃以上范围内,电阻和温度的关系是R=R0×(1+A×t+B×t²),其中R0=100欧姆,A=3.9083e-3,B=-5.775e-7。我不建议去解三次方程或者反复迭代,直接把二次方程反解出来就够了:
// PT100电阻转温度,适用于0~850℃范围 float rtd_res_to_temp(float r) { const float R0 = 100.0f; const float A = 3.9083e-3f; const float B = -5.775e-7f; float t = ( -R0 * A + sqrtf(R0 * R0 * A * A - 4.0f * R0 * B * (R0 - r)) ) / (2.0f * R0 * B); return t; }如果项目要覆盖-200℃到0℃的低温段,公式里要多加一个C=-4.183e-12的修正项,处理起来也不复杂,但HVAC场景基本用不到,工业现场需要低温段的时候我才会把完整版公式放进去。
4.3 热电偶冷端补偿与软件滤波、断线检测
热电偶的算法核心是"看两个表":先查冷端温度对应的热电势,再查总热电势对应的温度。实际操作上我会先把冷端温度送进K型分度表查到热电势E1,然后把实测热电势加上E1得到总热电势,最后用总热电势反查温度。为了节省运行时间和程序空间,我把分度表做成每10℃一个节点的整数数组,中间用线性插值,在HVAC的精度要求下绰绰有余。
软件滤波方面,移动平均和中值滤波我都用上了。中值滤波主要对付偶发的尖峰干扰,窗口取5个点就足够;移动平均负责平滑温控回路的反馈信号,窗口长度在HVAC里我取8到16个采样点,太多会让温控环路反应变慢,太少又压不住噪声。
断线检测是温度采集必须考虑的功能,不然传感器松了系统还在盲目控温,那会出大问题。ADS122U04内置的激励电流源有个天然优势:RTD断线时测量端的电压会直接顶到电源轨或者塌到零,读数要么接近满量程、要么直接归零。我在固件里做了一层阈值判断:原始码超出正常RTD工作范围上限或者归零时,立刻打传感器故障标志,上位机收到后进入安全状态。这套逻辑在HVAC机组里帮我挡过好几次端子松脱的事故。
5. 工业与HVAC场景下的校准、抗干扰和长期稳定性
5.1 两类场景的干扰画像与布线对策
同样是测量温度,HVAC控制器和工业现场的干扰来源完全不同。HVAC里最凶的干扰是变频风机和压缩机,变频器工作时的开关噪声会通过电源线耦合进来,干扰频谱集中在几十千赫兹到几兆赫兹之间。工业现场除了变频器,还可能有大电机启停带来的地电位瞬变,以及动力线对信号线的空间耦合。
我的处理思路是分两道防线:第一道在传感器入口,模拟输入引脚前加RC低通滤波。RTD属于慢变化信号,我用的截止频率大约在几百赫兹,一下子就把高频干扰挡在外面。第二道在电源入口,ADC的模拟电源用LDO单独从系统电源取电,避免数字部分和通信接口的噪声串进来。
布线层面有一条经验值得单独说:ADC的模拟输入走线和激励电流走线尽量做到"平行同层",不要跨越参考地平面的缝隙。地平面如果有开槽或者断裂,回流电流会被迫绕路,形成环路天线,差模干扰直接耦合进信号。很多精密ADC读数在换了布局之后莫名其妙变干净,原因就在这份地平面的完整度上。
5.2 两点校准与比率测量带来的稳定性收益
芯片的初始增益误差和偏移误差虽然不大,但要达到工业级的±0.2℃精度,还是要做一次出厂校准。我的校准方法很简单:准备一个精密电阻箱,分别在100欧姆和138.51欧姆两个点(对应0℃和100℃)各测一次,记录下理论电阻和ADC读数的差,算出偏移和增益修正系数,存到MCU的EEPROM里。
校准系数在每次开机时加载到全局变量,温度计算时先修正电阻值再查表。这里有一点我要特别强调:校准最好在整个系统联调之后再做,因为温度误差里很大一部分来自参考电阻、运放输入偏移和PCB上的热电动势,这些只有在完整链路上测量才真实。如果只在开发板上校准,换到生产板上系数基本要重来。
比率测量带来的稳定性收益是另一个重点。因为RTD电阻是通过和基准电阻的原始码比值算出来的,只要激励电流源不突变,电流的绝对温度漂移就完全不影响精度。我做过高低温箱对比测试,没有用比率测量的版本在-20℃到60℃范围内读数差了接近0.5℃,改成比率测量之后整条曲线几乎拉平。这个差距足够说明问题了。
5.3 自诊断设计:看门狗、通信超时与传感器故障识别
系统长期运行,不能只在正常路径上考虑问题。我在固件里加了三层自诊断:第一层是MCU看门狗,主循环里喂狗,任何软件死循环都能自动复位恢复;第二层是UART通信超时,如果单片机向ADC发了命令后超过规定时间没收到响应,判定ADC异常,执行一次软复位重试;第三层是传感器故障识别,包括断线、短路、读数超出工作范围,直接上报故障码而不是输出一个离谱的温度值。
这三层逻辑在HVAC机组的现场调试里非常有用。有一次通信线被老鼠咬断了一半,上位机收到的是断断续续的异常温度,自诊断第一时间报出传感器故障,运维人员十分钟就定位了问题。要是没有这层保护,控制系统很可能把异常温度当成真实值,触发错误的启停机逻辑。
6. 三轮改版踩过的坑:错帧、漂移和上电异常
6.1 波特率时钟累积误差导致的偶发错帧
第一版样机在实验室里跑得很欢,一到工业现场就开始偶尔出现温度跳变,频率不高但很恼人。我用逻辑分析仪抓DOUT波形,发现ADC返回的数据字节偶尔会缺少一个字节,导致后面所有数据整体错位,解析出来的温度直接飞了。
根因是波特率容差。MCU内部振荡器的精度大约在±2%到±3%,而且随温度变化,ADC内部时钟也有自己的误差,两边一叠加,长时间运行后就可能超出UART接收的容限。这其实不是某个单字节立刻出错,而是累积相位误差在某一次采样点超出阈值,造成采样点落在数据位边缘。
解决方法是三管齐下:MCU换成外部晶振做时钟源,把时钟精度拉到几十ppm;波特率从19200降到9600,单bit时间变长之后同样的绝对时间误差造成的相位偏移变小;发送命令前加一个短暂延时,确保DIN空闲足够时间,避免芯片状态机进入错误分支。改动之后这套通信就再也没出现过偶发错帧。
6.2 低温环境下读数漂移的真正来源
第二轮改版遇到了更麻烦的问题:整机在高低温箱里,-20℃以下的时候ADC读数开始缓慢漂移,温度回升后读数又恢复正常。一开始怀疑是ADC本身温漂,换了芯片问题依旧,后来排查到激励电流源的设置上。
问题出在电流源回路上的一颗采样电阻。我一开始用的采样电阻温漂系数偏大,温度一低阻值变化明显,电流源的绝对值跟着漂,而当时的硬件没有做比率测量,所以这个漂移直接变成了测温误差。解决方法是把它换成一颗25ppm甚至更高精度的精密电阻,同时固件里改用基准电阻与RTD的原始码比值算法。改了之后,同样在-20℃环境下,温度读数的重复性从原来的±0.5℃改善到±0.1℃以内。
这件事给我的教训是:精密模拟电路里没有"随便放颗电阻"的说法。凡是参与信号链的电阻,TCR(电阻温度系数)和长期稳定性都要单独审查,省这几毛钱会让整个系统的精度目标大打折扣。
6.3 上电时序与复位竞态问题
第三轮的问题出现在批量测试阶段,偶尔有板子上电后MCU已经跑起来了,但ADC一直没响应。排查发现是上电时序竞态:ADC的电源轨爬升需要时间,而MCU往往几十毫秒就完成了复位开始往外发命令,这时候ADC还在内部上电复位中,MCU发过来的第一条命令直接被丢弃。
我并不建议用复杂的电源时序控制电路来解决,成本高且没必要。简单可靠的做法是在MCU初始化ADC之前加一个固定延时,我用的延时是200毫秒左右,确保ADC完成内部上电复位。同时,在发送任何写寄存器命令后,都回读一遍寄存器做校验,如果匹配失败就重新软复位、重新配置。这两条措施加进去之后,批量测试的上电异常率直接降到零。
6.4 做完这个项目后我会记住的几条设计准则
第一,精密ADC的参考和电流源路径上,任何电阻都不允许随意选型。测温精度是系统级的,一个温漂大的电阻就能毁掉整条信号链。第二,UART通信看似简单,波特率时钟的长期累积误差是真实存在的坑,尤其是MCU用内部振荡器的时候,必须在设计阶段就考虑到。第三,所有温度采集系统都要把断线和故障检测当成必备功能写进固件,而不是等现场出了事故再补。
如果你也在做类似的温度采样,希望这些记录能帮你少走一轮改版。