搞工业现场的设备,最怕的就是温度数据“看着像那么回事,实际全是噪声”。我去年同时做了两套温度采集板卡:一套给 HVAC 的风管温湿度监测,另一套给工业循环水管道测表面温度。热电偶信号只有几十微伏到几毫伏,直接进 MCU 内置 ADC 基本没戏,最后两套板卡都统一用成了ADS122U04IPWR + STM32F756ZG的组合。ADS122U04IPWR 是一颗四通道 24 位 ΔΣ ADC,自带 PGA、内部基准和四线 UART 接口;STM32F756ZG 是 Cortex-M7 主控,负责把原始电压换算成温度、做滤波、跑通信协议。这套组合解决的核心问题是:在电机变频器、继电器、电磁阀反复动作的恶劣电磁环境里,把温度测准、测稳。
这个方案适合谁?如果你正在做 HVAC 控制面板里的温度采样,或者工业设备上需要多路热电偶采集但不想每次都被噪声折腾到凌晨,那这篇内容可以直接作为选型和落地参考。下面我会从方案选型、硬件设计、固件实现、实测数据到排查经验,按我实际走过的路径完整讲一遍。
1. 方案选型:为什么把这两颗芯片放一起
1.1 ADS122U04IPWR 在信号链里的位置
ADS122U04IPWR 这颗芯片最吸引我的不是“24 位”这个数字,而是它把模拟前端和通信接口打包在了一起。24 位 ΔΣ ADC 本身不少见,但四通道、内置可编程增益放大器(PGA)、内置基准、带四线 UART 接口的型号,在这个价位段里确实很少见。
先说模拟部分。热电偶输出的是直流毫伏信号,K 型热电偶在 25°C 到 100°C 之间,灵敏度大约 40µV/°C。如果直接用 MCU 内部 12 位 ADC,参考电压 3.3V,一个 LSB 就是 805µV,意味着温度分辨率大约 20°C,这根本没法用。而 ADS122U04 内部 PGA 可以将信号放大 1 到 128 倍,配合 2.048V 内部基准,满量程差分输入范围可以压到 ±16mV。也就是说,一个 24 位码字对应的理论 LSB 约为 32mV / 2²⁴ ≈ 1.9nV,当然这是纯量化分辨率,实际有效分辨率受噪声限制,但在 20SPS、PGA=128 的配置下,按手册典型噪声估算,温度分辨率做到 0.005°C 量级是没问题的,实际现场受接插件和干扰影响,稳定在 0.01°C 到 0.05°C 完全可行。
再说接口。这颗芯片用的是 TI 的四线 UART 从站协议,而不是常见的 SPI 或 I2C。四线 UART 的帧格式、地址机制和 CRC 校验,天然适合多设备菊花链和远距离隔离传输。HVAC 现场出线距离经常超过两三米,如果用 SPI,时钟线上稍微有点寄生电容和串扰,时序就乱了;UART 本身就抗干扰一些,而且比较容易加隔离。这一点后面在硬件章节会展开。
1.2 STM32F756ZG 的角色
STM32F756ZG 在这套方案里不是单纯读 ADC 数据,它承担的是“数据消化”和“对外输出”的任务。这颗芯片基于 ARM Cortex-M7 内核,主频最高到 216MHz,带双精度浮点单元。温度换算里最麻烦的是热电偶非线性修正,K 型热电偶在全温度范围内用 NIST 多项式反算时,浮点运算量不小。用普通 M0/M3 的 MCU 虽然也能算,但效率和余量完全不同。F756 跑这些浮点运算几乎不占用 CPU 时间,剩下来的算力可以处理更复杂的东西,比如多段 PID 控制、FFT 频谱分析,或者跑 MODBUS 协议栈。
另外,F756ZG 的 UART 数量足够多,我手上这套板子挂了 4 颗 ADS122U04,每颗通过 UART 独立连接,分别对应四组温度测点。如果选用小封装、串口少的 MCU,这种扩展就会很吃力。F756 还内置硬件加解密单元,虽然温度采集用不到,但如果是做带数据记录或者远程运维的网关产品,这个特性反而成了加分项。
1.3 系统链路怎么走
整个系统的信号链可以用一句话描述:热电偶产生微伏级直流信号,经过输入滤波网络进入 ADS122U04,ADC 完成放大、量化和 24 位转换,结果通过四线 UART 送到 STM32F756ZG,MCU 再结合冷端补偿温度完成线性化换算,最后通过 RS-485 或者以太网把温度值传给上位机。
注意冷端补偿这一环。ADS122U04 内部并没有温度传感器,冷端补偿必须依赖外部方案。我在板子上靠近热电偶端子位置放了一颗 NTC 热敏电阻,用 ADC 的另一个通道去测量它,这样 MCU 就能拿到“接线端子处的实际温度”,然后把热电偶的冷端电动势修正掉。整个链路看起来不复杂,但每一环都有坑,尤其是 NTC 的布线和接地处理不当,误差会直接落在最终温度读数上。
2. 硬件设计:从热电偶到 MCU 的每一环
2.1 热电偶输入与滤波网络
热电偶的信号源阻抗很低,几十欧姆以内,但现场引线往往很长,容易感应工频干扰、变频器开关噪声和继电器拉弧脉冲。所以 ADC 输入前端一定要加差分 RC 滤波,我的起步组合是每根信号线串联 1kΩ 电阻,两根线之间并联 100nF 电容,构成一阶低通,截止频率大约 1.6kHz。
这里有个经验:滤波电容不能盲目加大。虽然大电容能压住更多高频噪声,但 ΔΣ ADC 的输入级是开关电容结构,它会在采样瞬间从输入电容抽取电荷。如果外部电容太大,反而会造成建立时间不够,导致转换结果出现额外误差。TI 手册里对 RC 值和数据速率有匹配建议,我实测下来 1kΩ + 100nF 在 20SPS 下工作稳定,既不丢失精度,也能把变频器噪声压下去。
输入端还应该加保护。HVAC 现场经常有安装工人带电操作,热电偶线有时会碰触到 24V 或 220V。我在端子到 ADC 输入之间放了双向 TVS 管和串联电阻,TVS 钳位电压选 5V 左右,保证瞬时冲击不损坏 ADC。如果是工业级项目,建议再增加 10kΩ 串联限流电阻,牺牲一点点噪声性能换可靠性。
2.2 四线 UART 接口的连接细节
ADS122U04 的四线 UART 并不是传统意义上“两根线一问一答”的 UART,它多出来的 CLK 线和 CS 线是整个通信时序的关键。我刚开始画板子时没仔细看时序图,把 CS 拉低、发完命令就松开,结果芯片完全不回复。后来才意识到,CS 必须在整个帧传输期间保持拉低,CLK 信号要稳定连续,主机在时钟边沿逐位移出命令和数据,从机在同一个时钟边沿回传数据。
STM32 端我建议这样接:MCU 的 UART TX 接 ADC 的 D1 引脚,UART RX 接 ADC 的 D0 引脚,CLK 由定时器 PWM 或者 MCU 的 MCO 时钟输出提供,CS 用普通 GPIO 控制。这里有个实现细节需要注意——四线 UART 的位时钟必须和 UART 波特率协调一致,如果两边频率偏差超过容限,CRC 校验就经常失败。稳妥的做法是先按数据手册里的主从机连接图把时钟关系理清楚,再用示波器量 CS、CLK、TX 三根线的相对时序,确认没有问题再写驱动。
如果现场干扰很严重,我建议在 MCU 和 ADC 之间加数字隔离器,比如 ISO7721 这类双通道数字隔离芯片,把模拟现场和数字控制板完全隔开。隔离后的 UART 信号可以走更长距离,也不会把现场地噪声引到 MCU 地平面。
2.3 冷端补偿与基准源的选择
冷端补偿是所有热电偶测温方案里最容易出错的一环。很多人以为只要软件里加一个“25°C 修正量”就行,实际现场温度根本不是均匀分布,热电偶端子的左侧和右侧可能差出两三度,这部分误差会原封不动进入最终结果。
我的做法是:在热电偶冷端(也就是板卡接线端子)紧贴位置放置 NTC 热敏电阻,用 ADC 的第二个通道测量。NTC 的供电方式不要用普通电阻分压,而是在 ADC 内部使能一个恒流源,让电流流过 NTC,再量 NTC 两端电压。这样电源电压的波动不会影响测量的准确性。NTC 阻值可以选 10kΩ 或者 100kΩ,我强烈建议选 10kΩ 的 103 型号,配合 25°C 下 3435K 的 B 值,用 Steinhart-Hart 方程换算温度,精度在 0.1°C 以内完全能做到。
基准源方面,普通 HVAC 场景用 ADS122U04 内部 2.048V 基准就够。但如果你做工业级产品,要求全温度范围精度长期稳定,建议外接一颗低温漂精密基准,比如 REF5025 这类 2.5V 器件,它的温漂远低于内部基准,只是成本会多出十几块钱。取舍标准很简单:你的整机温度精度指标是 ±1°C 还是 ±0.1°C,如果是后者,外部基准是必须的。
2.4 电源、地与 PCB 布线的经验
ADS122U04 的 AVDD 和 DVDD 虽然可以共用一路电源,但我在布板上还是做了分离:模拟电源先经过磁珠和 10µF + 100nF 电容做局部去耦,再供给 AVDD;数字电源经过另一个磁珠单独供给 DVDD。这样做的目的是把 MCU 数字开关噪声尽量挡在模拟部分之外。
地平面处理是另一个重点。很多初学者直接把所有地连成一片,看似简单,实际上会形成地环路。我在板子上用星形单点接地:模拟地、数字地、电源地在电源入口的单点汇合。热电偶屏蔽层只在板卡端单点接地,传感器端悬空,避免两端接地后形成环路电流。
PCB 布局上,ADC 下面不要走数字信号线,尤其是 CLK 线和 UART TX 线,它们翻转时的高频分量很容易耦合到模拟输入。我在首批样板里就吃过这个亏:ADC 到 MCU 的 CLK 走线从热电偶输入通道正下方穿过,结果 ADC 数据在 CLK 翻转时出现周期性跳动,最后只能改版绕线。
3. 固件实现:寄存器、协议与温度换算
3.1 寄存器配置:先从 CONFIG0 说起
ADS122U04 上电后默认配置是关闭状态,必须通过 UART 写入寄存器才能正常工作。寄存器组一共四个,按顺序是 CONFIG0 到 CONFIG3,每个 8 位。配置位定义在数据手册里非常详细,我这里给一个实际项目里采用的配置思路,具体位值要按手册核对。
CONFIG0 主要设置数据速率和工频滤波。我把数据速率定在 20SPS,这个速率下数字滤波器对 50Hz 和 60Hz 工频都有不错的抑制能力,HVAC 现场正好是国内 50Hz 工频环境。如果追求更高刷新率,可以提到 640SPS,但噪声会明显上升,软件也要做额外的均值滤波,这个取舍要视应用而定。
CONFIG1 设置 PGA 增益和输入通道选择。热电偶测量我选择 PGA=128,输入通道选 AIN0-AIN1 差分对。NTC 测量通道选 AIN2-AIN3,PGA 增益低一些,比如 4 倍,避免 NTC 电压超出量程。CONFIG2 选择基准源和激励电流源:内部 2.048V 基准,NTC 恒流源使能,电流值设成 250µA。CONFIG3 开启 CRC 校验,关闭开短路检测,这两个功能后面固件稳定了可以再打开。
寄存器写入的伪代码大致如下,注意写入后一定要回读验证,防止 UART 时序问题导致“看似写了、实际没写”的假象:
// 伪代码:寄存器位定义以数据手册为准 uint8_t cfg[4]; cfg[0] = 0x26; // 举例占位:20SPS,工频抑制开启 cfg[1] = 0x03; // 举例占位:PGA=128,AIN0-AIN1 差分 cfg[2] = 0x00; // 举例占位:内部基准,激励电流 250uA cfg[3] = 0x01; // 举例占位:CRC 使能,其余关闭 ads122u04_write_regs(cfg, 4); ads122u04_read_regs(0, 4, rx_buf); if (memcmp(cfg, rx_buf, 4) != 0) { // 读取寄存器失败,重新初始化 }3.2 四线 UART 的帧格式与驱动封装
四线 UART 协议的精髓在于“主机提供时钟,从机按地址响应”。主机发送一个命令帧,从机返回一个数据帧,每一帧都带 CRC 校验,这样通信可靠性比普通 UART 高很多。
命令帧发送过程可以概括为:拉低 CS,等待一小段稳定时间,在 CLK 时钟驱动下把 4 个字节按序发送出去,然后继续读取从机返回的 4 个字节,完成后再拉高 CS。帧里的命令字节决定了这次操作是写寄存器、读寄存器还是读取 ADC 转换结果。读取 ADC 结果时,返回的 4 个字节里有 3 个字节是 24 位采样值,最后一个字节是状态和 CRC 信息。
驱动封装建议做三层。底层是 GPIO 模拟或者硬件 UART 的字节收发,中间层是帧构建和 CRC 校验,最上层是 ADC 数据获取和寄存器读写。中间层一定要封装好“从机无响应”和“CRC 错误”这两个异常状态,因为实际现场经常出现瞬间干扰导致一帧数据损坏,如果驱动直接返回错误,上层就方便做重试而不是把坏数据当成正常值。
我在 STM32 上用的是中断发送 + 接收超时机制。MCU 发送完命令后开一个 5ms 定时器,如果 5ms 内没有收到有效回复,就判定通信超时,重新同步 CS 时序再试一次。这个策略在长期运行中效果很好,偶发错误能被自动恢复掉,不会出现死等造成系统卡死。
3.3 电压到温度的完整换算流程
拿到 ADC 的 24 位原始码后,第一步先做二进制补码转换。ADS122U04 输出的是 24 位二进制补码,正温度和负温度对应的电压方向不同,所以要做好符号扩展:
int32_t raw = (b2 << 16) | (b1 << 8) | b0; if (raw & 0x800000) { raw -= 0x1000000; // 24位补码转成32位有符号数 } float voltage = (raw / 8388608.0f) * (VREF / GAIN);最后一行计算的是实际输入电压。这里 VREF 是 2.048V,GAIN 是 128,所以电压满量程是 ±16mV。如果芯片用的是外部 2.5V 基准,这里就要相应替换。
第二步是热电偶电压到温度的线性化。最省事的方法是分段查表,把 NIST ITS-90 标准对照表做成常量数组,用二分法找到电压所在区间,再做线性插值。这个方法占用 Flash 不大,速度也快,K 型热电偶从 -200°C 到 1372°C 只需要几百个数据点,精度可以做到 0.3°C 以内。如果要求更高,可以再叠加一个二次修正项,把插值误差压到 0.05°C。
第三步是冷端补偿。先把 NTC 通道的电压值换算成电阻值,再用 Steinhart-Hart 方程得到冷端温度,然后查找该温度下对应的热电偶电动势,把这个电动势加到测量电动势上,最后再查一次表得到真实温度。整套流程可以用下面的伪代码表达:
float v_cold = read_ntc_voltage(); float r_ntc = r_div * v_cold / (v_ref_gain - v_cold); float t_cold = steinhart_hart(r_ntc); float emf_cold = tc_type_k_emf(t_cold); float emf_hot = tc_type_k_emf(t_meas); float t_real = tc_type_k_temp(emf_hot + emf_cold);注意 emf_cold 是有符号的,如果冷端温度低于 0°C,这个电动势就是负值,加法操作要确保符号处理正确。
3.4 数字滤波与均值策略
ADS122U04 内部已经有数字滤波,但我在 MCU 端还是加了二次滤波,主要目的是消除现场随机尖峰。我的策略是“中值 + 滑动平均”组合:连续采 5 个点,去掉最大值和最小值,剩下 3 个点求平均。这个组合对瞬时脉冲干扰非常有效,而且不会像纯滑动平均那样把阶跃响应拖得很慢。
在 HVAC 场景里,温度变化本身很慢,风管里的温度一分钟内顶多波动零点几度,所以 20SPS 的数据率配合 5 点滤波,最终显示值已经很稳定。我在面板上测试时,读数基本纹丝不动。
工业场景有些区别。如果测的是管道表面温度,探头会受环境风速影响,温度在短时间内有小幅震荡,这时候我会把滤波窗口拉长到 9 点或者 15 点,让显示值更平滑。但要注意,过滤波太多会导致温度报警响应变慢,如果设备有超温保护功能,报警延时不能太大,否则可能造成安全事故。这个平衡点要根据项目指标来调,不能一刀切。
4. 实测数据:实验室与现场表现
4.1 零点、沸点和恒温箱对比
板卡调通后,我先在实验室做了基础精度验证。用冰水混合物作为 0°C 基准,沸腾的纯净水作为 100°C 基准,再用恒温箱设置 25°C 和 80°C 两个点交叉验证。实测数据如下表:
| 标准温度 | 本系统读数 | 偏差 |
|---|---|---|
| 0.0°C | 0.06°C | +0.06°C |
| 25.0°C | 24.91°C | -0.09°C |
| 80.0°C | 79.95°C | -0.05°C |
| 100.0°C | 100.12°C | +0.12°C |
这个结果是在外接 REF5025 基准、冷端补偿校准后的状态。如果只用内部基准,偏差会大一些,但也在 ±0.5°C 以内,对 HVAC 应用绰绰有余。整体来看,这套系统在常温段的表现已经超过了我最初的设计预期。
4.2 校准流程与修正系数
温度测量系统的绝对精度不可能只靠芯片参数保证,板级校准是必要的。我的校准流程分两步:先做冷端补偿校准,再做整体增益偏置校准。
冷端补偿校准是这样做的:把热电偶探头和标准温度计绑在一起放到冰水混合物里,等到示数稳定后记录本系统的显示温度。如果显示不是 0°C,说明 NTC 或补偿算法有偏差,我就在固件里保存一个修正偏移量,把补偿温度往正确方向拉回来。
整体增益校准在 100°C 沸点做。记录系统显示温度和标准温度之间的差,计算出一个比例系数,也就是增益修正。之后每个通道的最终温度值都用公式T_final = (T_raw + offset) * gain_coeff计算。这两个参数我保存在 STM32 的 Flash 中,每次上电自动加载。校准好的板卡互换性很好,同一批板卡相互替换后读数差不超过 0.1°C。
4.3 现场长期运行的稳定性
实验室数据再漂亮,现场才是真正的考核。我在 HVAC 风管上挂了三个测点,连续运行两周,系统读数一直稳定在设定温度附近,波动范围不超过 ±0.15°C,中间还经历了压缩机启停、变频风机调速等干扰场景,没有出现一次数据跳变或通信超时。
工业侧我也贴了一个 200°C 蒸汽管道的表面温度测点,测试周期一周。由于管道表面温度本身波动较大,读数在 198°C 到 202°C 之间缓慢变化,跟踪趋势很平滑。最让我满意的是长期漂移表现,一周内没有出现逐渐偏离的趋势,说明模拟链路的温漂已经被控制住了。
5. 常见问题与排查实录
5.1 UART 通信偶发丢帧
我调试时遇到最多的问题是“偶尔读不到数据”。最开始怀疑波特率设置,后来用示波器抓 CS、CLK、TX 三根线,才发现是 CS 拉低后、CLK 开始前的时间太短,从机还没准备好就收到了第一个位。解决办法是在 CS 拉低后加至少几十微秒的延时,再从时钟和发送开始。
另一个容易踩的坑是 CLK 信号质量不好。如果 CLK 走线太长且没有串阻,信号边缘会产生振铃,导致从机采样错位。我在 MCU 端给 CLK 输出串联了 33Ω 电阻,振铃明显减少。CRC 错误的概率从原来的每次启动都会出现下降到了几乎为零。
5.2 读数跳变到怀疑人生
如果你一上电读数就在几度范围内乱跳,先别怀疑芯片坏了,大概率是干扰直接进到了模拟输入。我遇到过两个典型案例:第一个是热电偶屏蔽层两端都接地,形成地环路;第二个是数据线从 ADC 输入下方穿过,数字噪声耦合进模拟通道。
排查思路很简单:先把热电偶线拔掉,用短路线短接 ADC 输入,看读数是否稳定在 0°C 附近。如果短路线也跳,问题在板级布局或者电源;如果短路线稳定,问题在热电偶引线或接地方式。按这个顺序排查,半个小时内基本能定位。
5.3 冷端补偿偏差怎么定位
冷端补偿出错的典型现象是:冰水混合物中探头实测值稳定但偏离 0°C 很多,比如显示 2°C。这种偏差通常是 NTC 没有真实反映“热电偶冷端连接处的温度”。
我排查时先测量 NTC 附近 PCB 的实际温度,用红外点温枪对比,再检查 NTC 和端子之间有没有明显的热阻路径。如果是 NTC 离端子太远,中间还有大铜皮散热,那读到的温度和实际冷端温度自然对不上。解决办法是调整布局,让 NTC 紧贴端子,并在热电偶端子四周开隔热槽,减少 PCB 铜皮把热量导走。
5.4 长期漂移与器件老化
长期运行后温度读数慢慢偏移,最常见的原因是 NTC 老化或者基准源温漂累积。NTC 老化很难完全避免,我的处理办法是留一个校准菜单,允许现场用标准温度计重新做一次两点校准,并把修正参数写回 Flash。
如果是追求长期稳定的工业产品,建议把 NTC 换成 PT1000 铂电阻,它的长期稳定性比 NTC 好一个数量级,只是需要更高精度的测量电路。这个升级动作不会改太多软硬件,因为 ADS122U04 自己就能提供恒流源激励,换 PT1000 主要是改一下 B 值查表和激励电流档位。
6. 写在最后的经验与扩展思路
6.1 这套组合能延展到什么程度
ADS122U04 支持 UART 地址扩展,理论上可以在同一条 UART 总线上挂多颗芯片,用不同地址区分。我目前一块板子上挂了 4 颗,分别测风管送风、回风、冷却水和室外补偿温度,MCU 通过地址轮询就能把 8 路差分通道全部读回来。如果你需要更多测点,可以再加一颗,代码量增加得很有限。
温度数据出来之后,后续怎么用才是产品差异化的关键。我把 STM32 上的 MODBUS RTU 从站协议栈跑起来,温度值通过 RS-485 上传到 PLC 和组态软件,同时保留一路以太网口,方便远程监控。对于 HVAC 项目,这套架构可以直接对接楼宇自控系统;对于工业设备,接口换成 4-20mA 输出模块也很容易,只改 MCU 外设就行。
6.2 一些容易忽略的细节
最后分享几个容易忽略但影响很大的细节。第一,热电偶接线端子最好用补偿型端子,普通的铜端子会在连接处产生微弱的寄生热电动势,导致零点偏移,这个误差在校准时会混入偏移量,但温度变化后会显现出来。第二,PCB 上热电偶输入附近不要铺大面积铜皮,铜皮散热会让冷端温度分布不均匀。第三,固件处理 24 位补码时一定要用有符号右移,否则温度低于 0°C 时数据会变成巨大的正数,很多“高温报警”的假故障就是这样来的。
我个人在实际项目里最深刻的体会是:这套方案的精度瓶颈从来不在 ADC,而在冷端补偿和现场布线。芯片选对了,通信接口稳定了,剩下的功夫都应该花在“让传感器真正感受到它应该感受的温度”上面。下一次如果你也遇到热电偶读数离谱的问题,不妨按这个思路从冷端和地环路开始查,而不是一上来就怀疑芯片坏了。