在工业现场做温度采集,最难处理的往往不是“把温度算对”,而是“把微伏级信号从干扰环境里救出来”。这个项目用的是 TI 的 ADS122U04IPWR——一颗 24 位、四通道、带 UART 接口的 ΔΣ 型 ADC,配合 STM32F303VE 这颗带浮点运算单元的 Cortex-M4 主控,专门处理暖通空调(HVAC)和工业设备里的 PT100/PT1000 铂电阻、K 型热电偶信号。整套方案做下来,风管温度、水管温度、设备表面温度都能覆盖,精度和抗干扰能力都比我之前用 MCU 内置 ADC 加外部运放的方案好了一个量级。这篇文章把选型思路、硬件设计、软件驱动、校准过程和踩过的坑都整理出来,供做同类项目的朋友参考。
1. 为什么是这个组合:ADC 和 MCU 的分工逻辑
1.1 ADS122U04IPWR 到底强在哪
先说这个 ADC。ADS122U04 属于 TI 的 ADS122U 系列,核心是一个 24 位 ΔΣ 型转换器,内部集成了可编程增益放大器(PGA),增益从 1 到 128 可调,还带两个可编程激励电流源(IDAC)和一个低温漂的内部基准电压源。这四个功能叠在一起,意味着外设电路可以做得非常精简:PT100 的激励电流由 IDAC 提供,电压基准由内部基准承担,信号放大交给 PGA,MCU 那边只需要通过 UART 读结果就行。
很多朋友第一反应是“为什么要用带 UART 的 ADC,SPI 不是更快吗?”这个问题的答案要放到工业现场看。SPI 是同步通信,SCLK 和 MOSI/MISO 三根线一起跑,一旦传感器板和主控板距离超过半米,时钟抖动、信号反射就会开始捣乱,而且做电气隔离时光耦要占用好几路。UART 是异步通信,两根线就能工作,走 1 到 2 米没有任何压力,要用光耦或数字隔离器的时候也只需要隔离 TX 和 RX 两条通道。其实 TI 这个系列把同一个 ADC 核分成了 ADS1220(SPI 接口)、ADS122C04(I2C 接口)和 ADS122U04(UART 接口)三个版本,就是因为不同总线在工业场景里各有各的拥趸。温度测量这种本身就不追求高刷新率的应用,UART 完全够用。
再说几个实际用下来很讨喜的细节。内部基准是 2.048V,温漂典型值在 10ppm/°C 左右,这对工业温度测量足够了;IDAC 电流可以从 10uA 一直配到 1500uA,驱动不同阻值的铂电阻都有余量;内置温度传感器还能用来做热电偶的冷端补偿,省掉一颗外置的冷端芯片。这些功能如果是用“MCU + 外部运放 + 外部基准 + 外部电流源”的方案去做,电路板面积至少要翻一倍,调试的变量更是成倍增加。
1.2 STM32F303VE 在这里面负责什么
STM32F303VE 是一颗基于 Cortex-M4 内核、带 FPU 的 MCU,主频最高 72MHz。单看主频,在当下不算亮眼,但温度测量这个场景对 MCU 的要求本来就不是“算得快”,而是“算得稳”。FPU 的好处非常直接:RTD 线性化和热电偶查表都需要做浮点运算,如果在没有 FPU 的 Cortex-M0 上做,一个多项式可能得耗几十微秒,而在 F303 上只需要几个指令周期。
F303 系列真正的强项是模拟外设丰富。它自己有 4 个 12 位 ADC、多个 DAC、比较器和运放,虽然这些在这次项目里主要用来做辅助监控(比如监测板载环境温度、电源电压跌落),但它们的存在让整个设计有了很大的扩展空间。比如以后想在同一块板子上再扩展一个 4-20mA 输入通道,直接用 MCU 内置 ADC 就能完成,不需要额外加芯片。
在这个项目里,STM32F303VE 承担的任务很明确:通过 UART 和 ADS122U04 通信、读取转换结果、把原始码值换算成电压和电阻、再用 Callendar-Van Dusen 公式算出温度,最后把温度值打包成协议发给上位机或触摸屏。整颗 MCU 的负载率其实不到 20%,剩下的资源可以用来做报警判断、历史数据缓存和 Modbus 从站协议。如果用的是资源更小的 MCU,当然也能跑,但没必要把自己的方案卡死。
1.3 这个方案和传统方案的对比
我在项目前期做过一个对比,把“MCU 内置 ADC 直接采集”、“外置 16 位 SAR ADC + 仪表运放”、“ADS122U04 方案”三种路线摆在一起看。MCU 内置 ADC 的劣势很直白:12 位分辨率、无内置 PGA、基准源精度一般,采集热电偶信号时几十微伏的噪声就能让读数跳好几度,完全不适合做高精度温度。16 位 SAR ADC 加仪表运放的做法在实验室里能调到不错的指标,但电路复杂度和成本都高,而且 SAR ADC 对 PCB 布局非常敏感,稍有不当,数字噪声就会耦合到模拟前端。
ADS122U04 的方案等于把高精度模拟前端整块集成进了芯片里,PGA、基准、激励源都在内部,模拟路径非常短,受外部干扰的概率就小了很多。24 位分辨率带来的直接好处是余量充足:即使信号满量程只用了 ADC 量程的一小部分,依然能保证足够的分辨率。配合 20SPS 的数据速率,内部数字滤波器对 50Hz/60Hz 工频干扰有很强的抑制能力,这在工业现场是“救命”级别的特性。
2. 硬件设计:测量链路到底该怎么搭
2.1 PT100 接入方式:两线、三线还是四线
铂电阻的接线方式会直接影响测量精度,这个环节值得先说清楚。两线制最简单,就是两根线把 PT100 接进输入端,但导线电阻直接叠加在传感器电阻上,比如 1 米长、0.5 平方毫米的铜线大约有 0.033Ω,PT100 在 0°C 时的标准阻值是 100Ω,温度系数大约是 0.385Ω/°C,算下来 0.033Ω 的导线电阻就能引入大概 0.09°C 的误差。导线稍长一点,误差轻松上 0.5°C,这对工业温度测量来说是不能接受的。
四线制最准,它是用两根线给 PT100 送激励电流,另外两根线专门采电压,采样回路里几乎没有电流,所以导线电阻不影响测量。但四线制需要四根线,在很多场景下布线和接线的成本都高。实际项目里,三线制是性价比最高的选择,管线只需要三根线,配合 IDAC1 和 IDAC2 两个激励源,可以用“电流强制、电压采样”的方式抵消大部分导线电阻的影响。
三线制的接法我建议这样:IDAC1 输出的激励电流从 AIN0 脚接到 PT100 的一根引线,PT100 的另一端接回到 AIN2;同时 IDAC2 从 AIN1 脚输出另一路电流,通过一根与前面等长的导线接到 PT100 的公共端。这样设计的关键是让两路线电阻上产生的压降尽量对称,然后通过差分输入 AIN2-AIN3 把公共端的导线压降抵消掉。实际接线时,三根导线要尽量等长、等规,否则残余的不平衡仍然会产生误差。ADS122U04 的 IDAC 路由可以灵活配置到不同模拟输入引脚,具体映射关系在寄存器 CONFIG3 里设置。
2.2 激励电流和增益怎么定
选定接线方式后,下一个需要拍板的是 IDAC 电流值和 PGA 增益。这两者需要放到一起算,因为它们共同决定了 ADC 的输入信号幅值。
先看 PT100 的阻值范围。工业上常用测量范围是 -40°C 到 150°C,对应 PT100 的阻值大约是 84.27Ω 到 158.84Ω。如果选择 250uA 的激励电流,信号电压就是 21mV 到 39.7mV。再想让小信号部分也想有足够的分辨率,可以用 PGA 把信号放大,比如 PGA=64 时,满量程输入范围大约是 2.048V/64=32mV,39.7mV 的满量程超出了范围,所以这时候就得把电流降到 100uA,或者调整 PGA 换成 PGA=32。
我最终用的是 250uA 激励电流加 PGA=32。整量程 64mV 正好覆盖 21mV 到 39.7mV 的信号,留了接近 40% 的余量。激励电流选 250uA 而不选 1mA,是为了控制 PT100 的自热效应。电流流过电阻会产生焦耳热,1mA 在 100Ω 上的功耗是 0.1mW,在热容量小的场景里可能引入 0.1 到 0.5°C 的额外温升;250uA 时功耗只有 0.025mW,自热基本可以忽略。这是仪表设计的常识:激励电流不是越大越好,够用就行,信号幅度不够就靠 PGA 拉。
如果用的是 K 型热电偶,信号特性完全不同。K 型热电偶在 0 到 1000°C 范围内输出电压只有 0 到 41mV 左右,而且 0°C 附近输出接近 0mV,这时候 PGA 需要开到 32 或 64,才能把信号推到 ADC 的合理量程内。热电偶本身不需要激励电流,所以 IDAC 是关闭的,测量的是差分电压。ADC 内置温度传感器在这种情况下就非常重要,因为热电偶测的是热端和冷端的温差,必须要用冷端处的真实温度去补偿,才能得到热端的绝对温度。
2.3 基准选择和比例测量的妙处
关于基准电压,ADS122U04 可以在内部 2.048V 基准和外部基准之间切换。我一上来就选了内部基准,但这里有个更深层的技巧值得展开:如果用同一个 IDAC 去驱动 PT100 和基准电阻,IDAC 电流的变化会被分子分母同时吸收,也就是所谓的“比例测量”(ratiometric)。
具体来说,激励电流流过 PT100 产生电压 VI,流过基准电阻产生电压 VREF。ADC 的转换结果本质上是 VI 和 VREF 的比值,如果两个电压都来自同一个电流源,那么电流的温漂和长期漂移会在比值中消掉。这个特性的好处是,即使 IDAC 电流随着温度缓慢漂移,最终输出的电阻计算结果也不会跟着漂。所以如果条件允许,我更推荐用外部基准电阻加比例测量的接法,它能明显提升长时间连续运行时的稳定性。当然,如果为了简化设计直接用内部基准,只要环境温度变化不那么剧烈,数据也是完全可用的。
2.4 UART 通信和隔离电路
ADS122U04 的 UART 接口部分有个容易踩坑的地方——它没有片选信号,所以 MCU 和 ADC 之间没有任何“握手”信号来标志一帧数据的开始。工作时 ADC 会持续判断收到的字节,靠特定格式的命令帧来识别操作。这就要求我们保证发送端波特率准确、帧格式一致,尤其上电后默认波特率一般为 2400,要改成 115200 需要先同步、再写配置寄存器。
我在原理图上给 UART 的 TX、RX 各加了一根 ESD 保护二极管,同时串联了 22Ω 的电阻做限流。如果系统需要和外部仪表通信,我建议在 MCU 这边做隔离,ADS122U04 到 MCU 之间用数字隔离器,比如 ISO7721 或 ADuM 系列都行,隔离后的通信距离能拉到几十米,抗共模干扰能力也上了一个台阶。隔离电源可以用独立的 DC-DC 模块,但要注意 DC-DC 的开关噪声,布局时隔离电源要尽量远离模拟输入走线。
3. STM32 端软件实现:从寄存器配置到温度计算
3.1 UART 初始化和同步时序
软件部分第一步是把 STM32F303VE 的 UART 配置好。我用的是 USART1,波特率 115200,8 位数据、无校验、1 位停止位。需要强调一点:虽然 MCU 这边能轻松跑到 115200,但 ADS122U04 在上电后默认波特率不一定是 115200,要看它的默认配置。我在数据手册里查到默认波特率是 2400,所以在代码里要先进“配置模式”,把波特率改成 115200,再正常通信。
这段逻辑看起来简单,实际调试时很容易犯两个错误。第一个错误是上电后立刻发命令,ADC 还没完成内部初始化,命令被悄悄丢掉;第二个错误是没有发送同步字节,导致 ADC 对收到的第一个字节误判。稳妥的做法是上电后延时 10ms 以上,然后连续发送至少 3 个 0x00 字节,让 ADC 的 UART 接收状态机回到一个已知状态,再发送真正的命令帧。UART 是异步通信,没有片选,时序上就是要靠这种“先导字节”来对齐。
寄存器配置部分,我用的是读写寄存器命令。ADS122U04 的寄存器一共有 4 个,分别叫 CONFIG0 到 CONFIG3,地址是 0x00 到 0x03。配置流程大致是这些步骤:先写好 CONFIG0(输入通道选择、PGA 增益)、CONFIG1(数据速率、转换模式、温度传感器开关)、CONFIG2(基准选择、IDAC 电流)、CONFIG3(IDAC 路由到哪个引脚),然后启动连续转换,开始读数据。写寄存器时要注意,某些关键配置必须在 ADC 处于关断状态或空闲状态时修改,否则寄存器写不进去或者要等当前转换完成才能生效。
一个有效的写寄存器帧示例大概是这个样子(示意):
// 写 1 个字节到 CONFIG0:首字节 WREG 命令+地址0,第二字节长度-1,第三个字节是配置值 uint8_t cmd[] = {0x40, 0x00, 0x00}; // 写 3 个字节到 CONFIG1-CONFIG3:首字节 WREG 命令+地址1,第二字节长度-1=2,后面跟三个配置值 uint8_t cmd2[] = {0x41, 0x02, 0x80, 0x00, 0x00};这些命令字节的定义在数据手册里很清楚,我这里提醒一个细节:WREG 的第二字节表示“要写的寄存器数量减一”,我最初就是因为没减一,导致配置只写成功了一半,读数一直是不正常的满量程码。
3.2 读取转换结果和二进制补码换算
配置完成并且启动连续转换后,ADC 会按照设置的数据速率不断产生转换结果。读取数据有两种方式:一种是直接用 RDATA 命令读取当前最新的转换结果,另一种是在数据准备好时通过引脚状态通知 MCU,MCU 再读取。工业应用中我习惯用第一种加定时轮询,简单可靠,STM32 端用定时器中断或者主循环里按时读取,不会丢数据。
ADS122U04 返回的转换值是 24 位二进制补码格式,范围是 0x800000(负满量程)到 0x7FFFFF(正满量程)。拿到原始值后要换算成电压,公式是:
float adc_code = (float)raw; // 先把24位补码符号扩展到32位 float voltage = adc_code * 2.048f / (128.0f * 8388608.0f); // VREF=2.048V,PGA=128时上面这个例子用的参数是 PGA=128,如果实际配成其他增益要跟着改。很多朋友第一次做 24 位 ADC 都容易踩一个坑:24 位补码需要先看最高位,如果为 1 说明是负数,要把数据手动扩展成 32 位有符号整数,否则用 int 类型直接读,负数会被当成一个巨大的正数,算出来的温度直接变成 1000°C 以上。这个问题的典型表现就是“铂电阻温度显示异常高”,排查时一定要先看原始码值。
3.3 铂电阻线性化计算
PT100 的温度-阻值关系不是线性的,所以拿到电阻后还不能直接线性折算温度。标准做法是用 IEC 60751 推荐的 Callendar-Van Dusen 公式。整个温区用一个公式表达:
R(t) = R0 * (1 + At + Bt^2 + C*(t - 100)*t^3)
其中 R0 是 0°C 时的标称阻值,PT100 就是 100Ω;A = 3.9083e-3,B = -5.775e-7,C = -4.183e-12。这个公式在 -200°C 到 850°C 的范围内都是适用的,C 项主要负责低温段的非线性矫正。
实际使用中,我们通常是测到了电阻 R,要通过反解公式求温度 t。这个方程是一元四次方程,没有简单的解析解,最快的方法有两种。一种是牛顿迭代法:先估计一个初始温度(比如直接用线性公式 t = (R/100 - 1) / 0.00385),然后迭代 3 到 5 次,误差就能收敛到 0.01°C 以内。另一种是查表加插值:把整个温度范围以 1°C 为步长预存一张阻值表,然后用二分查找加线性插值或者三次样条插值。STM32F303 有 FPU,我直接用牛顿迭代法,代码清晰,误差可控。
这里有个工程细节想强调:在 STM32 上做浮点计算时,尽量统一用 float 单精度。单精度够用吗?温度计算精度只要求 0.01°C 的话,单精度浮点的 23 位尾数完全不虚。如果代码里写了 double,在 F303 上会触发软件浮点库,计算耗时反而增加好几倍。我把 A、B、C 这些常量都定义成 float,编译时告诉编译器不要自动提升为 double,速度提升非常明显。
4. 实测数据与校准心得
4.1 实际测量精度和噪声表现
硬件和软件都跑通之后,我做了两轮验证。第一轮是在实验室用精密电阻箱模拟 PT100,分别设在 80Ω、100Ω、120Ω、150Ω 几个点,看 ADC 读回来的电阻值偏差。第二轮是用真正的 PT100 放在冰水混合物(0°C)和沸水里(当地实测约 100.1°C)做端点验证。
在数据速率设为 20SPS、PGA=32、IDAC=250uA 的情况下,用连续 100 次采样的平均值做统计,电阻读数的峰峰值波动在 0.005Ω 到 0.01Ω 之间,折算成温度大约是 0.013°C 到 0.026°C 的波动。这个噪声水平在工业温度测量里已经非常优秀了。如果要求更高,可以把数据速率降到 20SPS 以下不具备,或者对读数做滑动平均滤波,代价是响应变慢。
ADS122U04 的数据速率和噪声之间的权衡要放到具体对象上看。HVAC 的风管温度、水管温度本身热惯性大,温度变化很慢,用 20SPS 甚至更低的数据率完全没问题,系统响应足够快。工业设备表面测温如果希望能捕捉到快速变化,可以切换到 90SPS 或 175SPS,但噪声会明显变大,需要配合额外的数字滤波。我的经验是:预报性测温场景 20SPS,过程监控场景 90SPS,不要用过高的速率去换本来就不需要的带宽。
4.2 系统级校准流程
虽然 ADS122U04 本身精度很高,但整个测量链路还有传感器误差、接线电阻、PCB 焊盘温升等因素,所以做一次系统级校准还是必要的。我的校准流程分成三步。
第一步是零点偏移校准。把 ADC 的两个差分输入短路,读出来的码值理论上应该是 0,实际会有少许偏移,把这个偏移值记下来,之后的每次读数都先减掉。这个偏移主要是 PGA 的输入失调电压和 PCB 上轻微的热电势引起的,数值通常很小,但乘以 PGA 增益后会被放大,所以一定要做。
第二步是增益校准。用一个高精度电阻箱设定一个接近量程中段的标准电阻,比如 125Ω,读取 ADC 换算后的电阻值,和标准值对比得到一个修正系数。之后所有电阻读数都乘以这个系数。这个系数主要用来补偿内部基准电压的实际偏差和 PGA 增益误差,做完之后整个量程内的线性误差可以控制在 0.02% 以内。
第三步是温度点验证。有条件的话把传感器伸进干井炉或恒温槽,至少标三个温度点(比如 0°C、50°C、100°C),检查最终温度输出的误差。如果发现某个温度段偏差较大,可以建立一张两点或三点校准表,在软件里做分段线性插值。工业现场这类校准基本都是用后两步,就够用了。
还要留意预热时间这个问题。ADS122U04 内部的基准和 PGA 都有温度系数,上电之初芯片本身温度还没稳定,前几分钟的读数会有小幅漂移。我在测试时观察到,从冷启动到读数完全稳定大约需要 3 到 5 分钟,对于长期上电运行的应用,这个特性没什么影响,但如果做快速抽检,就得注意等到稳定后再读数。
5. 常见问题与故障排查记录
5.1 转换结果全是满量程或全是零
这个现象几乎是所有 24 位 ADC 项目里最容易遇到的头号问题。如果读回来的值固定在 0x7FFFFF 或 0x800000,通常不是传感器坏了,而是配置错误。我排查时会按下面这个顺序看。
第一步看通道选择。CONFIG0 里的 MUX 位有没有真的切到对应的差分输入?第二步看 PGA 增益和基准是否匹配。输入信号放大了 128 倍之后,只要电压超过量程一点点,ADC 就会“顶满”。遇到满量程时先把增益降到 1,看信号是不是在范围内,再做加法。第三步看 IDAC 有没有把电流送到传感器。用万用表测量 AIN0 和 AIN2 之间的直流电压,如果没有电压,说明 IDAC 配置或连接线有问题。
还有一个非常容易被忽略的问题:输入通道的引脚有没有悬空。ADC 的模拟输入是不能悬空的,悬空时输入共模电压不确定,转换结果就会出现满量程跳变。我在实验板上遇到过这个问题,把闲置通道接到模拟地或者配置成内部传感器测量,现象立刻消失。
5.2 读数有规律地周期性跳动
如果读数不是一个稳定的噪声,而是“刷一下高、刷一下低”的周期性波动,多半是工频干扰或者是电源噪声。工频干扰可以用 20SPS 数据速率来解决,这个速率下数字滤波器在 50Hz 和 60Hz 处都有陷波特性。我当时遇到的抖动就是工业现场的 220V 电缆靠近了传感器走线,把数据速率从 100SPS 改到 20SPS 之后,读数立刻稳定了。
电源噪声则是另一个高频问题。如果 ADC 的供电电源纹波大,转换结果也会跟着波动,表现为一种高频抖动。处理方法是给 ADS122U04 的 AVDD 和 DVDD 分别加一个 10uF 钽电容和 0.1uF 陶瓷电容,且这些电容要尽量靠近芯片电源引脚。我最初只是随便引了一根电源线到面包板,测量结果惨不忍睹,后来画了正规 PCB,加上完整的电源滤波,噪声水平立刻恢复正常。
5.3 STM32 读到的数据和预期差一个固定倍数
这个现象通常出现在 PGA 配置和电压计算公式没对准。我调试的时候把 PGA=128 配置写进去了,但换算公式里仍然默认 PGA=32,结果所有读数都大了四倍,温度自然不对。解决方法是把换算公式里的 PGA 参数做成一个变量,每次配置后从寄存器回读一遍,确认实际生效的增益是多少,再拿这个值去算温度。ADS122U04 支持回读寄存器,花 10ms 读取 CONFIG0 就能避免这类低级错误。
5.4 长期运行后出现偶发错误码
工业现场设备要跑几个月甚至几年不重启,ADS122U04 的 UART 没有片选,偶发的一个字节错误可能导致命令解析错乱。为了增强稳健性,我在软件里加了两个机制。一是周期性做寄存器回读校验,如果发现配置值被改写,就重新写一遍配置;二是对转换结果做合理性检查,比如温度突然从 25°C 跳到 500°C,或者电阻值超出 PT100 量程,就丢弃这次读数并触发一次 ADC 软复位。这套机制跑下来,数据链路基本不需要人工干预。
ADS122U04 的 UART 本身支持 CRC 校验,我觉得有必要在说明一下:如果项目对数据完整性有极高要求,可以启用 CRC 功能,代价是每帧数据会多出两个字节,通信时序稍复杂一些,但稳定性更上一层。具体配置方式在数据手册里有,我这里就不展开写代码了,遇到这方面问题的朋友可以对照手册和我的流程排查。
6. 项目扩展与个人经验总结
整个方案做下来,我最想分享的其实不是某个具体寄存器配置,而是一条测量链路的整体思维:传感器信号是高阻抗微弱信号,模拟前端要靠近传感器,高精度 ADC 负责把信号数字化,MCU 只做计算和通信,不要试图用一个通用 MCU 的片内 ADC 去干高精度测量的活。ADS122U04 和 STM32F303VE 的组合,把一个原本可能需要七八颗芯片才能拼出来的链路压缩成了两片核心器件,这对产品体积、功耗、可靠性和调试效率都是实打实的提升。
实际运行中,这个方案给我留下的最深印象是它的稳定性。连续通电 72 小时,环境温度在空调房内轻微波动,读数的零点漂移不到 0.05°C。如果以后环境温度变化更剧烈,我会改用外部基准电阻做比例测量,那样温漂会更小。做 HVAC 项目时,我还把同一块 ADC 的另一个通道接了一个 PT1000 来测回风温度,两个通道交替转换,完全不影响各自的精度,这正好验证了四通道设计的好处。
最后留一句实在话:不要迷信 24 位。ADC 分辨率只是上限,真正决定精度的永远是模拟前端、电源、基准、传感器和校准流程。这颗 ADC 能把上限拉得很高,剩下的就看大家的板子画得干不干净,校准做得认不认真了。