工业多路测温方案:24位ADC+PIC32+UART接口实战解析

发布时间:2026/11/5 5:01:39
工业多路测温方案:24位ADC+PIC32+UART接口实战解析

前一阵子给一套多路温控板做升级,传感器侧用的是热电阻和K型热电偶混合输入,主控端则用 PIC32MX675F512L 来统筹采集、滤波和上位机通信。温度采样这个环节,最初想过直接用MCU内置ADC,但工业现场一测就露馅,噪声和漂移扛不住。后来改成外置ADC,锁定了 ADS122U04IPWR 这颗带UART接口的24位Δ-Σ转换器,折腾了大半个月总算把整个链路跑顺了。这篇文章把这套方案的选型逻辑、硬件连接、寄存器配置和实际调试中的坑完整记录下来,希望能给正在做工业测温或HVAC控制器的同行省点时间。

1. 为什么最后选了UART型ADC加国产化容易的PIC32

1.1 初始需求拆解:不只是一块测温板

这块板子最终要服务的场景是HVAC楼宇自控里常见的冷水机组和空调箱,外加一部分工业管道测温监测。粗略列一下需求:至少4路温度输入,覆盖-50℃到+150℃(管道水温、回风温度、盘管表面温度),部分测试点需要用热电偶覆盖到300℃(比如压缩机排气管);每路采样刷新率不低于10次/秒;系统长时间工作后温漂必须压到±0.3℃以内;通信要稳定,不能因为某个传感器线长了一点就出现数据毛刺。

这些要求直接把MCU内置ADC的路堵死了。PIC32MX675F512L内置的10位ADC,理论上能满足分辨率需求,但工业现场存在共模干扰、电源纹波、参考源波动,10位ADC加上无内置PGA,小信号几乎没法看。热电偶输出信号本身就弱,K型热电偶约41µV/℃,做0.1℃分辨率就需要0.4µV级别的检测能力,没有外部高精度ADC根本无从谈起。

当时对比过ADS1220(SPI接口)、ADS1232(桥式专用)、AD7190(价格偏高),最终选了ADS122U04IPWR。这颗芯片是TI的24位Δ-Σ型ADC,带UART接口,内置可编程增益放大器(PGA,增益1/2/4/8/16/32/64/128)、双路恒流源、低漂移内部基准源(5ppm/℃典型值)。4通道模拟输入配一个2:1多路复用器,单颗芯片就能搞定4路单端或2路差分输入。最关键的是UART接口,下面详细说。

1.2 为什么UART接口反而是优势

大部分精密ADC都是SPI或I2C接口,ADS122U04用UART,很多人第一反应是不习惯。但在实际项目中,这个设计确实有它的道理。

工业现场传感器到主控板的距离往往有几十米,SPI的SCK时钟线在长线传输时容易反射、串扰,时钟抖动会直接影响采集精度。UART是异步协议,没有连续时钟,抗干扰能力相对好,而且物理层可以很自然地挂RS-485或RS-422收发器,实现远距离多点采集。ADS122U04IPWR支持四线UART(TXD、RXD、START、DRDY)和二线UART(TXD、RXD复用,加上START),四线模式下DRDY引脚可以作为一个硬中断信号直接通知主控"数据准备好了",不需要主控反复轮询。

对PIC32MX675F512L来说,它的UART外设自带FIFO,配合中断驱动几乎不占CPU。实测下来4路通道连续扫描时,CPU占用率只有5%左右,还有大量剩余算力可以用来做滤波算法、RS-485协议栈和PID调节。

1.3 PIC32MX675F512L在系统里的分工

PIC32MX675F512L这颗芯片属于Microchip的MIPS M4K内核,主频最高80MHz,512KB Flash、64KB RAM对这类应用绰绰有余。它在这块板子上承担的角色是"采集调度+数据处理+对外通信":

  • 通过两路UART:一路与ADS122U04IPWR对话,一路输出到RS-485总线(Modbus RTU协议)供上位机读取;
  • 用定时器1产生节拍,控制ADC的采样序列和通道切换节奏;
  • 完成滤波(移动平均、中值滤波)、热电偶冷端补偿查表、RTD线性化校正等运算。

需要强调一下,PIC32MX系列在工业级应用中比较受认可的原因之一是它的IO引脚5V容忍度设计,3.3V逻辑与外围5V器件的电平匹配问题在模拟前端设计中少了一层麻烦。不过ADS122U04IPWR的数字IO供电DVDD可以接1.8V到5.5V,所以这里直接统一3.3V供电,不需要做电平转换。

2. 传感器信号链路设计:热电偶、RTD和NTC怎么接

2.1 热电偶测温的弱信号处理与冷端补偿

K型热电偶的输出电压与热端温度呈近似线性关系,0℃到300℃范围内大约41µV/℃。这个信号有多弱?如果用内置ADC满量程5V计算,1个最低有效位(10位ADC约5mV)相当于122℃的温差,完全无法分辨。而ADS122U04IPWR在PGA增益为128时,满量程输入只有±78.125mV,24位分辨率下理论最小分辨约9.3nV,K型热电偶的0.001℃变化都能体现出来。当然实际系统噪声水平远高于理论值,但做到0.02℃级别稳定度是可行的。

热电偶测量最大的坑是冷端补偿。热电偶测量的是热端与冷端的温差,不是绝对温度。冷端(即热电偶接入PCB的端子)温度每变化1℃,测量值就会偏移约41µV等效的温度数值。很多新手直接忽略了这一步,结果发现温度随环境温度漂移。我这里的做法是:

  • 在接线端子旁贴一颗数字温度传感器作为冷端参考,精度±0.25℃;
  • PIC32主控读取冷端温度,换算成对应热电偶的补偿电压(查表或多项式拟合),加到ADC测得的毫伏值上;
  • 最终用NIST ITS-90多项式反向查表得到热端温度。

实测下来,只要冷端传感器与接线端子热耦合良好(中间填充导热硅脂),冷端补偿误差能做到±0.3℃以内。一个容易忽视的细节是:PCB铜箔的导热会让冷端传感器"感知"到板内热源(比如电源芯片)的温度,所以冷端传感器要尽量靠近端子、远离发热器件,并且最好在下方铺一小块独立的接地铜皮来均匀温度场。

2.2 RTD铂电阻的激励电流与引线电阻误差

工业HVAC系统里,管道水温、回水温度这类测量更多用PT100或PT1000铂电阻,因为稳定性比热电偶好得多。RTD是无源元件,需要激励电流流过它产生电压,再通过ADC测量该电压反推电阻值。

ADS122U04IPWR内置两个匹配电流源IDAC,每个可设置输出10µA到1500µA共8种档位。我用的是500µA档,这样PT100在0℃时产生50mV电压,在100℃时产生约69.2mV(PT100的电阻温度系数约0.385Ω/℃)。PGA增益设为8,满量程±0.625V,测量范围覆盖-50℃到+300℃,留有余量。

引线电阻是RTD测量的经典误差源。两线制接法下,导线本身的电阻(比如2米长的0.5mm²铜线,电阻约70mΩ,往返140mΩ)会叠加到传感电阻上,导致几度的偏差。所以我在板子上预留了三线制接口,硬件上采用"恒流源+差分测量"的改进方案:

  • IDAC1电流从RTD一端流入,经传感器后从另一端汇出;
  • 同时测量RTD两端电压(其中一端的走线上没有负载电流,只有检测电压)。

这样迈过导线的电压降,测量值就是纯传感器电阻上的压降。软件上再结合一次"零电流测量"来消除ADC输入偏移电压的影响。AD122U04的测量序列支持先测一次带激励电压、再测一次无激励电压、然后自动相减,这个功能叫"测量消除",正是为桥式和RTD应用准备的。

2.3 NTC热敏电阻在HVAC中的应用与低成本方案

对于回风温度这类精度要求±0.5℃即可的测点,我用的是B值3435的10kΩ NTC热敏电阻,而不是铂电阻,成本可以降低一个数量级。NTC接法很简单:NTC串联一个10kΩ精密电阻,跨接在ADC的正输入端和REF+之间,负输入端接REF-,构成比例测量。

这种"比例式"测法的好处是:ADC的输出码值与基准电压成正比,所以不需要高精度基准源,电源波动会被自动抵消。ADS122U04的内部基准源(2.048V或2.5V可选)在这里的作用是给串联电阻分压电路提供激励,而测量结果只取决于两个电阻的比值,基准漂移基本不影响精度。实际测量NTC电阻的公式:

RNTC = RREF × Code/(2^24 - 1)

然后查NTC的R-T表或者用Steinhart-Hart方程反算温度。B=3435的10kΩ NTC,0℃时约33.5kΩ,25℃时10kΩ,100℃时约830Ω,跨度很大,好在24位ADC在全量程范围内分辨率都足够,不需要像10位ADC那样分段拟合。

3. ADS122U04IPWR寄存器配置与UART通信协议细节

3.1 上电时序与硬件复位注意事项

ADS122U04IPWR上电后默认是睡眠模式,需要主控通过UART发送命令来唤醒并配置寄存器。硬件上有一个RESET引脚,低电平有效,复位后芯片会回到已知的默认状态,所有寄存器恢复默认值。这个引脚不能悬空,最好接一个100kΩ上拉电阻。

上电时序上有个容易被忽略的问题:AVDD(模拟电源)必须先于DVDD(数字电源)到达,或者至少同时到达。如果数字IO在模拟部分之前提前上电,会导致内部ESD保护二极管正向导通,芯片进入闩锁风险。这里我用了电源监控芯片来控制上电顺序:先给模拟电源,延迟10ms后再给数字电源,实测芯片从未出现异常发烫或电流过大的问题。

3.2 UART指令集解析:唤醒、RREG/WREG、启动转换

ADS122U04IPWR的UART协议很简洁,所有命令都是1~4字节的组合。最关键的一条经验是:任何命令之前必须先发送两个0x00字节来"唤醒"芯片(因为UART空闲电平是高电平,0x00的起始位能确保时序同步),然后立即发送真正的命令字节。

寄存器读写命令格式如下:

  • 写寄存器(WREG):命令字节0x43,后跟3个数据字节(寄存器0x00、0x01、0x02的内容);
  • 读寄存器(RREG):命令字节0x20,后读3个数据字节;
  • 启动单次转换:命令字节0x1C;
  • 连续转换模式:命令0x14。

寄存器0x00控制输入多路复用器选择、PGA增益、输入模式(单端/差分);寄存器0x01配置数据速率、滤波器模式和工作模式(单次/连续);寄存器0x02配置电流源、基准源大小和基准源位置。这里给出我实际使用的配置代码(基于PIC32的UART驱动):

// 配置ADS122U04: 差分输入AIN0-AIN1, PGA=8, 数据率=1000SPS uint8_t reg_data[3]; reg_data[0] = 0x11; // AIN1-AIN0差分, PGA=8 reg_data[1] = 0x84; // 连续转换, 1000SPS, 正常滤波 reg_data[2] = 0x0C; // IDAC1=500uA, IDAC2=off, 基准=内部2.048V // 唤醒 uart_send_byte(0x00); uart_send_byte(0x00); uart_send_byte(0x43); // WREG命令 uart_send_byte(reg_data[0]); uart_send_byte(reg_data[1]); uart_send_byte(reg_data[2]); // 等待DRDY引脚拉低前,先等待50ms完成内部初始化 delay_ms(50); // 启动连续转换 uart_send_byte(0x00); uart_send_byte(0x00); uart_send_byte(0x14);

数据手册要求上电后等待14ms,然后发送两个0x00字节后,芯片才会进入待命状态。这个等待时间不能省,否则发送的唤醒字符会被当成噪声忽略,芯片一直不理你。

3.3 数据就绪检测:DRDY中断为什么比轮询可靠

芯片转换完成时,DRDY引脚会拉低并持续几个微秒,然后重新拉高。务实的做法是把DRDY连接到PIC32的外部中断引脚(比如INT0),下降沿触发中断,在中断服务函数里通过UART读取24位转换结果。为什么强调用中断而不是轮询?因为UART读取是异步的,如果主控正在执行一个长任务(比如跑Modbus从站处理),轮询模式可能错过DRDY信号,导致数据读取滞后甚至误读。

读取转换结果的命令很简单:发送0x00、0x00、0x10(RDATA命令),然后接收3个字节(最高字节、中间字节、最低字节)。三个字节拼接成一个有符号整数:

int32_t raw = (buf[0] << 16) | (buf[1] << 8) | buf[2]; if (raw & 0x800000) raw |= 0xFF000000; // 24位有符号扩展 float voltage = raw * 78.125e-6f / 128.0f; // 满量程±78.125mV时LSB≈9.53nV

3.4 单次转换与连续转换模式的选择依据

ADS122U04IPWR支持三种数据获取方式:单次转换(通过命令0x1C触发一次)、连续转换(0x14)、以及"自动轮询"模式(芯片内部自动切换输入通道)。连续转换模式下,只要读取数据就行,CPU开销最小,但每次切换到新通道时,必须等一个建立时间,否则前一个通道的残余信号会影响新通道的首次读数。

系统要求10Hz刷新率、4路通道,换算下来每路25Hz,ADS122U04的数据速率设在200SPS就够,每个通道采集5次取平均。我踩过的一个坑是:在连续模式下直接切换多路复用器后立即读数据,前一个通道的电荷还在滤波器里,导致读数跳变。解决方法是:软件上在切换通道后丢弃前2次转换结果(约10ms),从第3次开始采样。

4. PIC32MX675F512L 侧的UART初始化和数据流设计

4.1 UART外设的初始化与FIFO深度利用

PIC32MX系列的UART模块和PIC18、PIC16的差别挺大,寄存器结构不一样,而且带了一个收发FIFO(发送和接收各4级)。初始化UART2用于ADC通信时,关键配置如下:

U2MODEbits.BRGH = 1; // 高速波特率模式,误差更小 U2MODEbits.PDSEL = 0b00; // 8位数据,无校验 U2MODEbits.STSEL = 0; // 1位停止位 U2BRG = 52; // 时钟80MHz下,波特率115200(高速模式),BRG=52 U2STAbits.UTXEN = 1; U2STAbits.URXEN = 1; U2STAbits.URXISEL = 0b00; // 接收中断:任意字节就触发 U2MODEbits.ON = 1;

BRG的计算公式是:BRG = (Fpb/(16×Baud)) - 1,其中Fpb是外设总线时钟。我用80MHz外设时钟,115200波特率,BRG=42.4,取整为43,实际波特率误差只有0.3%左右,完全在UART容差范围内。注意BRGH=1的模式下误差更小,标准模式下BRG误差略大,长帧(发送多个连续的0x00唤醒字节)时可能触发错误帧中断,所以这里选了高速模式。

接收FIFO只有4级深度,在连续读取ADC数据的场景中,如果主控在处理其他中断(比如定时器节拍中断)时FIFO溢出,数据就会丢失。解决办法是在接收中断服务函数里立即把FIFO中的数据搬到RAM环形缓冲区,而不是在中断里做业务处理。我在RAM里开了一个256字节的环形缓冲区,ADC读取、Modbus收发共用,实测即使主控被中断打断几百微秒,数据也不会丢。

4.2 异步读取时的超时与重试机制

UART通信有个天生的缺点:如果对方没有应答,主控永远不知道是命令丢了还是数据丢了。所以在软件上必须建立一个超时机制:发送读取命令后,开启一个5ms的软件定时器,如果在超时时间内没有完整收到3个数据字节,就向上层报告"通信超时",并在下几个周期尝试重新同步。

重试的思路也很关键:不要单纯重发同一帧,而是先发送连续两个字节0xFF(UART的空闲字节,相当于重置接收状态),把对方可能卡在半途的接收状态机清掉,然后再发送唤醒字节和读取命令。这个"软复位"技巧我用在长线现场调试时效果很好,至少减少了70%的通信故障。

4.3 温度换算的工程化处理:查表、多项式与标定

拿到24位ADC码值后,最后一步是把它变成有意义的温度值。不同传感器换算路径不同,下面分开说:

热电偶链路:码值→电压→补偿电压合成→查表/多项式→温度。K型热电偶的NIST多项式是高阶的,PIC32在处理浮点多项式时也不算吃力,但为了避免频繁计算,我在Flash里存了一张1℃步进的K型热电偶毫伏-温度对照表(覆盖-50℃到500℃),软件用线性插值搞定。每0.1℃的插值误差小于0.02℃,足够用。查表比多项式快3倍以上,而且Flash空间占用完全可接受。

RTD链路:码值→电阻值→Callendar-Van Dusen方程→温度。PT100在0℃以上部分还算线性,0℃以下有轻微偏差。CVD方程典型形式:

R(T) = R0[1 + A·T + B·T²],T>0℃ R(T) = R0[1 + A·T + B·T² + C·(T-100)·T³],T<0℃

系数A=3.9083e-3,B=-5.775e-7,C=-4.183e-12(标准值)。这个方程用浮点计算不算复杂,PIC32在80MHz主频下算一次只需要几微秒。

NTC链路:码值→电阻值→Steinhart-Hart方程。这个是三系数方程,精度依赖系数拟合质量。我用的B=3435的NTC,直接查表比拟合方程更可靠,因为不同批次NTC的B值有偏差,查表配合实际标定一定更准。

整个换算流程中,最容易被忽略的是"标定"。ADC和传感器都有偏差,理论换算和实际温度可能有1~2℃的系统偏差。实用方法是:做成成品前用水浴炉(或干井炉)在0℃、25℃、50℃、75℃、100℃五个温度点标定,把每路的修正系数存到PIC32的Flash数据区(通过Bootloader预留的存储区),工作时用线性插值补偿。这套流程做完后,系统整体精度从裸机状态的±1℃提升到了±0.2℃。

5. 实机调试中踩过的三个大坑

5.1 问题一:读回来的码值杂乱无章,完全对不上信号

现象:首次上电后读取的ADC码值像随机数,有时正、有时负,数值每秒跳动几百个码。

排查过程:先用示波器测量DRDY引脚的波形,确认芯片确实有转换动作;再量UART的TXD/RXD信号,发现波形有毛刺且幅值只有2V左右。问题出在PIC32和ADS122U04之间用了10cm杜邦线连接,连接线上有串扰,加上3.3V电源纹波较大(开关电源纹波约100mV),导致UART通信偶尔误判。

修复方案:把UART线改用双绞屏蔽线,电源部分加了LC滤波(10µH电感+22µF钽电容),ADC的模拟电源AVDD和DVDD之间串联了一个磁珠+两个100nF电容做进一步隔离。整改后码值稳定到噪声仅为几个LSB,信号对得上。

经验总结:ADS122U04对AVDD的噪声非常敏感,模拟电源最好用LDO单独供电,不要直接吃开关电源的输出。很多看似是通信问题,根源往往是电源噪声通过耦合进入信号链路。

5.2 问题二:热电偶温度读数比标准表低10℃左右

现象:热电偶实测温度始终比Fluke温度表的读数低约8~12℃,而且这个差值随环境温度轻微变化。

排查过程:这个差值非常规律,一开始以为是冷端补偿公式写错,查了代码也没发现明显问题。后来手动测量冷端传感器的真实温度,发现PCB表面在运行一段时间后比冷端传感器本身温度高了2~3℃。再追下去,发现冷端传感器和接线端子之间导热路径太长,端子温度并没有完全传递到传感器上。

修复方案:把数字温度传感器从PCB上挪到接线端子正下方,并给两者之间涂了导热硅脂。同时给附近的电源芯片加了一小块散热片,减少热风对冷端传感器的影响。整改后冷端补偿误差降到±0.3℃以内,系统读数与标准表差距缩小到±0.5℃,并稳定下来。

经验总结:冷端补偿精度不取决于传感器本身的精度,而取决于它和热电偶"冷端"之间的热阻。PCB布局上冷端传感器必须热耦合好,同时要远离功率器件。这是热电偶应用里最容易忽视、也最容易翻车的细节。

5.3 问题三:长距离通信时偶发丢字节,导致数据毛刺

现象:一个现场项目把ADC板放在室外,主控在室内,两者之间用4芯屏蔽线拉到50米。运行一段时间后发现偶尔会有某个通道的温度读数突然跳到-50℃或+300℃,持续几百毫秒后恢复正常。

排查过程:示波器捕捉后发现,偶发的错误数据来源于UART接收时字节错位。原因可以追溯到长线传输时信号上升沿变缓,加上电磁干扰,UART的波特率时钟在两台设备间产生微小偏差,长时间运行后累计出界。

修复方案:三层对策组合——第一,物理层加装RS-485收发器,把单端UART变成差分信号传输;第二,软件上在ADC读取帧中加入校验机制(发送命令后校验收到的3个数据字节是否满足范围合理性,明显超量程的数据标记为无效帧);第三,协议层增加重同步机制(连续两次无效帧后,主控进入复位序列,重新发送唤醒字符初始化)。整改后现场再没有出现过毛刺数据。

经验总结:UART只是协议层,物理层还是要点对点的差分传输才可靠,尤其是工业现场。RS-485收发器(如MAX3485)成本很低,却可以把传输距离从几米提升到100米以上,值得当作标配来设计。

6. 这套测温方案的性能表现与可复用设计参考

最终整个板子通过标定后的实测指标如下:

传感器类型测量范围精度(标定后)刷新率备注
K型热电偶-50~500℃±0.3℃20SPS冷端补偿后实测
PT100(三线)-50~300℃±0.2℃50SPS500µA激励
NTC 10kΩ-40~150℃±0.5℃100SPS比例测量

单颗ADS122U04IPWR支持4路模拟输入,因此整个板子目前只用了1颗ADC就实现了4路温度采集。如果后续需要扩展到8路,可以加一颗芯片,通过PIC32的第二路UART连接,通信协议完全一样,代码复用率很高。

从可复用角度,有几个设计经验可以直接抄:

  • 电源架构上,模拟3.3V和数字3.3V用磁珠隔离,ADC的AVDD单独一颗低噪声LDO,避免数字噪声影响模拟性能。
  • UART线建议在PCB上做差分对走线,即使不接RS-485,也可以提高信号质量。
  • 四个通道共用一个基准源,但热端测量、冷端补偿、RTD激励都围绕同一颗芯片完成,芯片内部自带的基准源和电流源匹配性比外部分立方案更好。
  • 数据格式上行统一用Modbus RTU,寄存器地址按通道分配,方便接入组态软件或楼宇自控系统,不需要额外定制协议。

实际使用中的体会是,ADS122U04IPWR + PIC32MX675F512L这个组合非常适合中低成本的工业测温面板。24位ADC保证了分辨率,UART接口让传感器板可以灵活布置,PIC32则有充裕的算力做滤波和协议处理。相比直接用PLC模拟量模块的方案,这套东西成本低一半,精度还更高一些。调试中最花时间的不是写代码,而是把PCB布局、电源和冷端补偿这些"模拟基本功"打磨到位。如果你正在做类似的温控项目,建议先把这一层理清楚,再动手写代码不迟。