ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

24位ADC+8位单片机:高精度温度采集实战指南

24位ADC+8位单片机:高精度温度采集实战指南

去年做楼宇自控项目时遇到一个很典型的需求:客户要求把风管温度、冷冻水供回水温度、以及压缩机排气管壁温度统一采集上来,测量精度做到±0.2°C,而且主板必须用他们指定的8位单片机。我当时第一反应是用MCU内置ADC配NTC,但把指标铺开一算就放弃了。最终方案是ADS122U04IPWR加PIC18F4682:一块24位delta-sigma ADC专门干模拟测量活,一块8位MCU负责协议、通信和控制。这篇文章就把这套组合从选型逻辑、硬件连接、固件驱动到温度换算的关键细节完整拆开讲一遍,适合正准备做多点温度采集、又不想把模拟前端搞得太复杂的嵌入式工程师参考。

1. 为什么是"24位ADC + 8位单片机"这种组合

1.1 PIC18F4682 内置 ADC 的短板

PIC18F4682 本身带一路10位ADC,通道数不少,但放在温度测量场景里完全不够看。10位分辨率配上5V参考电压,一个LSB大约5mV;PT100在1mA激励电流下每摄氏度只变化0.385mV,换算成温度一个LSB就是13°C左右。这种分辨率只能判断"机器有没有开机",根本谈不上"测量"。就算把参考电压改小、外面加一级运放,10位的量化噪声和单片机ADC的温漂仍然摆在那里。工业现场环境温度从-25°C到85°C变化时,零点漂移完全压不住,测出来的数据今天和昨天能差出好几十个计数。

更麻烦的是接口适配。温度采集板往往要处理两三种传感器:风管温度用PT100的浸入式探头,水管温度用PT1000的三线制探头,排气温度可能要用K型热电偶。每路的信号幅度、激励方式、线性化曲线都不一样。如果全部用分立运放和偏置电路去搭,PCB面积和调试成本会迅速膨胀。把前端推到一颗可编程ADC里,用寄存器切换输入通道、增益和激励电流,换传感器类型就从硬件改动变成了纯软件配置,这才是省事的路子。

所以选型逻辑很直接:模拟前端交给ADS122U04IPWR这种24位器件,MCU只做控制、线性化和通信。24位分辨率配合内部可编程增益放大器,PT100的信号可以直接放大到合理幅度再量化,理论分辨率能到0.001°C级别,实际受噪声限制也能稳定达到0.01~0.1°C,这对HVAC的±0.2°C要求是足够的。

1.2 为什么锁定 UART 接口版本而不是 SPI 版

ADS1220系列本身有SPI版本,但我特意选UART接口的ADS122U04IPWR,最直接的原因是PIC18F4682内部有现成的EUSART模块。SPI通信要芯片选择线、要严格的主从时钟时序,而UART只需要发送和接收两根线,加一个数据就绪脚就够了。对于想省引脚、或者对SPI时序不熟的项目,UART版确实友好。

UART接口还有个工程上的好处:帧结构本身带起始位和停止位,天然有边界对齐信息。再配合器件自带的CRC校验,在几十厘米到一两米的板间走线上比裸SPI在干扰环境下的鲁棒性更好。SPI一旦时钟线受干扰,数据错位很难及时发现;UART至少能通过校验发现坏帧,然后重新发命令。在HVAC机组柜内这种有风机、变频器、继电器切换的电磁环境里,这个特性很值得看重。

最后提一下封装型号的命名。ADS122U04IPWR里的"I"表示工业级温度范围,工作温度-40°C到125°C,比商用版更适合户外安装的空调外机和工业设备;PW是TSSOP-16封装;R是卷带包装。这个细节决定采购时不要买错温度等级,工业现场裸露安装的传感器板经常被晒到60~70°C,商用级器件余量很小。

1.3 双芯片分工:谁负责什么

这套架构里两块芯片的边界很干净。

ADS122U04IPWR做四件事:四路模拟输入复用、可编程增益放大、24位模数转换、通过UART把转换结果输出去。它内部还有两个可编程的激励电流源,可以直接给RTD提供激励电流,省掉了外面搭恒流源的电路;又带内部温度传感器,热电偶冷端补偿可以直接读芯片温度来估算。

PIC18F4682负责所有"非测量"的工作:初始化ADC、读UART数据、做线性化和数字滤波、把温度值换算成BACnet MS/TP或Modbus RTU报文、通过RS-485收发器或者ECAN模块跟上位机通信、驱动LCD和继电器。64KB程序存储器、3.3KB左右的SRAM加上1KB数据EEPROM,对于温度线性化查表、存储标定参数这种工作完全够用。

我特别建议把"传感器语义"处理在MCU侧完成,而不是把原始ADC码透传给上位机。前期调试时透传省事,但后面接入楼宇自控的点表时,上位机需要知道每个通道对应什么传感器、用什么公式换算,维护成本很高。我在这个项目里的做法是MCU侧把每个通道处理成标准物理量——温度就输出°C的浮点值,上位机看到什么就是什么,无论接哪个控制器语义都一致。

2. 硬件连接与布局的关键点

2.1 UART 三线接法与 SCLK 的处理

ADS122U04IPWR 的UART接口逻辑上就是标准的三线串口加一个就绪脚。硬件连接可以用一张表清楚说明:

信号ADC引脚PIC18F4682引脚说明
TXDTXDRC7(RX)ADC发送数据给单片机
RXDRXDRC6(TX)单片机发送命令给ADC
DOUT/DRDYDOUT/DRDYRB0(INT0)数据就绪,下降沿触发中断
RESETRESETRA3上电复位或手动复位
SCLKSCLK可配IO/时钟输出视时钟模式而定
电源地DGND/AVSS系统地单点连接

这里最需要留意的是SCLK。UART版ADC依然保留了SCLK脚,具体接法跟器件的时钟模式有关:如果采用器件内部振荡驱动的异步模式,SCLK可以接固定电平;如果采用主机提供节拍的方式,就需要单片机给一个干净的连续时钟。我做的板子上给SCLK加了一个跳线帽,两种模式都能切,调试时切换很方便。实际项目里主机提供时钟这种模式对时钟质量要求不高,用PIC18F4682的CCP模块产生一个固定占空比方波即可,但要注意在ADC初始化期间时钟要保持稳定,不要在配置寄存器过程中让时钟抖动。

配波特率时,我用的是PIC18F4682外部20MHz晶振,EUSART配成115200、8N1。单片机端要计算SPBRG寄存器值和BRGH位,公式是SPBRG = Fosc/(16×波特率) - 1,20MHz下配115200时SPBRG约等于10。UART两端的波特率误差必须控制在正负2%以内,建议用逻辑分析仪先抓一次实际波形确认帧格式正确再写驱动逻辑。

2.2 RTD 输入电路和热电偶输入电路

RTD部分推荐直接用ADC内部的IDAC电流源做激励,这样能实现比值测量。做法是把两个激励电流源中的一个配置成输出到AIN0,电流流经RTD后回到参考电阻,再由同一个电流源返回系统地。用另一个ADC通道或者复用同一通道测量参考电阻两端的电压,RTD电阻就等于参考电阻乘以两个电压的比值。由于激励电流同时流过标准电阻和RTD,电流源的波动和温漂在比值运算中被抵消,这是最省心的精密测量方式。

3线制PT100要考虑引线电阻补偿。标准做法是让两条引线分别承载激励电流进入RTD,第三条引线作为电压检测的回线,利用电流源1和电流源2同时激励,使两边的引线压降在差分测量中抵消。4线制则更加直接,激励和检测完全分开,引线电阻基本不影响结果。工业现场如果探头离主板比较远,我优先用4线制;HVAC风管里的短距离探头用3线制也能达到足够精度。

热电偶部分则要靠PGA放大。K型热电偶灵敏度大约40μV/°C,环境温度到800°C时输出电压大约33mV。PGA增益128时满量程约±16mV,所以不能一路用128倍增益测量高温段;可以分档配置,低温段用高增益提高分辨率,高温段降低增益防饱和。冷端补偿我利用ADC内部温度传感器,把安装在热电偶接线端子附近的PCB温度读出来后,查热电偶多项式算出冷端修正电压,加到测量值上。

推荐在ADC的模拟输入前加一级RC滤波。差分对地各放一个10nF电容,差分之间放一个1nF电容,配合限流电阻组成低通,能有效抑制变频器PWM载波耦合进来的高频干扰。这个电容值不能太大,否则激励电流建立时间变长,数据采集的第一个转换周期要主动丢弃。

2.3 电源和PCB布局

ADS122U04IPWR的模拟电源和数字电源要分开走,模拟地和数字地采用单点连接,连接点选在ADC的散热焊盘附近。电源引脚上0.1μF和10μF电容并联布置,参考电压REFP/REFN引脚更要重点去耦,参考电压一旦被扰动,24位精度的低几位全是噪声。

一个常见的坑是板上有DC-DC开关电源给MCU供电,DC-DC输出纹波直接进了ADC的AVDD。解决办法是在ADC的电源入口加一级低噪声LDO,或者至少用磁珠把模拟电源和数字电源隔开。温度采集板的PCB布局尽量把ADC放在远离继电器、MOSFET驱动这些大电流开关器件的地方,如果实在没法远离,给ADC区域加一个简单的铺地铜皮屏蔽带效果明显。

3. 固件实现:PIC18F4682 的驱动与寄存器配置

3.1 EUSART 初始化

PIC18F4682的EUSART配置本身不复杂,但要注意初始化顺序。先关闭发送和接收,配置SPBRG和TXSTA、RCSTA寄存器,然后开启接收中断。一个可用的初始化函数如下:

void uart_init(void) { TRISCbits.TRISC6 = 0; // RC6 = TX TRISCbits.TRISC7 = 1; // RC7 = RX SPBRG = 10; // 20MHz, 115200, BRGH=1 TXSTA = 0x24; // BRGH=1, TXEN=1, 8位数据 RCSTA = 0x90; // SPEN=1, CREN=1 RCREG = 0; // 清空接收缓冲 }

注意接收中断标志PIR1里的RCIF在使能接收后可能因为残留数据立刻置位,所以初始化后要主动读一次RCREG清掉假数据。底层的发送和接收函数,我习惯用阻塞式加超时计数器,避免UART卡死时程序整个挂住。每次发送命令后要等待应答,连续三次没有应答就判定ADC异常,置一个故障标志,这样上位机至少能看到"通道通信故障"而不是看到一堆乱码。

3.2 ADS122U04 的初始化命令序列

ADS122U04IPWR的寄存器配置遵循TI delta-sigma ADC的常见风格:先复位,再写配置寄存器,然后进入连续转换或者命令触发模式。命令字节的具体编码在数据手册里有明确定义,UART版跟SPI版的命令集可能不同,所以写代码时不要想当然套用。下面这段代码用宏区分命令和寄存器,便于替换:

#define CMD_RESET 0x06 #define CMD_RDATA 0x10 #define CMD_WREG 0x40 void ads_write_reg(uint8_t reg, uint8_t val) { uint8_t cmd = CMD_WREG | (reg << 2); // 命令拼接规则以手册为准 uart_write(cmd); uart_write(0x00); // 只写一个寄存器 uart_write(val); } void ads_init_rtd(void) { uart_write(CMD_RESET); __delay_ms(10); ads_write_reg(0x00, 0x00); // AIN0/AIN1差分,内部PGA ads_write_reg(0x01, 0x20); // 增益配置按实际需求调整 ads_write_reg(0x02, 0x40); // 内部2.048V参考,IDAC设置 ads_write_reg(0x03, 0x00); // IDAC输出到AIN0 }

配置有几个容易忽略的细节。第一,每次写配置寄存器后要等一小段建立时间,尤其是改了输入多路复用开关和PGA增益之后,第一次转换结果必须丢弃。第二,内部温度传感器默认是关闭的,做热电偶冷端补偿前要专门使能。第三,IDAC电流值选择要与RTD的阻值范围匹配。PT100在500°C时大约280Ω,1mA激励电流产生的压降约280mV,用PGA增益8时这个信号刚好落在线性区;如果电流给到1.5mA,100Ω低温段的功耗虽然只有0.2mW,但在狭窄风管探头里可能造成0.1°C以上的自热误差,这个后面细说。

3.3 24位数据的读取和解析

读取转换结果有两种方式。一种是在主循环里周期发送RDATA命令:单片机给ADC发一个读数据命令,ADC会把最新转换结果逐字节回传。另一种是配合DOUT/DRDY引脚,当转换完成时这个引脚拉低,触发PIC18F4682的INT0外部中断,在中断里置标志,主循环看到标志后再发RDATA命令。第二种方式时序更可靠,不会因为主循环繁忙而错过数据帧边界。

典型的数据解析代码:

volatile uint8_t adc_ready_flag = 0; void __interrupt() adc_isr(void) { if (INT0IF) { INT0IF = 0; adc_ready_flag = 1; } } int32_t ads_read_data(void) { uint8_t buf[4]; uart_write(CMD_RDATA); for (uint8_t i = 0; i < 4; i++) { buf[i] = uart_read_timeout(10); } int32_t raw = ((int32_t)buf[0] << 24) | ((int32_t)buf[1] << 16) | ((int32_t)buf[2] << 8) | ((int32_t)buf[3]); return (raw >> 8); // 取高24位有效数据 }

如果配置里开启了CRC校验,回传的字节里会多一个CRC字节,上面代码就要改成接收4个数据字节加1个CRC字节,校验失败时丢弃本次数据并重新同步。刚开始调试时建议先关闭CRC,等基本功能跑通、确定通信帧没有错位风险后再打开。否则帧一旦偏移,CRC就会连续报错,反而掩盖真正的问题。

解析得到的24位数据是有符号二进制补码格式,满量程对应正负参考电压除以增益。缩放公式是:电压值 = 原始码 × 2×Vref / (增益 × 2^24)。把原始码先转成float再乘缩放系数,后续线性化更方便;如果MCU资源紧张,可以在整数域做定点运算,但要注意中间结果别溢出。

4. 温度换算:从原始码到工程单位

4.1 PT100 的比值测量和线性化

RTD部分用比值测量可以把激励电流的漂移影响基本消除。读取两路电压后,RTD电阻直接等于:

R_rtd = R_ref × V_rtd / V_ref

参考电阻的阻值和温漂决定了整体精度,所以参考电阻一定要选低温漂的精密电阻,0.1%精度、25ppm/°C以下才合格。我用的是2.7kΩ金属箔电阻,实测温漂在允许范围内。

得到电阻值后,再换算温度。PT100有标准的IEC 60751多项式,也就是Callendar-Van Dusen方程。0°C以上用二次式解一元二次方程,0°C以下加C³修正项。下面给出一个适用于PIC18F4682的参考实现,用牛顿迭代处理负温度区间:

float rtd_to_temp(float R) { const float R0 = 100.0f; const float A = 3.9083e-3f; const float B = -5.775e-7f; const float C = -4.183e-12f; float t; float disc = R0*R0*A*A - 4*R0*B*(R0 - R); if (disc < 0) disc = 0; t = (-R0*A + sqrtf(disc)) / (2*R0*B); if (t < 0) { // 先代入不含C的近似解,再迭代2次修正 float dt; for (int i = 0; i < 3; i++) { float r_calc = R0*(1 + A*t + B*t*t + C*(t-100)*t*t*t); float dr_dt = R0*(A + 2*B*t + C*(4*t*t*t - 300*t*t)); dt = (r_calc - R) / dr_dt; t = t - dt; } } return t; }

LX8编译器用浮点库会额外占几KB Flash,但PIC18F4682有64KB程序空间,压力不大。如果对程序空间抠得比较紧,可以采用查表加线性插值:从-50°C到500°C每2°C一个点,510个点用uint16存储电阻值,只占1KB,查完用直线插值,误差小于0.05°C。

4.2 热电偶的冷端补偿与非线形校正

热电偶的输出电压与温度是非线性的,同样需要校正。K型热电偶在0°C到1000°C区间可以用NIST ITS-90多项式反推温度,但在8位MCU上直接算高阶多项式不太划算,我建议用分段查表加插值。把K型热电偶的分度表做成0.5°C间隔的查找表,电压用mV定点表示,总共不到5KB,查表加插值的误差在0.1°C以内。

冷端补偿是热电偶最容易出偏差的地方。ADS122U04IPWR内部温度传感器测的是芯片自己的温度,它跟热电偶接线端子之间通常有几度的温差,尤其是PCB上有MCU这种发热大户。所以不要直接把内部温度当作冷端温度,应该把热电偶的冷端接到一个物理上靠近端子的小型NTC或者PT100上,用那颗传感器测冷端温度,再通过热电偶反向多项式算出等效电压加到测量电压上。只有PCB布局把ADC和热电偶端子放得非常近、且板上没有明显热源时,才可以直接用内部温度传感器。

补偿的实际代码逻辑大致是:读冷端温度Tcjc,查K型分度表得到冷端电压E_cjc,读热电偶回路总电压E_total,实际热端电压E_hot = E_total + E_cjc,再用E_hot查分度表反推热端温度。这个"加电压"而不是"加温度"的顺序很容易写错,写成温度相加会导致非线性误差被放大。

4.3 数字滤波与工频干扰抑制

ADS122U04IPWR内部数字滤波器在20SPS数据速率下对50Hz和60Hz都有不错的抑制能力,这对HVAC现场特别重要。电网的共模干扰一旦耦合到传感器线上,在转换结果里会出现明显的50Hz纹波。所以能接受较低采样率时,我优先把数据速率配成20SPS,等于同时完成了采样和工频滤波。需要更快响应时,再在软件层做滑动平均。

软件滤波我一般做两层。第一层是滑动平均窗口,窗口大小根据现场响应要求调整,PT100这类时间常数本来就比较大的传感器,用8个采样点做平均不会让响应明显变慢。第二层是中值滤波,主要剔除偶发的冲击噪声——比如压缩机启动瞬间继电器跳变带来的毛刺。先取5个原始值排序取中间值,再做滑动平均,抗冲击效果比单纯平均好很多。在8位MCU上,5个值的排序用简单的插入排序就够,开销很小。

5. 从 HVAC 到工业现场:应用落地的具体问题

5.1 HVAC 风管和水管测温的探头安装

HVAC里的测温点各有各的脾气。风管温度传感器经常遇到气流分层问题,风管截面大、风速低时,上下温差能到2°C以上。如果在单点测,结果没有代表性。这种情况应该用平均温度探头,把多支PT100元件并联在风管截面的不同位置,或者用一根拉长的NTC线缆传感器横穿风管,让传感器本身覆盖整个截面。

水管温度测量用的是浸入式热井。冷冻水供水和回水的温度直接关系到冷机的能效计算,热水系统的温度则决定换热量的调节,所以这一类传感器对精度要求最高。安装时热井要插入管道中心线位置,探头插入深度不够会导致管道壁面温度影响测量。热井里通常要填充导热硅脂或导热膏,否则探头和热井壁之间会有空气间隙,响应时间会从几秒变成几分钟。

室外温度传感器的安装最容易被忽视。我曾经看到室外温度探头直接贴在阳光直射的墙面,中午读数比旁边气象站高8°C。室外探头必须装在通风良好的防辐射罩里,避免阳光和建筑热辐射直接加热传感器。同理,新风机组的传感器不要装在回风混合段出风口附近,那里气流温度已经和室内混合,测不准确室外温度。

5.2 制冷剂管壁温度和工业场景

工业场景更复杂。电机绕组温度一般用PT100埋入式探头或者绕线探头,轴承温度用更小巧的P1000探头,变压器油温则用浸入式探头配球形热井。压缩机排气温度用K型热电偶夹持在管壁上,热电偶的测温端要和管壁良好接触,外面包保温棉,否则环境风会把管壁热量带走,读数明显偏低。管道振动也是个隐患,长期振动会导致热电偶引线疲劳断裂,固定时要用耐温扎带留出应力释放弯。

这类现场信号还有一个共同问题:探头到主板的走线很长。ADC和单片机在同一块板上时,UART是板间通信没压力,但传感器信号线的延长会引入干扰。正确的原则是把模拟信号在ADC附近就近数字化,传感器探头出来的线尽量短;确实要远距离传输时,选用屏蔽双绞线并单端接地,不要把屏蔽层两端都接地形成地环路。

5.3 与上位机的通信选型和系统自检

采集层做完之后,数据要交给上位机。PIC18F4682的EUSART可以再配一路RS-485接口跑Modbus RTU或BACnet MS/TP,ECAN模块则可以直接接CAN总线。楼宇自控项目里BACnet MS/TP很常见,工业项目Motbus RTU更普适。建议给每一路温度通道都加上限值和变化率监视,温度突变超限时主动报警,而不是把故障数据发出去让上位机判断。

上电自检时我会先读一遍ADS122U04的内部温度传感器和内部参考电压,如果和预期偏离过多,判定ADC本身异常。再把输入多路复用器切到AVSS,测量系统零位,如果零位漂移超过阈值,记录下来供校准。工业现场的校准周期通常按年计,把标定参数存进PIC18F4682内置的EEPROM,下次开机自动加载,避免换板子后参数丢失。

6. 实测数据与调试踩坑记录

6.1 不同数据速率下的噪声表现

以下是我在这块板上实测的有效位数和温度噪声表现,环境不同会有差异,但趋势很典型:

数据速率(SPS)PGA=1 有效位数PGA=128 有效位数对应PT100温度噪声
2022~2318~19约0.005~0.02°C
902117约0.01~0.05°C
3302016约0.02~0.1°C
10001814约0.1~0.5°C

这个表能直观说明一件事:追求高采样率就会牺牲分辨率,温度系统大多数场景1SPS就够用。我最终把全系统采样率固定为20SPS,参考电阻比值测量加两层软件滤波后,实测PT100通道的重复性在0.02°C以内,完全满足HVAC现场要求。如果系统要求1秒内完成多通道扫描,20SPS意味着每通道最多占用5个采样点的时间窗口,设计时要提前算好ADC的转换节奏和UART读取节奏,避免通道间数据错位。

6.2 两个典型的调试点坑

第一个坑是UART错帧。初期直接把RDATA命令放在主循环里周期发送,没有用DRDY中断做同步。当主循环被通信任务占用时间变化时,命令发出去了但ADC正在更新数据,读回来的3个字节可能跨越两次转换边界,解析结果就变成混乱码。问题看起来是数据抖动,实际是帧同步失效。后来把DOUT/DRDY接到RB0的INT0中断,转换完成才触发读取,错帧问题彻底消失。这个经验适用于所有带就绪脚的ADC:别偷懒,一定要用数据就绪信号做读取锚点。

第二个坑是PT100自热。调试时为了简化配置,把IDAC统一设成1.5mA,结果发现读数比标准温度计偏高0.15°C左右,而且探头放在流动空气中偏差变小、放在静止空气中偏差变大——典型的自热特征。PT100功耗是I²R,1.5mA在100Ω上就是0.225mW,热阻较大时温升不可忽略。最后把电流降到500μA,配合PGA增益16补偿信号幅度,自热误差降到0.02°C以下。设计RTD时,激励电流的选择要结合探头热阻和量程来权衡,追求大信号就牺牲自热,没有免费的午餐。

6.3 冷端补偿偏差的排查过程

热电偶通道还遇到过一次冷端补偿偏差问题。客户反映某个通道温度比别人家的表低4°C,怎么校准都拉不回来。最后发现不是因为ADC电路,而是PCB上有一个给数码管供电的线性稳压器正好压在热电偶端子下面,稳压器表面温度比周围高5°C。热电偶的冷端实际在端子处,而补偿传感器测的是元件温度,温差被整进测量结果。后来把热电偶冷端移到板边并增加隔热片,误差立刻回到正常范围。这个小案例说明,热电偶的精度上限往往不在ADC,而在冷端温度的测量准确度,PCB热设计和传感器安装位置比芯片选型更决定最终精度。

最后再分享一个调试工具经验:UART版ADC调试时,用逻辑分析仪分析仪抓SCLK、TXD、RXD三个信号,比用示波器高效得多。命令发出后立即抓数据,几万次采样在电脑上回放,哪些帧CRC错误、哪些字节偏移,一目了然。相比之下,用串口助手直接看数据只能看到结果,看不到通信过程,遇到偶发错帧几乎无从下手。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进