STM32F042配ADS122U04:UART接口24位ADC打造HVAC高精度测温方案 前阵子给一套暖通空调控制柜做温度测量升级手头的板子用的是STM32F042C6原本测温靠NTC加上比较器误差和一致性都让人头疼。一路查资料、做实验下来最后落了地的是TI的ADS122U04IPWR——一颗24位Δ-Σ ADC而且接口是UART而不是常见的SPI。这个UART接口在HVAC和工业控制里特别有用它让我在不占用SPI总线的前提下把一个高精度模拟前端直接搭在了MCU的串口上。这篇文章记录完整的选型、硬件、驱动、算法和实际踩坑给做HVAC温控、工业RTD采集或者手里有F0小芯片想扩展模拟精度的工程师一个可直接抄的参考。1. 选型逻辑为什么偏偏是UART接口的24位ADC配一颗F0小芯片1.1 从HVAC温控的实际痛点说起HVAC现场传感器的分布有个特点风管送风/回风温度、水管供回水温度、机房环境温度信号线经常要走三五米到几十米。传统NTC加比较器或者直接用MCU内置ADC的方案在外墙控制柜的恶劣工况下会暴露出一堆问题。首先是NTC的一致性差。同一型号的10k NTCB值本身就有分散性25℃下阻值偏差动辄±1%换算成温度就是0.25~0.5℃的误差。批量生产时想做到±0.1℃只能逐台标定产线成本很高。其次是长线压降。NTC两线制串联到控制板线电阻从零点几欧到几欧不等对0.1℃精度的项目是毁灭性的。哪怕用10k NTC50米线的电阻也有好几欧直接吃掉零点几摄氏度。最后是内置ADC的精度天花板。STM32F042C6内置12位ADC满量程3.3V时1 LSB约0.8mV而Pt100在0.1℃时电阻变化只有约0.04Ω用1mA激励时对应电压变化约0.04mV完全量不出来。所以当HVAC或者工业项目对温度有0.1℃甚至更高要求时外置高精度ADC几乎是必选项。24位Δ-Σ ADC配合内置PGA可以把最小分辨率做到微伏级别这才具备测量Pt100/Pt1000这类低阻传感器的能力。1.2 UART接口在整机设计里的现实价值很多人第一反应是选ADS1220SPI接口资料多参考设计遍地都是。但我最终选了ADS122U04IPWR核心原因是控制板上SPI总线已经被Flash、LCD、无线模块占了。重新分配CS引脚也能做但布线、驱动都要改风险不小。而STM32F042C6有USART1和USART2两个串口用一个空闲串口接ADC改动量最小甚至可以直接复用现成的串口驱动框架。UART接口相比SPI还有一些工程上的隐性优势。长线传输更稳。SPI有SCK时钟线时钟信号容易辐射和产生边沿抖动走线超过10cm就开始有麻烦UART用9600或19200波特率传输低速串行信号抗干扰能力明显更好。便于光电隔离。如果要在控制板和传感器面板之间做隔离UART只需要隔离开TX/RX两根线比SPI的SCK、MOSI、MISO、CS四根线简单很多光耦成本也低。软件调试直观。逻辑分析仪直接抓UART命令帧ASCII/HEX一目了然定位问题比扒SPI时序快得多。代价是吞吐率不如SPI但温度测量属于典型低速应用ADS122U04在最高数据率下也只要求几十kbps级别的传输UART完全能扛住。1.3 为什么是STM32F042C6这颗芯片库存充足、成本低Cortex-M0核心确实弱但温度采集不需要跑复杂算法主要工作是UART驱动、24位数据处理和PID输出。32KB Flash装下通信协议栈和线性化查表绰绰有余6KB SRAM足够双缓冲。LQFP48封装引脚少layout简单做在小控制板上非常合适。而且F0系列的低功耗模式很适合HVAC控制器长期运行在待机/轮询状态。整体看一颗F0小芯片加一颗24位UART接口ADC成本可控、架构干净是这个选型组合最吸引人的地方。2. 硬件设计模拟前端怎么接才能让24位不白费2.1 供电与参考电压ADS122U04IPWR的数字/模拟电源都可工作在1.8~3.6V和STM32F042C6共用3.3V很方便。但“共用3.3V”和“共用同一个LDO”是两回事。如果板上同时有继电器、风机驱动开关噪声会通过电源串进模拟部分24位ADC的性能就会被白白浪费。我的做法模拟供电单独走一个低噪声LDOAVDD和DVDD分别加0.1µF和1µF退耦电容DGND和AGND在芯片下方单点相连。这个细节看着小实测对ADC噪声影响非常大。参考电压是另一个关键点。ADS122U04有内置参考但内置参考的漂移和噪声在0.1℃级应用里可能吃紧。做RTD比率测量时更稳妥的做法是把精密参考电阻接在REFP/REFN差分输入端利用同一个IDAC电流源既产生激励又产生参考电压。这样做的好处是电流源漂移可以互相抵消。如果做绝对电压测量比如热电偶优先选温漂≤5ppm/℃的外部基准比如REF5025而不是直接用AVDD当基准。2.2 RTD的2线/3线/4线接法RTD是低阻传感器导线电阻是最大误差源。这里给一张对比表接法激励/测量方式能否消除导线电阻适用场景2线IDAC从一根线流入同一根线测电压否线阻完全计入传感器和板子距离小于1米精度要求不高3线IDAC从AIN0流入AIN1取差分电压另一根线补偿基本消除线路不平衡时剩余少量误差HVAC盘管、管道工程上最常见4线两根激励、两根测量完全消除实验室、工业计量级3线接法的关键一定要让激励电流路径和电压测量路径分离。我实际试过两种接法效果差很多。一种是把RTD两根线直接并到AIN0/AIN1IDAC从同一个节点流入结果线阻变化直接进测量另一种是严格按照“激励线走IDAC1AIN0/AIN1只取电压”来接长线干扰降了两个数量级。ADS122U04有IDAC1和IDAC2两个激励源。3线方案推荐这样接IDAC1接到RTD一端同时通过独立的电压检测线引到AIN0RTD另一端接AIN1另外用IDAC2灌入一条补偿线。电压测量端的高阻特性不会拉低激励电流线阻上产生的压降要么被差分扣除要么根本不在测量回路里。激励电流大小也要选。Pt100用500µA比较合适自热功率I²R0.025mW引起的温升可以忽略。1mA以上在密闭探头里会明显自热我实测能把读数抬高0.1~0.2℃。Pt1000阻值大用100µA或250µA即可自热更小。2.3 热电偶冷端补偿如果项目里要测高温比如工业热风管道或烟道热电偶方案成本更低。ADS122U04的24位分辨率加PGA对K型热电偶非常充裕K型热电偶灵敏度约40µV/℃1 LSB等效温度远小于0.01℃。但热电偶必须做冷端补偿。冷端补偿是把热电偶冷端放在已知温度处通常用ADC内置温度传感器或者外接NTC测量冷端温度再按分度表或多项式补偿。这里有个很重要的经验冷端温度传感器要放在接线端子的金属夹片附近而不是放在MCU边上。处理器发热会带来几摄氏度的测量偏差这个坑非常隐蔽。我第一次做的时候把NTC放在PCB角落靠近CPU的位置结果热电偶读数差了将近两度找了好久才发现是冷端温度不对。2.4 PCB布局与滤波模拟输入的滤波和布线直接决定ADC实际能到多少位有效分辨率。我的经验参数模拟输入引脚前各加一个RC低通100Ω串联加100nF到地截止频率约16kHz用来过滤射频干扰。传感器信号线用双绞线或屏蔽线屏蔽层在控制板端单点接地不要在传感器端也接。ADC模拟输入走线远离继电器、PWM输出焊盘模拟区下面铺完整AGND。如果传感器和控制器距离超过5米不要直接传RTD弱信号更稳的做法是把ADC芯片放到传感器侧用UART数字信号回传。这是一个架构层面的大优化。3. 让UART链路跑起来ADS122U04的通信协议与驱动要点3.1 通信帧结构与自检流程ADS122U04不是简单的“发命令收数据”它有明确的帧协议。以9600bps、8N1为例MCU发命令帧ADC返回应答帧。命令类型包括复位、写寄存器、读寄存器、启动/停止转换、读取数据等。帧里可以带校验字节具体是否启用由配置寄存器决定。实际调试时最怕两件事一是波特率不匹配导致整帧错位二是发送命令后没等够响应时间把应答帧当成下一条命令的输入。我的经验是在上电后先发复位命令并延时10ms以上再读回一个已知寄存器验证链路。这个自检流程能省掉一半的玄学问题。具体帧格式细节因批次和手册版本有差异但字节序大多是“命令字寄存器地址数据”读数据时用RDATA命令连续取3字节。3.2 寄存器配置与初始化/* 使用STM32F042C6的USART2波特率96008N1 */ static void ads122u04_uart_init(void) { /* 配置PA2/PA3为USART2的TX/RX */ /* RCC、GPIO、USART初始化略 */ } static void ads122u04_write_reg(uint8_t reg, uint8_t val) { uint8_t frame[3]; frame[0] 0x00; /* 写命令字按手册定义 */ frame[1] reg; /* 寄存器地址 */ frame[2] val; /* 要写入的值 */ uart2_write_bytes(frame, 3); delay_ms(1); /* 等待ADC处理完成 */ } static void ads122u04_init(void) { ads122u04_uart_init(); ads122u04_reset(); /* RESET引脚拉低再拉高或发复位命令 */ /* 配置寄存器0PGA增益、参考电压选择 */ ads122u04_write_reg(0x00, 0x06); /* 示例值具体按手册bit定义 */ /* 配置寄存器1输入MUX、单次/连续模式、IDAC相关 */ ads122u04_write_reg(0x01, 0x22); /* 配置寄存器2数据率、滤波器 */ ads122u04_write_reg(0x02, 0x10); /* 45SPS50/60Hz抑制 */ /* 配置寄存器3IDAC电流大小、输出通道 */ ads122u04_write_reg(0x03, 0x6C); }说句实在话寄存器0到3的bit位在不同数据手册版本里画法不完全一致。我第一次调的时候就是没仔细看版本号把某个bit写反导致MUX一直指错通道。所以上面代码里的具体值只能当示例务必对照你手中那颗料的datasheet来填。3.3 读取转换值转换模式建议用单次转换。每次需要时才触发一次期间MCU可以做别的事也方便多通道轮询。读取代码static int32_t ads122u04_read_one_sample(void) { uint8_t buf[3]; /* 启动转换 */ uart2_write_byte(CMD_START); /* 等待DRDY拉低 */ while (gpio_read_pin(DRDY_PIN) GPIO_HIGH); /* 发送读取命令或按配置直接接收数据 */ uart2_write_byte(CMD_RDATA); uart2_read_bytes(buf, 3); /* 24位数据转成带符号的32位整数 */ int32_t raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | ((int32_t)buf[2]); if (raw 0x800000) raw | 0xFF000000; /* 符号扩展 */ return raw; }DRDY引脚强烈建议接到MCU的外部中断引脚而不是轮询。轮询在测试时能用一旦主循环里加了LCD刷新或PID计算很容易丢数据。用外部中断就能保证每次转换完成立刻读取。3.4 通信可靠性加固工业现场UART会受到干扰帧错位偶尔会发生。我的做法每个命令帧带校验ADC端校验失败会返回错误帧MCU检测到就重发。每次读取数据后检查数值是否在合理范围比如RTD对应电阻在80~300Ω之间超范围判定为传感器故障或通信异常。连续出现三次通信错误时对ADC做一次硬复位拉低RESET引脚重新初始化而不是傻等着。这些“脏活”看似无关紧要但在实际项目里正是因为有了异常恢复机制设备才能在变频器等强干扰源附近连续跑几个月不出问题。4. 从ADC码值到温度RTD线性化与标定4.1 从码值到电压24位ADC输出的是二进制补码或偏移码取决于配置。转换成电压V_in raw × VREF / (2^23 × PGA)假设VREF2.048V、PGA4则1 LSB对应的电压为2.048 / (8388608 × 4) ≈ 61nV。这个分辨率对测温绰绰有余。但直接按这个公式计算会引入VREF的绝对误差。如果做RTD比率测量用下面的方式更准。4.2 比率测量为何能省掉基准误差把精密参考电阻Rref接在REFP/REFN之间IDAC电流同时流过Rref和RTD。ADC测量RTD电压与参考电压的比例为code_rtd / code_ref R_rtd / R_ref所以R_rtd code_rtd / code_ref × R_ref这个公式里已经完全消掉了IDAC电流大小。即使电流源随温度漂移了1%分子分母同步变化比例不变。这是RTD测量的精髓。Rref本身的温漂成了主要误差源所以Rref要选低温度系数精密电阻建议≤10ppm/℃。实际代码里我会在每次读取RTD通道后紧接着读参考通道的码值用最新一次的比例计算避免IDAC慢漂移带来的长期误差。4.3 RTD电阻转温度Callendar-Van Dusen方程Pt100在0℃以上满足R(t) R0 × (1 A×t B×t²)其中R0100ΩA3.9083e-3B-5.775e-7。0℃以下还要加一项R(t) R0 × [1 A×t B×t² C×(t-100)×t³]C-4.183e-12。0℃以上可以直接用求根公式解省去迭代t (-A sqrt(A×A - 4×B×(1 - R/R0))) / (2×B)代码float rtd_resistance_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 ratio r / R0; if (r R0) { return (-A sqrtf(A*A - 4.0f*B*(1.0f - ratio))) / (2.0f*B); } else { /* 0℃以下用迭代或查表 */ float t -10.0f; /* 初始猜测 */ for (int i 0; i 10; i) { float r_calc R0 * (1.0f A*t B*t*t C*(t-100.0f)*t*t*t); t (r - r_calc) / (R0 * (A 2.0f*B*t)); } return t; } }为了防止浮点导数写错负温区我更推荐直接做查表加线性插值。把-200~0℃按每10℃建一张电阻表插值精度足够到0.05℃。CVD公式用于桌面校验查表用于MCU运行省去迭代的耗时。4.4 校准即使ADC精度很高系统层面还有传感器误差、Rref误差、PCB焊接应力等。我习惯做两点校准低温点用精密电阻箱拨到100.00Ω对应0℃记录ADC计算出的温度值。高温点拨到138.50Ω对应100℃记录偏差。然后做线性修正t_corrected t_raw × gain_cal offset_cal其中gain_cal 100 / (t_high_raw - t_low_raw)offset_cal -gain_cal × t_low_raw。这种做法能把系统级误差压到0.05℃以内。如果现场没有电阻箱至少也要用冰水混合物做一次零点校准。很多时候焊板造成的初始偏差比想象的大千万别跳过这个步骤。4.5 精度预算参考误差来源典型影响Pt100 0~100℃对策RTD自身±0.1℃Class A选型时确认精度等级Rref温漂约0.02℃10ppm选低温漂精密电阻IDAC电流比率测量下几乎抵消不要做绝对电压测法PGA失调约0.005℃单点校准抵消ADC噪声约0.01℃ RMS45SPS软件取平均/滑动滤波综合下来系统做到±0.1℃不是难事关键投入在传感器等级和参考电阻上。5. HVAC与工业场景分场景处理与工程细节5.1 HVACNTC与Pt1000怎么选大多数HVAC项目对成本敏感NTC依然是主流。ADS122U04的24位精度拿来测NTC有点“大材小用”但换来的是不需要逐点校准的一致性。10k NTC在25℃附近灵敏度约-4%/℃也就是-400µV/℃1µA激励时用PGA16后分辨率远超需求。在AHU和风机盘管的进出水温度、送回风温度测量上用NTC加本方案的ADC读值省去传统“电位器调零”的人工标定批量生产一致性会好非常多。HVAC环境具体选型时有几点经验送风/回风温度探头要伸到气流里避免贴近外壳导致热惯性用护套型Pt1000响应更快。盘管供回水温度水管内温度波动大数据率建议45~90SPS软件再做16次滑动平均能明显压住水流脉动带来的读数波动。冷凝风险盘管表面可能在低温工况结露模拟前端布线要避开冷凝水滴落路径PCB刷三防漆传感器端子用防水型。抗干扰变频风机、水泵在HVAC系统里到处都是ADC模拟输入端远离变频器输出线屏蔽层单端接地这些在施工图上就要提前标好。5.2 工业长线传输、断线检测与EMC工业场景和HVAC最大的区别在距离和电磁环境。如果传感器离控制器20米直接传RTD弱信号一定会被干扰。这时候把ADS122U04IPWR做成传感器侧模块UART走RS485或隔离UART回传主机是更稳的架构。UART的低速特性也让光电隔离成本更低。断线检测方面RTD开路时测量电压会异常拉高ADC很容易识别短路时读数接近0。利用IDAC的特性可以做自动诊断正常Pt100在-50~150℃对应约80~157Ω如果换算出来的电阻值超出范围就报故障并选择安全输出。在工业现场这些诊断逻辑比精度还要重要——宁可显示“传感器故障”也不要错误数据触发连锁反应。另外工业现场经常和变频器、接触器共用电气柜。安装时注意模拟信号线不要和动力电缆平行走线穿越不同区域时尽量垂直交叉。实在避免不了的长距离布线优先选择电流环或者数字总线传输。5.3 多通道轮询与电源策略ADS122U04有AIN0到AIN3四通道。做典型HVAC控制箱时我会这样分配通道用途传感器AIN0/AIN1RTD差分Pt100供水温度AIN2单端NTC回风温度AIN3单端NTC送风温度或冷端补偿轮询时每通道用单次转换每路取4次平均后再切换到下一路。45SPS数据率下4路完整轮询大约90ms对HVAC的温控周期通常1s毫无压力。供电上如果现场有24V电源用DC-DC降到3.3V时注意纹波。实测发现DC-DC开关纹波直接落在模拟电源上时ADC噪声能明显多出几个码加了π型滤波后安静很多。这也是一个花钱很少但效果非常大的细节。6. 实测数据与踩坑记录6.1 坑一激励电流太大把读数“烤”高了刚开始为了追求信号幅度把Pt100的IDAC设成1mA。密闭式探头在空调水管上装了一晚第二天看数据比参考温度计高0.19℃。起初以为是传感器装偏了后来单独做自热实验同一探头在静止空气中1mA下读数持续爬升十几分钟才稳定改成500µA后这个爬升基本消失。这就是自热误差。选激励电流时不要贪信号强度信号差一点可以靠PGA补自热误差却是实打实的物理偏差。6.2 坑二基准电压直接接AVDD读数跟着开关电源一起跳舞有一版样板为了省料把REFP接AVDD、REFN接GND。结果只要压缩机继电器一吸合ADC读数就跳十几码。查到最后发现AVDD上叠加了几十mV的开关毛刺基准不干净转换结果自然全脏。改成外部基准加RC滤波后同样工况下噪声降了一个数量级。经验模拟基准和数字电源必须分开至少也要在基准引脚前加二级LC滤波。6.3 坑三UART中断被LCD刷新打断导致错帧最初用轮询方式读UART结果在LCD刷新期间把RX字节丢了校验直接不过隔三差五出一帧坏数。后来把UART接收改成中断加FIFOLCD刷新等任务优先级调低丢帧率降到零。另外请务必给每条命令帧加超时重发机制发送后20ms内无响应就重发连续失败才复位芯片。这个机制看着简单在现场调试时是救命稻草。6.4 实测结果一个在恒温槽里做的对比45SPSPGA43线Pt100参考温度℃本方案读数℃最大偏差℃0.000.010.0125.0024.980.0250.0050.020.02100.0099.960.0432小时连续运行管温监测通道读数波动在±0.02℃以内滑动平均16次后。这水平对HVAC的能效控制和工业的温度监测完全够用。6.5 最后的体会这套方案用下来最大的感受是“有时候换个接口比换一颗更强的MCU还管用”。ADS122U04IPWR的UART接口解决的是整机架构的现实问题STM32F042C6虽然只是一颗Cortex-M0小芯片但配合24位ADC后能力边界一下子大了很多。后续如果要做多路扩展可以在前端加模拟开关扩展通道数如果做分布式采集直接让每块传感器板挂一颗ADS122U04UART汇总到主控架构非常干净。希望这篇实操笔记能给正在做HVAC温控或工业测温和的朋友少走点弯路。