
前阵子给一栋商业楼的HVAC控制系统做温度采集节点改造涉及风管温度、空调水管温度和环境温度三路传感器目标温度分辨率要到0.01℃级别整机还要照顾功耗。评估一圈之后我选了TI的ADS122U04IPWR四通道24位Δ-Σ ADC搭配ST的STM32L073RZ超低功耗MCU。这套方案已经在我这里跑通了两版硬件稳定性符合预期。不少同行看到这个组合会问一句为什么不用MCU内置ADC这里把选型思路、电路设计、固件实现和调试踩坑完整记录下来给正在做工业传感器采集的朋友一个参考。1. 这套组合要解决的是HVAC温度采集里什么实际问题1.1 工业与HVAC场景面对的三类温度传感器HVAC系统的温度采集看起来简单实际上要同时伺候好几种完全不同的传感器。风管温度和回风温度我们用的是PT1000铂电阻稳定、抗老化适合长期运行空调冷热水管里则是PT100配合一体化温度套管插到管路中室内环境温度用NTC热敏电阻成本低探头小放在面板里很合适。此外个别项目里还要测锅炉排烟温度或热泵压缩机排气管温度这种场合只能用K型热电偶因为量程能到几百度PT100和NTC都扛不住。这三种传感器有一个共同特点输出信号极微弱。PT100在0到100℃范围内阻值只变化约38.5Ω如果以1mA电流激励电压变化只有38.5mV热电偶更是夸张K型热电偶在常温附近灵敏度约为40μV/℃非常小。HVAC控制器的MCU如果直接采样这些信号光噪声和量化误差就够喝一壶。1.2 MCU内置12位ADC为什么不够用STM32L073RZ内部有一个12位ADC规格在通用MCU里算不错但拿到温度测量这个场景就有几个硬伤。首先是分辨率不够。12位ADC在2.048V参考下一个LSB大约0.5mV。PT100在1mA激励下温度变化0.1℃对应的电压变化只有0.0385mV连一个LSB都不到。这就像拿一把最小刻度4.88mm的卷尺去量0.1mm的划痕根本无从谈起。虽然可以用软件过采样做均值提升有效位数但那只是“平滑噪声”改变不了运放和ADC本身的失调、增益误差。其次是参考电压稳定性。MCU内部ADC参考电压通常精度一般温漂也比较明显而且在MCU负载变化时容易波动。工业现场一年四季温度变化可能超过60℃参考电压漂移直接变成测量误差。第三是通道切换问题。内置ADC多通道采集时采样保持电容需要重新充电切换瞬间容易引入误差通道越多越明显。所以我的结论很直接要稳定做0.01℃级别的温度监测必须外置高分辨率ADC。1.3 为什么选ADS122U04而不是SPI接口ADCADS122U04本质上是一个24位Δ-Σ ADC四通道内部带可编程增益放大器PGA、内部参考电压、可配置电流源IDAC还有一个内部温度传感器。最大亮点是它把数据接口做成了UART而不是常见的SPI。做传感器采集的工程师应该能立刻体会到UART接口的好处。SPI需要SCLK、DIN、DOUT、CS四根线信号之间还有时序约束走线稍长就容易出问题而UART只需要TX、RX两根线很多仪表线缆里正好有两芯闲置屏蔽层还好处理。HVAC现场传感器离主控板可能有五米甚至十几米UART用低波特率9600bps跑双绞屏蔽线非常稳这是SPI很难做到的。ADS122U04内部还有两个匹配的可编程电流源IDAC和低边开关这意味着它天生就是为RTD铂电阻设计的。做PT100三线制测量时不再需要外置恒流源芯片电流源、PGA、参考电压都在一颗芯片里外围元件大幅减少。内部温度传感器则可以直接用来做热电偶冷端补偿省掉一颗外置温度芯片。1.4 STM32L073RZ在方案中的定位STM32L073RZ是ST的超低功耗MCUCortex-M0核主频32MHzFlash 192KBRAM 20KB还有6KB的EEPROM。选择它有几个理由一是功耗足够低有多档睡眠模式适合HVAC节点长期通电运行二是串口资源丰富USART数量够用一个串口给ADC一个串口将来扩展RS485读数完全没有问题三是内置EEPROM非常实用校准系数和通道配置可以直接存进去不需要额外挂一颗EEPROM芯片。至于算温度换算、查表、线性化这些工作M0核完全够用毕竟温度采样并不需要复杂算法。2. 硬件电路设计电源、传感器前端与UART连接2.1 供电和去耦别小看这几颗电容ADS122U04的工作电压范围是2.3V到5.5V我这里直接用STM32L073RZ所在的3.3V电源轨模拟和数字部分都用同一个3.3V需要注意的是去耦。我的做法是模拟电源AVDD和数字电源DVDD各自接一个100nF陶瓷电容离电源引脚尽可能近再各加一个10μF钽电容做低频储能。如果项目对电源噪声要求高可以在3.3V入口串一颗磁珠把模拟电源和数字电源隔开一点。实际测试中磁珠对降低ADC输出数据跳动有可感知的作用特别是现场有变频器干扰时。网上一些参考设计会让AVDD和DVDD分开供电但对HVAC这种节点来说不必要反而增加电源电路复杂度。只要去耦做足单电源完全可行。2.2 PT100三线制接法利用IDAC电流源做比例测量PT100在工程上有两线制、三线制、四线制接法。两线制的导线电阻直接串进测量回路误差很大只适合短线、精度要求不高的场合。四线制精度最高但探头和线缆成本高接线端子也多。HVAC现场最常用的是三线制配合ADC内部的IDAC做比例测量。我的接法是这样的IDAC1电流源接到PT100的一端IDAC2电流源接到另一根线两根引线分别走到PT100的两端然后差分输入AIN0和AIN1。同时让IDAC2流过一只精密参考电阻RrefRref两端分别接ADC的REFP和REFN。两个IDAC输出电流是匹配的这样导线电阻上产生的压降在差分测量中被抵消掉剩下的就是PT100本体电阻上的电压。参考电压由同一个电流在Rref上产生测量变成了比例式电源电流波动和基准电压温漂都不会影响结果这是比例测量最大的优势。参考电阻我选了2.048kΩ、精度0.1%、温漂25ppm的金属膜电阻。计算一下IDAC电流设成1mA时参考电压就是2.048V正好在ADC的测量范围内。PT100在0℃时阻值100Ω电压约100mV在满量程的5%左右即使考虑PGA放大分辨率也足够支撑0.01℃指标。2.3 热电偶与NTC的输入处理热电偶直接接到AIN2和AIN3差分输入增益设成64或128。K型热电偶信号只有几十微伏每摄氏度必须用PGA放大。冷端补偿用ADS122U04内部温度传感器这一点后面软件部分详说。硬件上要注意的是在AIN2和AIN3对地各加一个100nF电容滤掉高频噪声同时串联1kΩ电阻限流防止热电偶意外接触强电时烧坏ADC输入。NTC的接法更简单直接接在一个通道的单端输入上外部用精密电阻做分压。NTC精度本身一般分压电阻用1%精度就够。测量电压后按Steinhart-Hart方程反算电阻和温度不用PGA增益设1就行。2.4 与STM32L073RZ的UART接口和DRDY中断ADS122U04的UART引脚包括RXD、TXD还有DRDY数据就绪输出和RESET复位输入。STM32L073RZ这边我用USART2作通信TX接ADS122U04的RXDRX接ADS122U04的TXD交叉连接即可。两个芯片都是3.3V逻辑无需电平转换这就是选同供电电压芯片省事的地方。DRDY引脚很关键它指示ADC是否有新的转换结果。连续转换模式下每次转换完成DRDY会拉低读取数据后自动恢复。我做的是单次转换模式所以DRDY在转换期间保持高电平转换完成后拉低读到数据后恢复。我把DRDY接到了STM32L073RZ的PA0引脚配置为外部中断下降沿触发这样MCU可以在睡眠状态下等DRDY唤醒适合低功耗场景。RESET引脚用一个普通GPIO控制上电后先拉低再拉高确保ADC稳定复位。如果项目不需要也可以把RESET直接接到RC复位电路上但我建议保留GPIO控制调试时能强制复位芯片非常方便。2.5 PCB布局的几条实操经验这块板的PCB布局我有几条教训。第一ADS122U04的模拟输入引脚附近不要铺数字地REF电阻要尽量贴近REFP、REFN引脚引线越短拾取的噪声越少。第二模拟地和数字地单点连接具体位置选在ADC芯片底下让信号电流和数字电流分开回流。第三UART的TX、RX走线与其他信号线拉开距离特别是不要和电源走线平行太长。第四DRDY线上接一个10kΩ上拉这样即使ADC没上电也不至于让MCU引脚悬空误触发中断。这些细节都不会写进数据手册的电气参数里但做出来的板子实际噪声表现差距很大。我第一版就是因为UART线和3.3V供电线长距离平行导致通信偶发误码后来强制把线分开、中间加地隔离问题就没再出现过。3. 固件开发UART命令、寄存器配置与数据读取3.1 ADS122U04的UART协议和基本命令ADS122U04的UART协议与SPI版本的命令体系类似只是物理层换成了串口。默认波特率9600bps8位数据无校验1位停止位。这个波特率不高但ADC数据速率最高才1000SPS传输3字节结果完全不成问题9600bps足够覆盖应用。需要掌握的命令并不多。NOP空操作、RESET复位、START启动转换、POWERDOWN进入掉电模式、RDATA读取转换数据以及RREG读寄存器和WREG写寄存器。RDATA是读转换结果的核心命令发送0x10ADC会回传3字节的24位数据先后顺序是高字节、中间字节、低字节。START命令用于单次转换模式下启动一次转换发送后等待DRDY变低就能读结果。3.2 寄存器配置按项目需求逐项设置ADS122U04有4个8位配置寄存器功能大致是寄存器0配置输入通道选择、测量模式和PGA增益寄存器1配置数据速率、转换模式寄存器2配置参考电压选择和内部温度传感器寄存器3配置IDAC电流源电流大小和低边开关。寄存器映射的逻辑和ADS1220系列很像但每一位的定义必须以数据手册第五节为准这里不逐位抄写因为我见过网上不少例程直接乱套寄存器值最后测出来的数据完全不对。就以我的PT100通道为例配置顺序是这样配置项寄存器我的取值意图输入通道寄存器0选择AIN0/AIN1差分对PGA增益1数据速率寄存器120SPS带50Hz干扰抑制转换模式寄存器1单次转换省电优先参考电压寄存器2外部参考取自Rref两端内部温度计寄存器2使能用于热电偶冷端补偿IDAC电流寄存器31mA激励PT100和Rref写寄存器时用WREG命令加上寄存器地址具体代码可以封装成函数。比如写单个寄存器static void ads122u04_write_reg(uint8_t reg, uint8_t val) { uint8_t cmd 0x40u | (reg 2u); /* WREG一次写一个寄存器 */ uint8_t buf[2] { cmd, val }; uart_write(buf, 2); }读寄存器类似只是命令改成0x20开头然后接收一个字节。3.3 单次转换流程配合低功耗MCU的经典做法HVAC温度节点对实时性要求不高一秒更新一次温度完全够用所以我用单次转换模式而不是连续转换。原因是单次模式可以在转换完成后让ADC自动回到低功耗状态节省几百微安的电流对长期通电的设备很友好。流程是这样先写寄存器配置然后发送START命令启动转换接着MCU可以进入睡眠等待。DRDY引脚在转换完成后拉低触发STM32的EXTI中断MCU醒来后发送RDATA命令读回3字节数据。读完数据后DRDY恢复高电平MCU可以再一次进入低功耗。整个过程可以做成状态机避免UART阻塞等待。关键代码片段如下/* 等待DRDY变低然后读取转换结果 */ int32_t ads122u04_read_conversion(void) { uint8_t cmd 0x10u; /* RDATA */ uint8_t raw[3]; /* 等待DRDY为低表示数据已就绪 */ while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) ! GPIO_PIN_RESET) { /* 可加超时保护 */ } uart_write(cmd, 1); uart_read_bytes(raw, 3); /* 合成24位有符号数 */ int32_t value ((int32_t)raw[0] 16) | ((int32_t)raw[1] 8) | ((int32_t)raw[2]); if (value 0x800000u) { value - 0x1000000u; } return value; }这段代码里的补码转换很重要很多新手直接把3字节拼成int32结果温度忽高忽低就是因为没有把高位置1的负值处理成负数。24位ADC的输出是二进制补码最高位为1时表示负电压必须扩展符号位。3.4 热电阻比例测量的软件配合PT100比例测量在固件里有一个容易忽略的点参考电压虽然直接接到了REFP、REFN引脚但ADC的配置里必须选择“外部参考”而不是“内部参考”。如果寄存器里还沿用内部2.048V基准那么读回来的数据换算会全部错位。我在调试时犯过一次这个错误硬件接线完全正确参考电阻上确实有约2.048V压降但寄存器配置忘了改参考选择导致0℃时读回来的码值偏大换算出来温度变成十几度。排查了好久才发现是配置寄存器的问题。所以硬件初始化和寄存器初始化要一起检查不要只看一边。4. 从原始码到温度值电压换算、线性化与冷端补偿4.1 24位原始码怎么变成电压ADS122U04是24位Δ-Σ ADC输出码与输入电压的关系为Vin raw × Vref / (2^23 × Gain)这里raw是24位补码范围是-8388608到8388607。Vref是参考电压Gain是PGA增益。以我的PT100通道为例Vref取Rref两端电压约2.048VGain1那么1个LSB对应的电压约为0.244μV。这比12位ADC的0.5mV小了两到三个数量级0.01℃对应的PT100电压变化大约0.385μV也就在1-2个LSB附近完全可分辨。代码实现很简单double voltage (double)raw * 2.048 / (8388608.0 * 1.0);唯一要注意的是Vref不要写死最好每次上电从Rref的实际电压测量值校准一次或者干脆走比例式换算。4.2 PT100比例式换算绕开基准电压漂移既然参考电压来自同一路恒流源流过Rref产生的压降那么严格来说不需要知道Vref的绝对值直接用比例关系算电阻更干净。double resistance (double)raw / 8388608.0 * RREF;比如PT100在0℃时阻值100Ω如果Rref是2048Ωraw应该是约409600100除以2048再乘以8388608正好等于409600回算正确。这样电源电压变化、基准温漂都会被抵消掉只剩下参考电阻Rref本身温漂的影响所以前面强调Rref一定要选低温漂精密电阻就是这个原因。4.3 PT100的线性化从阻值到温度PT100的阻值与温度不是严格线性关系。工程上常用两种方案查表插值或者用Callendar-Van Dusen方程求解。查表法简单可靠把PT100分度表按0.1℃间隔存成数组实测电阻用二分查找加线性插值速度很快。我用的是CVD方程加牛顿迭代。CVD方程在0℃以上时可以简化为R(T) R0 × (1 A×T B×T²)其中R0100ΩA3.9083e-3B-5.775e-7。反算温度时先给初始值然后迭代double rtd_resistance_to_temp(double r) { double t (r / 100.0 - 1.0) / 3.9083e-3; for (int i 0; i 4; i) { double f 100.0 * (1.0 3.9083e-3 * t - 5.775e-7 * t * t) - r; double df 100.0 * (3.9083e-3 - 2.0 * 5.775e-7 * t); t - f / df; } return t; }四轮迭代足够收敛到0.001℃以内完全满足HVAC控制需求。如果你用的是PT1000只需要把R0改成1000A、B系数不变。4.4 NTC的Steinhart-Hart方程NTC热敏电阻的温度特性与PT100相反阻值随温度升高而下降而且非线性更强。我推荐用Steinhart-Hart方程1/T A B×ln(R) C×(ln(R))³式中T是开尔文温度A、B、C三个系数通常由厂家给出或者用三个已知温度点的阻值反求。有些NTC只给B值那可以用简化公式但精度不如三系数方程。实际使用时把电阻算出来代入即可不需要迭代一次计算就出结果。HVAC环境温度用NTC可以做到±0.3℃以内够用。但需要注意NTC探头自热效应激励电流不要超过0.5mA否则自身发热会引入误差。4.5 热电偶冷端补偿内部温度传感器的妙用热电偶测的是热端和冷端的温差电动势所以必须知道冷端温度才能换算出真实热端温度。冷端通常在接线端子处也就是PCB附近。ADS122U04内置温度传感器可以输出芯片结温把它当作冷端温度参考就省掉了一颗外置温度传感器。实现思路不复杂测出热电偶通道的电压Vemf再从内部温度传感器读出冷端温度Tcold根据热电偶分度表查Tcold对应的电动势把这个电动势加到Vemf上再用分度表反查真实温度。V_total Vemf lookup_emf(Tcold) T_hot lookup_temp(V_total)查表可以用多项式拟合或者直接查标准分度表。K型热电偶在0到500℃范围用多项式拟合误差可以控制在±0.1℃以内。需要注意内部温度传感器感知的是PCB温度如果板上有发热器件比如电源芯片测出来的冷端温度会偏高最好远离发热源或者在结构上让热电偶端子尽量接近ADC芯片。5. 实测记录通信稳定、噪声抑制与现场校准5.1 UART偶发误码的排查链路第一版样机调试时程序跑几个小时就会出现一次读取值跳变。乍一看像是ADC数据错误实际上本质是UART通信偶发误码。排查过程是这样的先在示波器上挂RXD、TXD和DRDY三根线发现DRDY下降沿之后MCU立刻调用RDATA但UART线上出现的是一个极窄的毛刺导致起始位判断错误读回来的字节错位。根源出在DRDY中断和UART发送的时序竞争上。DRDY下降沿触发中断后MCU进入中断服务函数立刻调UART发送RDATA但如果此时DRDY信号还在下降沿抖动或者中断优先级和UART发送之间有微秒级的抢占就会造成UART起始位异常。解决方法是给DRDY中断加一点软件迟滞在中断服务函数里先延时几十微秒再发送RDATA命令。实际上DRDY拉低后数据一直在寄存器里晚几十微秒读完全不影响。实测下来这个改动之后连续跑72小时没有一次误码。5.2 风机启停带来的50Hz工频干扰HVAC项目最头疼的干扰源是风机变频器和电磁阀。风机启动瞬间传感器线上的感应电压能跳到好几伏ADC数据出现大幅毛刺。我做了双重处理。第一重是硬件层面热电偶和PT100输入各加100nF差模滤波电容传感器屏蔽层单端接地接在控制柜的地而不是传感器侧悬空。第二重是软件层面ADC数据速率设为20SPS这档速率内置了50Hz工频陷波能把工频干扰衰减到很低的程度。同时软件取5次采样做中值滤波后再平均有效剔除尖峰。实测效果风机连续启停200次温度读数最大抖动0.02℃完全满足设计要求。5.3 长距离传感器线缆与地环路问题有一个项目传感器线缆走了将近15米中间还和220V供电线共桥架。初始上电后PT100温度比实际偏高约0.5℃查了很久才定位到是地环路问题。控制柜端MCU电源地通过开关电源与电网地相接而PT100探头外壳通过金属套管与管道接地两个地之间存在电位差这个电位差叠加到信号上造成误差。解决方法是把PT100改成真正的三线制开尔文连接信号只在AIN0、AIN1差分端读取传感器屏蔽层只在控制柜单端接地管道侧不接地。同时确保Rref的地和ADC的地单点连接。改完之后误差恢复到0.05℃以内。这里提醒一句HVAC现场很多管道、套管是接地的一定要留意地环路问题别忽略。5.4 零点校准与两点校准ADS122U04虽然分辨率高但失调和增益误差依然存在现场校准必不可少。我做的是两点校准0℃用冰水混合物把PT100探头和标准温度计同时插入冰桶等待温度稳定后记录ADC读数100℃用沸水或者精密电阻箱模拟。通过两个点计算出每通道的offset和gain校准系数存到STM32L073RZ的EEPROM里。实际上更常用的是用0.01%精密电阻箱模拟标准温度点因为沸水温度受大气压影响不好控制。我习惯用电阻箱分别模拟0℃100Ω、50℃119.4Ω、100℃138.51Ω每点读3次取平均算出线性修正系数。PT100传感器本身稳定性好标定一次能用很长时间。校准代码里要把修正系数放在结构体里管理方便通过串口命令现场修改而不是每次重新编译固件。这个细节对现场调试体验提升很大。5.5 功耗与长期运行表现低功耗模式下单次转换加MCU睡眠整机平均电流实测不到300μAADC本身在单次模式下的消耗大约100μA量级MCU睡眠时电流很小。按这个功耗如果配电池做无线节点也能撑很久这就是当初选STM32L073RZ的原因之一。长期运行方面这块板子在模拟HVAC环境环境温度25℃到60℃湿度高跑了三周温度数据最大漂移不到0.1℃表现稳定。内部温度传感器的冷端补偿也很可靠PCB温度升高时读数依然准确。最后分享一个小技巧PCB上专门留了一组测试点把ADC的DRDY、TXD、RXD都引出来调试时用USB转TTL直接监听ADC和MCU之间的通信数据。这对我排查前面说的UART误码问题帮助极大。做这类采集板留着测试点绝对比后期飞线省心得多。