在工业现场和 HVAC 系统里折腾温度采集,最烦人的往往不是传感器本身,而是信号链路上那些说不清道不明的干扰和误差。热电偶的毫伏级信号、PT100 的长引线电阻、控制柜里的变频器噪音,随便哪个都能让数据变得没法看。我之前做过几块基于 ADS122U04IPWR 和 STM32F042C6 的测温板卡,算是把这套组合彻底摸了一遍,今天把从硬件设计到软件滤波的完整思路和踩过的坑都整理出来,给正在选型或者已经踩坑的朋友一个参考。
ADS122U04 是 TI 家一颗内置 UART 接口的 24 位 Δ-Σ ADC,四通道,自带可编程增益放大器和两个激励电流源,专门为温度传感器这类小信号场景设计。STM32F042C6 则是意法半导体入门级 Cortex-M0 内核 MCU,主频 48MHz,片上资源不多但刚好够用,关键是自带一个 USART,可以直接跟 ADS122U04 对接,省掉 SPI 转串口的麻烦。这两个芯片搭在一起,本质上是用一颗带串口输出的高精度 ADC 配合一颗轻量级 MCU,组成一套完整的温度采集前端,既能处理热电偶、RTD 这种微弱信号,又能把数据通过 UART 或 RS-485 传到上位机或者 PLC。
这套方案的适用场景很明确:工业过程控制里的多点温度监测、HVAC 系统的回风/供水温度采集、或者设备巡检用的便携式测温仪。如果你只需要一两路高精度温度,又不想在布线上折腾 SPI 时序和隔离,那这种“ADC 自带串口 + MCU 只做协议解析”的模式会省心很多。
1. 整体设计思路与硬件选型拆解
1.1 为什么偏偏是 ADS122U04,而不是其他 24 位 ADC
选 ADS122U04 之前我对比过 ADS124S08、ADS1262 还有经典的 ADS1115。先说结论:ADS1115 是 16 位,做普通室温监测够用,但想分辨热电偶的 0.1℃ 变化就差意思了;ADS124S08 和 ADS1262 性能更强,可它们都是 SPI 接口,在信号隔离和长距离传输上要多做一层转换。ADS122U04 的差异化价值在于它集成了 UART 接口,MCU 这边只要一根 TX 一根 RX 就能读数据,而且它内部自带 CRC 校验,字节在线上被干扰了能直接发现,这在工业现场比什么花哨功能都实在。
再看它的模拟前端,内部有两路可编程激励电流源,输出范围从 0.5μA 到 1500μA 可选,这对于三线制 PT100 特别关键。三线制 PT100 的引线电阻补偿原理,就是靠两个等值电流源分别流过两根引线,然后在 ADC 输入端做差分,把引线电阻上的压降抵消掉。ADS122U04 内部已经帮你把这两个电流源匹配好了,你只需要在寄存器里把 IDAC 配置到对应通道就行。如果用外部搭恒流源,匹配精度根本做不到这么高,而且温漂难控制。
还有一点容易被忽略的,ADS122U04 的 PGA 增益范围是 1 到 128,输入端的绝对输入范围是 AVDD - 0.2V 到 AVSS + 0.2V。这意味着如果你用单极性供电,负信号没法直接进来,热电偶的负温度输出会直接被削掉。解决办法要么用双极性供电,要么在输入通道上叠加一个直流偏置。我后面在硬件设计章节会详细说这个,这里先提个醒。
1.2 STM32F042C6 在这套方案里的角色定位
STM32F042C6 算是我故意选的一颗“够用就好”的 MCU。它主频 48MHz,Flash 32KB,RAM 6KB,片上带一个 USART、一个 I2C、一个 SPI,还有 12 位 ADC。这颗芯片跑 ADS122U04 的 UART 协议解析、PID 控制算法、或者简单的 Modbus 从站协议,压力都不大。选它的另一个理由是它内置 USB 控制器,如果你想把采集器做成 USB 热电偶采集棒,F042 可以直接枚举成 CDC 虚拟串口,硬件上不加任何转换芯片。
实际项目里,我通常让 STM32F042C6 只干三件事:第一,上电时通过 UART 配置 ADS122U04 的寄存器,包括 PGA 增益、数据速率、激励电流源、通道选择;第二,周期性地读取 ADC 转换结果,做线性化和滤波;第三,把温度值打包成 Modbus RTU 帧,通过 RS-485 发出去。也就是说,MCU 在这里是一个协议处理和数据网关的角色,不碰任何模拟信号处理,这也是这种分离架构最大的好处——数字部分和模拟部分可以独立调试。
1.3 方案的整体信号链路规划
以一块典型的两路 PT100 + 两路热电偶测温板为例,信号链路是这样的:PT100 传感器经过三线制接入,由 ADS122U04 内部 IDAC1/IDAC2 提供 500μA 激励,差分信号进入 PGA,放大后进入 24 位 Δ-Σ 调制器,数字滤波后通过 UART 发出;热电偶信号则直接接入差分通道,PGA 增益设置在 32 或 64 倍,冷端补偿由板上 NTC 或者 DS18B20 完成,MCU 读到热电势后查表或计算得到真实温度。
这套链路中有一个核心逻辑值得展开说:为什么 PT100 需要恒流源激励而热电偶不需要。PT100 是电阻型传感器,要测电阻必须先给一个已知电流,然后测电压;热电偶是电压型传感器,自身输出毫伏级电压,不需要激励。ADS122U04 的 IDAC 就是为前者准备的,而后者靠 PGA 的高增益直接放大。这也是为什么很多新手在配 ADS122U04 时会在寄存器里反复纠结——你得先搞清楚自己用的是哪种传感器,再决定开不开 IDAC、开多大增益。
2. 硬件设计实操:从原理图到 PCB 的完整细节
2.1 供电架构与参考电压设计
模拟供电和数字供电必须分开处理,这是玩高精度 ADC 的第一条铁律。ADS122U04 我习惯用 AVDD = 5V、DVDD = 3.3V 的方案,AVDD 负责模拟电路和 IDAC,DVDD 负责数字接口逻辑,这样 UART 电平可以直接跟 STM32F042C6 的 3.3V GPIO 对接,不需要额外的电平转换电路。
5V 供电我用了一颗低噪声 LDO,比如 TPS7A4901 或者 LP5907,输入从 24V 系统电源通过 DC-DC 降到 5.5V 左右,再由 LDO 出 5V。这里千万别用开关电源直接给 AVDD 供电,纹波会把 24 位 ADC 的表现直接拉垮。LDO 后面再放一个 10μF 钽电容并联 100nF 陶瓷电容做去耦,走线时让这两个电容尽量靠近 AVDD 引脚。
内部参考电压是 ADS122U04 的一个重要特性,它内部自带一个精度为 ±0.2% 的 2.048V 基准,出厂校准过温漂。对于绝大多数工业测温场景,内部基准的精度和温漂已经足够用了。如果你要做比对标准或者计量级采集,那可以考虑外部基准 REF5025 之类的,但需要改寄存器配置,把内部基准关掉。我的经验是,除非客户明确要求,否则一律用内部基准,省出来的面积和成本都很可观。
2.2 热电偶输入通道的前端保护与偏置设计
前面提到过 ADS122U04 的输入共模范围限制,热电偶负温度时输出负电压,直接接入会出问题。我的做法是在每个模拟输入通道对地接一个 10MΩ 电阻到 VBIAS,VBIAS 由 AVDD 通过两个 10kΩ 电阻分压得到约 2.5V,这样热电偶输出的负电压就被抬高到 1.25V 到 2.5V 之间,落在 ADC 的合规输入范围内,同时不影响差分测量的精度。
另外,工业现场的热电偶线经常会被感应出共模电压,甚至直接搭到动力线上。所以输入保护不能省:每个输入端串联一个 1kΩ 电阻,并联一个 TVS 管到 AVSS/AVDD,再加一个 1nF 的差模电容。这个滤波电容的取值要跟 PGA 输入阻抗配套,1nF 配 1kΩ,截止频率大概在 160kHz 左右,对 50Hz 工频干扰和高频 EMI 都有一定的抑制,又不影响测量带宽。你要是把电容加太大,比如 100nF,PGA 的输入建立时间会不够,数据抖动反而变大,这是一个很容易被忽略的细节。
2.3 PT100 三线制接法与 IDAC 配置参数选择
PT100 的接法直接决定了测量精度,三线制是最常用的折中方案。原理是让两个等值电流源分别流过传感器的两根引线(一根在激励回路,一根在测量回路),这样引线电阻 RL1 和 RL2 上的压降在差分输入端互相抵消。接法上,IDAC1 输出到 PT100 的一端,另一端接 IDAC1 的回报引脚,而差分输入则同时采集 PT100 两端的电压。ADS122U04 的 IDAC 支持这个模式,你把 IDAC 配置成“IDAC1 → AIN0,IDAC2 → AIN1 或者不接”,再设置好回报通道即可。
关于电流源大小的选择,我推荐 500μA。太小的电流(比如 50μA)虽然自热小,但信号幅度也小,对 PGA 噪声和 EMC 干扰更敏感;太大的电流(比如 1mA)会让 PT100 本身发热,0.1℃ 的误差就是这么来的。500μA 在 0℃ 时 PT100 上产生 0.5mA × 100Ω = 50mV 电压,在 100℃ 时产生约 69.25mV,对 PGA 来说是非常舒服的输入范围。
2.4 PCB Layout 的关键环节
Layout 的功夫全在模拟输入走线和地平面分割上。首先,ADS122U04 的模拟地和数字地要单点连接,在芯片下方的焊盘处汇合,避免数字噪声通过地平面灌进模拟地。其次,热敏电阻或 PT100 的走线要用差分对形式,两根线紧贴着走,减小环路面积,环路面积越小,感应到的磁干扰越小。第三,晶振要远离模拟输入端子,STM32F042C6 的 8MHz 晶振如果太近,高次谐波会通过空间耦合进 ADC,我的经验是至少保持 1cm 以上距离,或者加一个屏蔽地线环。
还有一个细节是 UART 走线。ADS122U04 的 DOUT/DOUT/SCLK 和 DIN 数字信号虽然速率不高,但在长走线情况下还是会跟相邻的模拟走线产生串扰。我的做法是让数字线走内层,模拟线走表层,中间隔一层完整的地平面,实测下来数据稳定性有明显改善。
3. 软件实现与关键代码解析
3.1 寄存器配置的完整流程
ADS122U04 的寄存器配置走的是“先写配置寄存器,再触发转换”的流程。每次上电,MCU 通过 UART 发送一串配置字节,把以下这些参数写进去:PGA 增益、数据速率、通道选择、IDAC 电流值、IDAC 回报通道、内部参考电压开关。这串字节的格式是固定的:第一个字节是命令头(比如写配置寄存器是 0x42),后面跟着寄存器地址和值。
我给出一个实际的配置代码段,这段代码是把 ADS122U04 配置成“AIN0/AIN1 差分输入,PGA=32,数据速率 20SPS,IDAC=500μA 从 AIN2 回报,内部基准 2.048V”:
uint8_t ads_config[4] = {0x42, 0x00, 0x30, 0x1C}; // 0x42: 写配置寄存器命令 // 0x00: 从寄存器0开始写 // 0x30: 配置寄存器0—— PGA增益32 (010), 数据速率20SPS (101) // 0x1C: 配置寄存器2—— IDAC1=500μA (001),IDAC2关闭,IDAC从AIN2回报 (100)配置寄存器的每一位含义都很关键,搞错一位数据就废了。我这里用的 0x30,对应的二进制是 0011 0000,高三位 PGA 设置里 010 表示增益 32,低五位数据速率 00101 表示 20SPS,跟热电偶的信号带宽完全匹配。0x1C 的二进制是 0001 1100,IDAC1 高三位 001 表示 500μA,低两位 IDAC2 全零表示关闭,中间几位是回报通道设为 AIN2。
配置完成后,还要发一个“启动转换”命令(0x02),然后 MCU 就可以在需要读取时发送“读取数据”命令(0x12),或者配置成连续转换模式持续输出。实际项目里我更推荐读命令模式,这样 MCU 可以自己控制节奏,不用被动接收数据流,减轻中断压力。
3.2 配置寄存器写入后的验证方法
寄存器配置有没有写成功,不能只靠“接下来读数据试试”,那样出了问题很难定位。我用过一个很笨但很有效的办法:写完配置后马上读回配置寄存器,比较读回值和写值是否一致。ADS122U04 支持读配置寄存器命令(0x20 起始读),MCU 发一个 0x20 再加地址,然后连续收四个字节,跟期望值比对。这一步在很多项目里被省掉了,但我就遇到过因为 UART 波特率配置错误导致写入数据错位的案例,如果没有读回验证,你会以为是 ADC 坏了,其实是配置根本没写进去。
3.3 数据读取与字节对齐处理
ADS122U04 的数据输出是 24 位,但它的数据格式是 MSB 先出,而且包含符号位。MCU 接收时要特别注意字节顺序问题。我在代码里是这样处理的:
uint8_t data_buf[3]; int32_t raw_value = 0; // 接收三个字节,然后组合成 24 位有符号数 raw_value = ((int32_t)data_buf[0] << 16) | ((int32_t)data_buf[1] << 8) | data_buf[2]; // 如果最高位是符号位 1,转成负数 if (raw_value & 0x800000) { raw_value = raw_value - 0x1000000; }这里有个坑必须提:ADS122U04 的数据寄存器里,如果只是普通读取,它返回的是上一次转换的结果,而不是“实时结果”。也就是说,你发读取命令前,必须确保已经有至少一次转换完成了。我遇到过第一帧数据全是 FFFFFF 的情况,就是因为上电后配置完立刻去读,PGA 建立时间还不够。解决办法是在主循环里先延时 100ms,再做第一次读取,之后每读一次至少等一个转换周期(对于 20SPS 是 50ms)。
3.4 温度换算与线性化处理
拿到原始位码后,温度换算分两种情况:PT100 和热电偶。
PT100 的换算思路是先把原始值转成电阻值。对于 PGA=1、IDAC=500μA、基准 2.048V 的情况,满量程对应的电压是 ±2.048V,而 IDAC 在 0℃ 时产生的电压是 0.05V,所以原始码到电阻的换算公式为:
R = raw_code / (2^23 - 1) * 2.048V / 500μA比如某个温度下原始码是 537600(十进制),代入公式得到:
R = 537600 / 8388607 * 2.048 / 0.0005 ≈ 262.5Ω对照 PT100 分度表,这个电阻大约是 156℃ 附近。然后用分段线性插值查表即可得到温度,我习惯用 0.1℃ 步长的分度表,精度足够。如果觉得查表麻烦,也有 Callendar-Van Dusen 方程可以算,但那个公式推导比较烦,实际工程中查表最可控。
热电偶的换算更绕一步。原始位码经过 PGA 放大后要还原成热电势 E,公式是:
E = raw_code / (2^23 - 1) * 2.048V / PGA然后用这个热电势去查对应分度表,得到“表端温度”,再根据冷端温度(板上 NTC 测得)查表得到修正值,两者的代数和才是真实温度。这部分代码逻辑不复杂,但 NTC 的查表和热电偶查表都要确保分度单位一致,V 和 μV 混用是新手最常见的 bug,没有之一。
4. 常见问题与故障排查实录
4.1 读回数据全是 0xFFFFFF 或 0x000000
遇到这个问题,先别急着怀疑 ADC 坏了。我排查的思路是这样:第一步,用示波器看 UART 的 TX 和 RX 波形,确认数据线上确实有脉冲,排除接线断路。第二步,检查波特率是否匹配,ADS122U04 上电默认 2048000bps,但 STM32F042C6 最高只能跑到 48MHz,在某系统时钟下要用 2.048M 波特率,需要正确配置 BRR 寄存器,而且 UART 的过采样模式要设置成 16 倍,否则误差会超 2%,直接导致接收错乱。第三步,检查发送的命令字节是不是对,特别是写配置寄存器前有没有先发“复位命令(0x06)”,我刚上手时因为没发复位命令,芯片处于上一手程序的残留状态,所有写入都无效。
4.2 读数跳变、低几位一直在抖动
如果读数稳定但低几位一直在抖,大概率是数字滤波配置太低。ADS122U04 的数据速率从 20SPS 到 1000SPS 都有,速率越高,内部数字滤波器带宽越宽,噪声越大。工业现场 50Hz 工频干扰强烈时,最稳妥的选择是 20SPS,这个速率下数字滤波器对 50Hz/60Hz 都有不错的抑制比。你要是追求响应速度,至少要选 100SPS 以上并配置成 50/60Hz 抑制模式,但那会牺牲有效分辨率,我通常不推荐。
还有一种情况是基准电压不稳。内部基准虽然出厂校准,但如果 AVDD 是开关电源出来的,基准电压会跟着纹波一起抖。前面建议的 LDO 方案就是干这个的,实测下来 LDO 方案能比 DC-DC 直供少 70% 的读数跳动。
4.3 热电偶读数偏差很大,且与温变趋势相反
这个十有八九是冷端补偿没做对。热电偶测的是热端和冷端的温差,不是绝对温度,冷端必须在探头处测量温度再进行补偿。我做过的项目里,有人把 NTC 放在接线盒外,导致冷端温度测的是空气温度而不是接线端子温度,误差能到好几度。另外,冷端补偿的 NTC 要贴在端子座上,最好用导热胶固定,让它的热惯性跟端子尽量一致,不然温度变化时补偿值滞后,读数会先朝反方向偏一下再回来。
4.4 多个通道串扰问题
ADS122U04 是多通道切换的工作方式,通道切换后 PGA 输入端的多路复用器需要时间稳定,如果读取太急,上一个通道的信号会残留到本次读取里。我的处理方式是每次做通道切换后,在配置寄存器里设置“建立时间”为 2ms 以上,或者简单的办法——切换通道后丢弃前两次转换结果,从第三次开始采样。这个方法简单粗暴但有效,实测能把通道间的串扰压到 LSB 级别以下。
5. 上电调测步骤与标定流程
5.1 最小系统搭建与基本通信验证
拿到新板子,我不会直接怼上传感器调代码,那样出了问题根本不知道是硬件还是软件。我的顺序是:第一步,只焊供电电路和 MCU 最小系统,烧一个 GPIO 翻转程序,确认晶振起振、电压轨正常。第二步,焊上 ADS122U04,但先不接传感器,用 USB-TTL 转串口接 STM32 的 UART,发送复位命令(0x06)和读取设备 ID(如果有的话),确认 UART 通信链路通了。第三步,写寄存器配置,读回验证,确认配置无误后,用短路帽把输入端短接到 AVSS,读取数据应该接近满量程零点,验证 ADC 零点。
这个流程看着繁琐,但能帮你把“通信问题”和“模拟问题”彻底分开。我踩过的教训是:有一次直接接 PT100 上电,结果一直读 0xFFFFFF,后来发现是 UART 线接反了,如果先做通信验证,十分钟就能定位,不会浪费整个下午。
5.2 用标准电阻代替温度计的标定方法
标定的时候,别一上来就用水银温度计或者用冰水混合物现场校验,那个操作复杂还容易引入人为误差。我的习惯是用精密电阻箱(比如 100Ω、138.5Ω、175.8Ω 分别对应 0℃、100℃、200℃)串联到 PT100 接口上,读取 ADC 原始值并记录。然后用最小二乘法拟合出“原始码→电阻值(或温度值)”的线性关系,把斜率和截距写进 EEPROM。这样标定出来的板子,在 0~200℃ 范围内误差可以控制在 ±0.15℃ 以内。
热电偶的标定就稍微复杂些了,因为冷端补偿会引入额外误差。我的做法是先测冷端补偿的 NTC 阻值,同时用一个标准的 PT100 温度计贴到热电偶冷端,比对 NTC 和 PT100 的读数差,把这个差值作为软件修正量存起来。这个修正量会随环境温度缓慢变化,但一次校准后至少能保证 24 小时内的补偿误差在 ±0.3℃ 以内。
5.3 长时间运行稳定性测试的观察重点
交付之前,板子至少要跑 72 小时老化测试。期间重点观察三件事:一是零点漂移,把输入短接,每小时记录一次原始码,看 24 小时后偏移有没有超过 1 个 LSB;二是温漂,利用恒温箱在 25℃ 到 55℃ 之间循环,看读数的变化趋势是否符合理论预期;三是通信稳定性,长时间跑数据,记录 UART 帧错误率和校验错误次数。如果这些指标都合格,基本可以放心交付了。
6. 升级路径与扩展思路
如果你只是做单机版测温,这套配置已经绰绰有余。但实际项目里“单机版”往往只是开始,工业现场通常需要多点组网、远程监控、数据记录。ADS122U04 + STM32F042C6 这个组合在扩展方面也留了足够余地。
一个很常见的扩展是加 RS-485 总线。STM32F042C6 的 USART1 可以和 SP3485 之类的收发器对接,然后跑 Modbus RTU 协议,把多台测温板挂在同一根总线上,上位机通过轮询读取。Modbus 从站协议在小型 MCU 上实现并不复杂,状态机加 CRC 校验大概 300 行代码搞定,网上也有现成的开源实现可以参考。
另一个扩展方向是增加本地存储。F042 只有 6KB RAM,存不了几笔历史数据,但你可以外接一个 I2C EEPROM(比如 AT24C256,32KB),每秒钟存一条温度记录也能存 8 个小时。如果需求是断电存储或者大数据量记录,那就要换带 SPI Flash 接口的 MCU,或者直接上 STM32F103/F407 了。
最后,如果你的系统里已经有上位机或者 HMI,且两者都支持以太网,那可以考虑把 STM32F042C6 升级成带以太网 MAC 的型号,或者外挂一个 W5500 硬协议栈芯片,把数据直接推送到 MQTT Broker。不过这么一来成本会上去不少,我的建议是,先确认客户到底是要“能读到数据”还是“要远程监控”,别过度设计。
我实际用过这套方案做的项目,最典型的是一个 8 通道 HVAC 温度采集器,前端 8 路 PT100 分两组各接一块 ADS122U04,两片 ADC 共用同一个 STM32F042C6 的 UART 资源(通过硬件片选或 GP