☰
基于ADS122U04IPWR与PIC18F87J10的工业温度测量系统设计
2026/11/5 4:59:59 网站建设 项目流程

在工业现场和HVAC系统里,温度测量是最基础但最容易翻车的环节。我这几周刚完成一个模块,主控是PIC18F87J10,ADC是TI的ADS122U04IPWR,目标就是给冷热源机房、风柜回风管和冷却水管道做多点温度测量。ADS122U04IPWR是一颗16位、4通道、带PGA和内部基准的Δ-Σ ADC,最特别的是它采用UART兼容接口,和PIC单片机连接非常舒服。整套系统里PIC18F87J10负责读取AD数据、做滤波和线性化,再通过RS485以Modbus RTU方式交给HVAC控制器。下面把选型、电路、固件和调试心得都写出来,给做类似项目的人一个可以直接抄作业的参考。

这个方案对我来说一直很顺手的点在于,它把“模拟前端”和“数字协议”彻底拆开了。ADS122U04IPWR管信号采集,PIC18F87J10管数据搬运和系统逻辑,两边不需要为了ADC采样去抢占CPU时间,也不需要为了拼接数据协议去动硬件SPI。你手上如果也有一堆老PIC项目代码,只是想换一颗更靠谱的ADC,那这套架构迁移成本很低。更重要的是,工业和暖通场景对温度测量有硬指标,比如冷媒管道上0.1℃的偏差可能影响好几台机组联动控制,所以能稳定的方案远比看着高级的方案值钱。

1. 方案选型与整体设计

1.1 为什么选 ADS122U04IPWR 而不是普通 ADC

先说结论:工业测温的难点不是“ADC有没有16位”,而是“在长期运行、温漂、电源噪声、传感器阻值差异都存在的条件下,结果能不能保持准确”。普通单片机内部ADC一上电数据就乱跳,PIC18F87J10自带的10位ADC就更不用说了,哪怕只做0.5℃分辨率都捉襟见肘。ADS122U04IPWR自带可编程增益放大器(PGA),增益范围从1到128,热电偶那几十毫伏甚至几毫伏的信号可以直接放大到满量程附近,这比外接仪表放大器省事得多。它内部还有一个低温漂基准源,不需要再单独配REF102这类精密基准,电路板面积能省出一大块。

更关键的是,ADS122U04IPWR内部集成温度传感器,这对热电偶冷端补偿特别有用。做HVAC的人可能只关心风管温度,但在工业现场,热电偶冷端补偿基准离测量端太远,误差就能到好几度。这颗ADC的内部温度传感器可以作为冷端补偿的参考点,直接用软件查表修正,省掉一路外加的NTC和对应的调理电路。除此之外,它还有两个IDAC电流源,配合外部参考电阻就能实现RTD比率测量,这种接法可以抵消激励电流的温漂,是做PT100测温时我最喜欢的功能。

回到通信方式,ADS122U04IPWR用的是UART接口,不是那种要接CS、SCLK、MOSI、MISO四根线的SPI,也不是I2C那种需要仔细处理地址和时钟延时的总线。一根TX一根RX就能把PIC拉起来干活,对PCB布局和排线长度也更宽容。相比SPI模式下的时钟尖峰,UART在普通FR-4板子上走10厘米到20厘米基本没压力。选型阶段我也考虑过外接AD7793这类SPI芯片,但最后发现ADS122U04IPWR和PIC18F87J10组合下的代码量最少,通信链路又足够快,16位分辨率对于0.1℃的HVAC需求已经算有余量了。

1.2 PIC18F87J10 的定位与资源分配

PIC18F87J10是一颗老牌8位单片机,128KB Flash、80引脚,工业级温度范围,在暖通控制器上有一大堆存量代码可以复用。很多人觉得8位机做高精度数据采集太吃力,其实只要你把“测量”和“控制”分清楚,8位机完全够用。我这里的思路是PIC不直接碰模拟信号,它只通过UART读取ADS122U04IPWR的数字结果,再跑滤波、线性化和Modbus协议。CPU占用率很低,剩余资源还能顺便管理继电器、按键、LED和看门狗。

资源分配上,PIC18F87J10有两路EUSART,我拿一路接ADC的UART,波特率固定115200,另一路接RS485总线,走Modbus RTU给上位机或者DDC控制器。内部振荡器在常温下问题不大,但为了UART波特率稳定以及ADC长时间采集不飘,我建议外部接16MHz晶体,实测下来比内部振荡器稳定得多。IO上还要留一个GPIO接ADC的复位引脚,上电时先复位一次,保证通信状态干净。

整体拓扑可以描述为:传感器信号进端子排,经过EMC和过压保护,送到ADS122U04IPWR的模拟输入;ADC通过UART把数据帧交给PIC18F87J10;PIC解出温度值后,再通过RS485转发给上层控制器。电源从24V工业电源进来,先用隔离DC-DC降到5V,再用低噪声LDO转3.3V供数字部分,模拟部分的AVDD单独走一个LC滤波。这样的好处是变频器、压缩机启动造成的电源污染不会直接影响ADC参考源和测量结果。隔离端我还没有做全,如果现场对地电位差大,建议在RS485和UART之间都加数字隔离器。

2. 硬件搭建:模拟前端与 UART 链路

2.1 供电、基准与参考电阻设计

ADS122U04IPWR是单电源供电,3.3V就能跑,但模拟电源质量决定了最终噪声底。我的习惯是AVDD和DVDD都接3.3V同一路,但PCB上严格分开走线,先在总电源处放一个10µF钽电容,再到模拟供电引脚放100nF陶瓷电容。如果成本允许,AVDD进来先串一颗几十欧姆的磁珠,能滤掉不少数字串扰。内部基准引脚REFP和REFN各放0.1µF电容,位置尽量靠近芯片,别让电容下面有高频信号穿过。

对于RTD测温,我强烈建议走比率式测量。也就是让ADS122U04IPWR的IDAC电流同时流过参考电阻和PT100电阻,ADC的参考电压取自参考电阻两端。这样激励电流的绝对精度不重要了,只要电流在整个测量周期内稳定,温度结果是Rrtd/Rref的比例值,电流源的温漂会被自动抵消。参考电阻选择低温漂的精密电阻,比如0.1%、25ppm/℃或者更好,参考电阻的阻值选择取决于IDAC电流和满量程范围。我实际用的IDAC电流是500µA,参考电阻选4.096kΩ,这样参考电压在2.048V左右,既不会超电源电压,又给后续PGA留出余量。

关于PGA增益,RTD信号幅度不像热电偶那么小,PT100在100℃时大约138.5Ω,500µA激励下电压只有69.25mV,PGA取4倍就很合适。如果设太高,比如128倍,输入端的偏置电流和噪声都会被放大,反而测不准。热电偶那边的增益要高一些,视分度号而定,K型热电偶常温附近灵敏度约41µV/℃,要看到0.1℃分辨率,至少要能分辨4µV的电压,PGA取64或128都合理,但必须提前算好满量程。ADS122U04IPWR的模拟输入允许双极性差分信号,如果输入超过满量程,转换结果会饱和,校准现场再发现就晚了。

2.2 热电偶、RTD和NTC怎么接最稳

热电偶接线看起来简单,实际上坑很多。首先是极性,K型热电偶红色线通常是正极,接错极性后温度值会反向,表现出来就是越加热读数越低。其次是补偿导线,工业现场不能随便用普通铜线延长,要用对应分度号的补偿导线,否则连接处会生成新的热电势,整条链路都乱掉。进入ADS122U04IPWR以前,我在每根信号线上各放一个10nF的C0G陶瓷电容到地,差分线之间再放一个1nF电容,构成低通滤波。这几个电容能抑制射频干扰,但要注意必须是C0G材质,X7R电容的压电效应会影响微伏级测量。

冷端补偿方面,我一开始图省事用ADS122U04IPWR内部温度传感器,校准后发现冷端处有风道吹过,温差能到2℃以上。后面改成在接线端子排附近贴一颗NTC,把这颗NTC接到ADC的另一个通道,用同一颗芯片来测冷端温度,效果比直接读内部温度传感器好很多。原因是内部传感器测的是芯片结温,而真正的冷端是热电偶信号接入端子那里,两者隔着PCB铜箔和外壳,热阻不小。想省成本可以用内部温度传感器,但要做1℃以内精度的热电偶测温,外部NTC基本省不了。

RTD的三线制接法也要单独说。三线制的目的是消除导线电阻,因为工业现场传感器离采集板几十米、上百米很常见,普通100m的导线电阻可能有好几欧姆,换算成PT100温度就是好几度偏差。用两个IDAC分别给两条引出线供电,可以在ADC输入差分端把引线电阻的影响抵消掉。我在实际布板时,把三条线都做了端子,靠近ADC输入侧还加了一排ESD二极管,防止雷击浪涌把芯片打坏。参考电阻和PT100必须保证是同一个电流回路,这个电流回路的逻辑是:IDAC电流经过参考电阻、再经过PT100和导线回到地,ADC测量PT100两端电压,参考电压由参考电阻产生,这样才叫真正的比率测量。

2.3 与 PIC18F87J10 连接的 UART 电气注意事项

ADS122U04IPWR的UART接口是3.3V TTL电平,PIC18F87J10同样工作在3.3V,两者可以直接连,但不能直接接RS232,也不能接5V单片机,否则时间长了会损伤IO。连接的时候注意交叉:ADC的TXD接PIC的RXD,ADC的RXD接PIC的TXD,地线必须共地。如果现场干扰强或距离远,信号线中间加一个3.3V电平的数字隔离器,把ADC侧和PIC侧的地彻底分开,这样能杜绝地环路噪声。

UART线在PCB上尽量远离模拟输入和参考电阻。我曾在一版板子上让UART收发线平行走了六七厘米,结果热电偶读数出现了规律性抖动,后来把这两根线挪到板边,中间铺地隔离,数据立刻干净很多。另外,如果ADC和PIC之间距离超过20厘米,建议把波特率从115200降到57600甚至38400,UART对边沿质量有一定要求,距离长了线缆电容会拖慢波形,误码率上升后你很难判断是协议错误还是硬件问题。

3. MCU固件:初始化、读数据与温度计算

3.1 UART通信协议与寄存器初始化

ADS122U04IPWR的寄存器细节比较多,我下面给的代码是工程抽象后的版本,不是手册寄存器原值。写固件之前,一定要把数据手册里的寄存器映射表单独打印出来,逐位核对。我的初始化流程固定四步:复位芯片、等待上电稳定、写配置寄存器、读回寄存器确认配置成功。

// 初始化PIC18F87J10的UART1,用于连接ADS122U04IPWR void uart1_init(void) { // 以16MHz外部晶体为基准,115200-8N1 TXSTA1bits.BRGH = 1; BAUDCON1bits.BRG16 = 1; SPBRGH1 = 0; SPBRG1 = 2; // 根据所选晶振计算波特率分频 RCSTA1bits.SPEN = 1; TXSTA1bits.TXEN = 1; RCSTA1bits.CREN = 1; PIR1bits.TXIF = 1; }

初始化完成后,我给ADC发一整组配置帧,包括通道选择、PGA增益、数据速率、内部基准使能、IDAC电流大小以及冷端NTC相关配置。配置结束后,调用一次读寄存器命令,把刚才写入的值读回来比对,错了就报错。这一步很多人会跳过,但工业产品如果没有自检逻辑,后面排查问题会非常痛苦。别偷懒,该读回的寄存器一定要读回。

ADS122U04IPWR支持单次转换和连续转换两种模式。HVAC这种慢变温度场景,连续转换完全够用,而且省了每次读取都要发触发命令的麻烦。我设置成数据速率每秒几百个样本,MCU每次收到一帧完整数据就解析,不主动查询。这样PIC的CPU占用很低,就算同时跑Modbus和本地LCD刷新,也不会有卡顿感。

3.2 读取ADC数据与温度换算代码

温度换算的代码最好和UART协议解耦。底层只负责把ADC码和ITP状态读出来,上层负责换算成温度值。我写了一个简单的读取函数,假设每次收到一帧三字节数据,前两个字节是有效16位数据,后一个字节保留给状态位或者校验位。实际帧格式务必以你手头芯片手册为准。

//从UART读取ADS122U04IPWR的一帧数据,返回16位ADC码 int16_t adc_read_frame(void) { uint8_t buf[3]; uint8_t timeout = 200; while (timeout--) { if (uart1_receive_byte(&buf[0])) break; delay_us(50); } // 这里要根据你的协议解析状态字节、帧头和校验 // 我工程里返回的是双极性补码 int16_t code = ((int16_t)buf[0] << 8) | buf[1]; return code; }

拿到ADC码之后,比率式RTD的换算非常简单。满量程对应的数字码是±32768(视具体寄存器配置而定),比例系数就是code除以32768,然后乘以参考电阻值,得到当前PT100的电阻。PT100的阻值转温度用Callendar-Van Dusen公式或者查表,前者适合代码量小的场景,后者适合精度要求更高的场景。

double rtd_resistance_to_temp(double r) { // 简化版,适用于0~300℃ // R(T) = R0 * (1 + A*T + B*T^2) // 这里用迭代法求出T double t = 0; double a = 3.9083e-3; double b = -5.775e-7; double r0 = 100.0; // 牛顿迭代,初值按线性估算 double dt = t; for (int i = 0; i < 5; i++) { double f = r - r0 * (1 + a * t + b * t * t); double df = -r0 * (a + 2 * b * t); t = t - f / df; if (t < -50) t = -50; if (t > 300) t = 300; } return t; }

热电偶那边稍微复杂一点。先用放大后的ADC码折算出毫伏或微伏电压,然后根据冷端温度查NIST多项式得到冷端热电势,加到测量热电势上,再反查热电势对应的温度。工程上我倾向于把NIST查询表做成const数组,用二分法查表,而不是直接上高次多项式,因为高次多项式在PIC这种8位机上跑起来会引入不少截断误差,而且慢。

double thermocouple_voltage_to_temp(double emf, uint8_t tc_type) { // 查表实现,返回温度,单位℃ }

3.3 多通道轮询、数字滤波与稳定时间

ADS122U04IPWR有4个通道,我的应用里通常一个通道接PT100测水温,一个通道接K型热电偶测风管温度,一个通道接NTC测冷端,还剩一个通道预留做设备面板温度监测。多通道切换之后,PGA和数字滤波器需要一点建立时间,不能切完通道立刻读第一次转换结果。我一般在切换通道后丢弃前2次数据,从第3次开始连续采16次,去掉最大最小值再求平均,效果很稳。

数字滤波也不能一上来就放很大的时间常数。HVAC控制里的PID周期通常是1秒到几秒,如果滤波太重,温度反馈滞后太多,反而会让压缩机或加热器反复震荡。我这里的经验是ADC原始数据先做一次简单的一阶低通,alpha取0.2左右,再丢进控制回路。如果只是为了显示,可以适当加大滤波;如果是为了控制,滤波必须贴合控制周期。

uint16_t temp_filter(uint16_t raw) { static uint16_t filtered = 0; // 一阶低通:新值占20%,旧值占80% filtered = (uint16_t)((raw * 20 + filtered * 80) / 100); return filtered; }

3.4 和HVAC上位机的Modbus RTU对接

PIC18F87J10第二路UART接RS485,通过MAX485芯片转差分信号。Modbus RTU协议其实不复杂,关键是把温度值放到保持寄存器或者输入寄存器里,并固定好小数位。我在工程里约定所有温度均为有符号整数,单位0.1℃,例如23.5℃存到寄存器里的值是235。

寄存器映射建议做到固定可配置:

寄存器地址内容数据类型说明
40001通道1温度int16实际温度×10
40002通道2温度int16实际温度×10
40003冷端温度int16实际温度×10
40004原始ADC码uint16调试用
40005设备状态uint160正常,1配置错误,2通信超时

Modbus报文处理要放在中断或轮询里,不要让RS485发送阻塞ADC读取。我的做法是PIC每10ms扫一次UART1有没有新ADC数据,有就更新温度缓存;RS485侧只在收到请求帧时读取缓存并回复,两边完全异步。这样即使上位机疯狂轮询,测温链路也不会被拖垮。CRC16校验函数网上大把,直接抄过来就行,但注意高低字节顺序要按Modbus规范来。

4. 常见问题与调试避坑

4.1 读数跳字、漂移大的原因排查

体温测系统最烦的故障就是“数据偶尔跳一下”。我遇到过好几次,最后查下来都不是ADC芯片问题,而是电源。变频器启动瞬间,24V电源上会出现很大的冲击,如果LDO前级没有足够储能电容,3.3V就会塌陷几十毫秒,ADC参考电压跟着抖,读数自然乱跳。所以电源入口的容量一定不能省,我用的是100µF电解电容加0.1µF陶瓷电容并联,效果立竿见影。

另一个容易忽略的是PGA输入范围。热电偶信号很小,但是共模电压可能偏高。如果ADS122U04IPWR的输入端共模电压不在允许范围内,寄存器读出来的数据会线性度很差。排查方法很简单,用万用表量一下ADC输入引脚对地的直流电压,确认它在电源轨以内且留出足够余量。如果输入共模太接近GND,可以考虑把信号抬高一点,或者检查传感器接线是否有漏电。

还有一点是PGA建立时间。高增益下,PGA对输入阶跃响应需要更长稳定时间。如果通道切换后第一帧就读,经常会看到接近满量程的假数据。我自己的做法是切换通道后等100ms以上再开始采样,或者直接丢弃前几次转换结果。这个时间和数据速率、PGA增益都有关系,别拍脑袋写死,最好用示波器抓一下DRDY信号和UART数据帧的关系。

4.2 热电偶冷端补偿不准的问题

冷端补偿不准是热电偶测温最大的坑,表现是:同样的温度环境,早上测和下午测差好几度。根源往往不是ADC,而是冷端温度传感器没有贴在真正的冷端上。ADS122U04IPWR内部温度传感器在芯片内部,PCB板上有散热铜箔、电源芯片和继电器这些热源,芯片结温和接线端子温度可能差出好几度。如果要求高,必须外部接NTC到冷端位置,而且要等设备通电后热平衡稳定30分钟再标定。

补偿导线的极性也很重要。K型热电偶的补偿导线有正负极之分,接反之后不但不补偿,反面引入额外误差。现场工人如果拉线不仔细,很容易把正负极接反。我在端子排上用不同颜色区分,并在固件里加入一个“极性自检”:把冷端位置放一个已知温度源,如果读数方向反了,就给出报警提示。

另外,工业现场如果同时有多个热电偶接到同一块采集板上,每路热电偶的冷端位置可能不一样。不能简单把所有通道共用同一个冷端温度,因为每根补偿导线进入端子排的位置、距离发热元器件远近都不同。我最后是在每个通道旁边都预留了NTC焊盘,谁需要谁贴,这样成本高一点,但精度可控。

4.3 UART接收乱码和信号质量问题

UART乱码通常被怀疑是波特率不对,但芯片的波特率寄存器已经配好了,怎么还会错?最典型的原因是PIC18F87J10用了内部振荡器,内部振荡器受温度和电压影响可以达到1%以上的偏差,而UART接收要求收发双方误差在一定范围内。所以只要用了这个组合,UART侧一定要用外部晶振,别指望内部振荡器跑工业现场。

另一个问题是地电位差。ADC的GND和PIC的GND不是同一个地时,UART信号会因为共模电压偏移而出现偶发乱码。我遇到过一块板子,接上隔离RS485后没问题,但直接用USB调试器连PIC的UART,读数就会零星出错,后来才知道USB调试器的地线和现场地之间存在电压差。解决方法是所有通信链路统一一点接地,或者在UART线上加数字隔离器。

线缆过长时,UART信号边沿会变缓。如果必须在百米级距离通信,就不能再用TTL UART了,要换成RS485走Modbus,或者把ADC和PIC放在同一个采集盒子里面,用传感器侧的长线到现场,UART只在盒子里跑几厘米。实测下来,盒内UART走线控制在5厘米以内时,几乎不会出现误码。

4.4 常见问题速查表

症状可能原因排查方向
读数稳定但偏低传感器接线错误或补偿导线接反重新核对传感器正负极
读数一直为0或满量程模拟输入开路、PGA增益配置过大、参考未使能测量输入电压,检查寄存器配置
几个通道之间温度互相干扰多路复用通道间串扰增加通道切换后的丢弃次数,检查PCB走线隔离
UART偶发乱码波特率误差、地电位差、线缆过长换外部晶振,检查共地,缩短线缆
热电偶读数随环境温度变化大冷端传感器位置远离接线端子外接NTC到冷端,重新标定
RTD温度漂移激励电流不稳定、参考电阻温漂大改用比率式测量,换低漂移参考电阻
上电后通信超时ADC未复位、UART波特率初始化顺序不对增加复位时序,读回寄存器确认

这个速查表是我在调完项目后整理的,很多问题看起来是芯片参数,其实是电源、地线、布线这些基础层面的东西。做工业测温不要一上来就怀疑ADC芯片,先把整条链路从传感器到串口全部查一遍,往往能省下半天时间。

最后再分享一个小技巧:调试时先别管温度和固件协议,直接让ADS122U04IPWR输出原始ADC码,配上串口助手盯着数据帧。如果你用一个稳定的电压源模拟传感器信号,原始码也跟着稳定,那问题一定在传感器或冷端;如果原始码也跟着抖,那就从电源、参考和布线查起。这招总能帮我快速缩小问题范围,比从头到尾测电压靠谱得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询