ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

高精度PT100温度采集:ADS122U04与STM32的工业级实现

高精度PT100温度采集:ADS122U04与STM32的工业级实现

去年做一套 HVAC 冷水机组的温度采集,甲方扔过来的需求让我印象很深:几十米外的风机盘管进水温度、回水温度要测到 0.05℃ 的稳定性,现场只给一根两芯屏蔽线,还要抗住变频器那边的电磁干扰。一开始我想偷懒,直接用 STM32F411RE 自带的 12 位 ADC 采样 PT100,算了一笔账就放弃了:12 位分辨率在 0~100℃ 区间勉強能分到 0.1℃ 左右,再加上基准漂移、走线压降和共模噪声,实际能做到 ±0.5℃ 就算运气好。最后换成了 TI 的 ADS122U04IPWR 和 STM32F411RE 这套组合:24 位 Δ-Σ ADC 负责把模拟小信号“看清楚”,Cortex-M4F 负责浮点换算温度、跑控制逻辑,中间用 UART 串起来。这篇就把这套方案的选型逻辑、硬件接线、固件时序、温度换算和调试踩坑完整展开,适合做工业变送器、HVAC 控制器、高精度温度采集的工程师拿去参考。

1. 为什么是 ADS122U04+STM32F411RE 而不是老方案

1.1 ADS122U04 到底解决什么问题

ADS122U04 是一颗 24 位 Δ-Σ ADC,和我们熟悉的 ADS1220 是同一家族,但接口换成了 UART。它内部集成了三样东西,少了哪一样做高精度温度都很折腾。

第一是低噪声可编程增益放大器 PGA。PT100 在 0℃ 时电阻只有 100Ω,用 500µA 激励电流,两端电压才 50mV 左右。这种小信号直接进 MCU 的 12 位 ADC,量化噪声就把有效位吃掉大半。而 ADS122U04 内部 PGA 可以放大 1、2、4、8、16、32、64、128 倍,把微伏级信号抬到适合 ADC 满量程的范围。

第二是激励电流源 IDAC。测量铂电阻必须有稳定的恒流源,否则电流一漂,电阻电压跟着漂。ADS122U04 内部有两个可配置的 IDAC,输出 50µA 到 1.5mA 多档电流。这颗芯片厉害的地方在于,它允许你用同一路 IDAC 电流同时流过铂电阻和精密参考电阻,做“比率式测量”,后面我会专门展开讲,这是整个精度体系的基石。

第三是灵活的参考电压。你可以选内部 2.048V 参考、内部 1.5V 参考,也可以把外部精密电阻两端电压直接作为 REFP/REFN 参考输入。做 PT100 最稳的玩法就是选外部参考,让 ADC 的“尺子”和“被测物”用同一股电流驱动,消除电流源漂移。

UART 接口是这颗芯片和 ADS1220 最大的差异。SPI 在板内调试很爽,但拉到工业现场,SCLK、MOSI、MISO、CS 四条线加上地线,还要考虑时钟信号长线反射,距离一长就给隔离和抗干扰增加不少成本。UART 只有 DIN、DOUT 两根信号线加一根地,19200bps 波特率下用双绞线轻松跑几十米,而且和光耦、数字隔离器对接非常自然。

1.2 STM32F411RE 在系统里干什么活

STM32F411RE 是 Cortex-M4F 内核,主频 100MHz,自带单精度浮点 FPU。温度换算看起来只是套公式,但如果要做 PID 闭环、做多点标定、或者跑一套几通道的滤波算法,有没有 FPU 差距非常明显。F411 的 FPU 让 CVD 方程求根、插值查表、卡尔曼平滑这些操作都能在一个主循环里轻松完成,不会挤占控制周期。

F411RE 内置多个 USART,做一个 ADC 通信口、一个调试口、一个 Modbus 口完全够用。处理 ADS122U04 这种 UART 接口的芯片,恰好是它的强项。而且 F411RE 的 USART+DMA 配合得很好,后面我会给一套 DMA 接收方案,把 DRDY 中断后的数据搬运交给硬件,CPU 只管换算。

很多人可能问,F411 自带一个 12 位 ADC,为什么不用?两个原因:分辨率不够,参考电压精度不够。12 位 ADC 在满量程 3.3V 下,一个 LSB 是 0.8mV;PT100 阻值变化 0.385Ω/℃,在 500µA 激励下只有 0.19mV/℃,一个 LSB 对应约 4℃。就算过采样到 14、15 位,也追不上 24 位 Δ-Σ ADC 的线性度和噪声整形能力。所以 F411 的 ADC 在温度测量这里就让它退休,专注干活。

1.3 三种常见方案怎么取舍

方案分辨率/精度接线与隔离开发难度适合场景
STM32F411 内部 12 位 ADC + 运放勉强到 ±0.5℃,受基准和噪声影响大板上搞定,长线能力弱低消费级、要求不高的温度显示
ADS1220 + SPI + STM3224 位,精度高SPI 四线,长线隔离成本高中板级高精度测量、变送器内部
ADS122U04 + UART + STM32F411RE24 位,精度高,远距离抗干扰好三线 UART,光耦/磁隔离容易中工业现场、HVAC 分布式采集

我的习惯是:如果传感器就在主控板上、距离不超过 10cm,ADS1220+SPI 足够;如果传感器分布在几米甚至几十米外,需要隔离、需要简单布线的,UART 版本值得多花几块钱。工程上没有“最贵最好”,只有“最合适”。

2. 硬件设计:从传感器到 ADC 到 MCU 的每个细节

2.1 传感器怎么选:PT100、PT1000 还是 NTC

工业温度测量里,铂电阻是主流。PT100 的优点是 0~100℃ 区间近似线性,阻值变化率约 0.385Ω/℃,成本低、互换性好;PT1000 阻值大十倍,同样激励电流下信号电压高十倍,可以在更低激励电流下获得同样信噪比,自热更小,但传感器自身连接线的影响也更明显。HVAC 领域现在很多项目直接用 PT1000,因为信号大、好处理。

NTC 热敏电阻响应快、价格便宜,但一致性差、曲线非线性剧烈,批量校准成本高。我的建议:做高精度计量或工业控制,老老实实上铂电阻;做消费品、只是“有温度显示”的功能,NTC 足够。

关于几线制的选择,简单说:二线制简单但有导线电阻误差,10 米 0.5mm² 铜线来回约 0.3Ω,对应 PT100 约 0.8℃ 的偏差,不能忽略;三线制用一根额外线补偿导线电阻,工业现场最常用;四线制最准,适合实验室,但布线成本高。我这套方案因为用了比率测量加独立参考电阻,二线制就能把导线误差的影响压到很小,这也是后面的重点。

2.2 比率式测量接线与 IDAC 电流怎么定

先理解一个核心痛点:如果用外部恒流源给 PT100 供电,电流源本身的温漂和噪声会直接进入测量结果。比如激励电流从 500µA 漂到 501µA,PT100 电压就漂了 0.2%,对应温度误差接近 0.5℃。解决思路是“让电流源误差自相抵消”:让同一股电流既流过 PT100,也流过精密参考电阻 Rref,然后用 RTD 两端电压除以 Rref 两端电压。因为 ADC 输出是输入电压与参考电压的比值,公式里的电流 I 被约掉了,电流源再漂也不影响结果。

实际接法采用低侧激励:IDAC 输出引脚接 PT100 一端,PT100 另一端串到精密参考电阻 Rref,Rref 再回到 GND。PT100 两端的差分电压接到 ADC 的差分输入对 AIN0/AIN1,Rref 两端电压接到 REFP/REFN。这样共模电压就是 PT100 和 Rref 上那几百毫伏压降,远低于芯片允许的输入范围,安全。

激励电流选多大由量程、增益、参考电压三者共同决定。我的常用参数:

  • 激励电流 IDAC = 500µA
  • 参考电阻 Rref = 1.5kΩ,则参考电压 Vref = 500µA × 1.5kΩ = 0.75V
  • PGA 增益置为 8
  • 量程按 -50℃ 到 +150℃ 设计,PT100 对应阻值约 80.31Ω 到 157.33Ω

验算一下:0℃ 时 PT100 为 100Ω,RTD 两端电压 500µA × 100Ω = 50mV,经 PGA 放大 8 倍后为 400mV,占满量程 0.75V 的 53%;150℃ 时 RTD 电压 500µA × 157.33Ω = 78.7mV,放大后 629mV,占满量程 84%,头部还留了余量。这个比例比较理想:信号幅度够大,又不至于在高温端削顶。

如果要把量程扩展到 850℃,必须降增益或者降电流。比如 IDAC=250µA、Rref=2kΩ、增益=4,这样满量程参考 0.5V,850℃ 时 PT100 约 390Ω,RTD 电压 97.5mV,放大后 390mV,占 78%。自热方面,250µA 在 390Ω 上耗散只有 24µW,几乎可以忽略。所以没有“万能电流”,先框量程再算参数,算完再用表验一遍。

PCB 布局上记住三条:Rref 必须用低温度漂移的精密电阻,推荐 0.1% 精度、25ppm/℃ 以下;REFP/REFN 引脚各放一个 0.1µF 和 1µF 的滤波电容,位置尽量靠近引脚;模拟区域的地单点接地,传感器引线不要贴着开关电源或继电器走线。

2.3 UART 电气连接和隔离设计

ADS122U04 和 STM32F411RE 之间就是三根线:DIN、DOUT/DRDY、GND。DOUT 同时承担数据输出和转换完成指示,在连续转换模式下,每个转换周期会拉低一次,通知主机“数据可以读了”。这根线建议加一个 10kΩ 上拉电阻,因为开漏输出方便做电平适配。

板内调试时直接 3.3V 对接即可。工业现场如果传感器与 MCU 之间有高压风险或者地电位差,在 UART 线上串数字隔离器,比如 ISO7721 或对应国产型号。隔离器放在靠近 MCU 一侧,给隔离器两侧分别供电,形成干净的“传感器地”和“数字地”。19200bps 对隔离器来说完全是小意思,随便选,但要注意 DOUT/DRDY 的脉冲宽度在隔离后是否还能被中断脚正确识别,实测下来 19200 波特率、低速光耦 10µs 级延迟不会丢数据,如果用慢光耦就得把串口速率降一档。

还有一点,现场走线超过两三米时,DIN/DOUT 建议用双绞线,并在接收端串 100Ω 到 220Ω 的电阻抑制反射。别指望 UART 能像 RS485 一样抗共模干扰,对方是大变频器大电机时,老老实实加隔离和屏蔽层。

3. 固件设计:让 STM32 和 ADS122U04 顺畅对话

3.1 ADS122U04 的 UART 协议不是普通字符串协议

很多人拿到这颗芯片,按习惯直接 printf 一个 0xAA 过去,发现完全没有反应——这是正常现象。ADS122U04 的 UART 帧是字节级的“命令-数据”结构:主机发送一个命令字节,比如写寄存器 WREG、读寄存器 RREG、读转换数据 RDATA,ADC 再回传对应字节。它需要通过 DOUT/DRDY 的变化来判断数据边界,而不是像往常那样按固定时间读。

这里有一个非常容易踩的坑:ADS122U04 的寄存器字段本身按位排列,而 UART 传输时又通常 LSB 先出。这会导致你软件里写的配置字节,在 ADC 看来可能是“位镜像”的。比如你以为写入了 PGA 增益 8,实际芯片收到的字段拼出来可能是完全另一个值。解决办法很简单:写完寄存器后立刻回读,把读到的字节和写进去的字节打印出来对比,不一致就调整你的位填充方式。我见过不止一个同事在这颗芯片上卡了一整天,最后发现是自己配置字拼错了位置。

另外,所有和 ADC 的命令交互必须在 DOUT/DRDY 有效的时间窗口内完成。简单的做法是:禁止片内自动连续输出,采用“主机查询 DRDY 引脚,拉低后再发 RDATA 命令”的方式,一帧一帧读,逻辑清晰不易乱。

3.2 关键寄存器配置思路

不同固件版本寄存器地址可能不同,具体地址以数据手册为准,我这里只说配置思路和字段含义。

采样率:工业温度测量优先选 45SPS 或 20SPS。Δ-Σ ADC 的数字滤波器在低速率下对 50Hz/60Hz 工频干扰有很好的抑制,90SPS 以上则很难兼顾,现场读数会明显发毛。温度本身是慢变量,20~45SPS 完全够用。

PGA 增益:根据前面算好的量程选 8。增益太低,小信号分辨率浪费;增益太高,噪声和失调也会被放大,反而不划算。如果现场发现读数跳变,先降增益观察,不要急着改滤波。

参考源:选择外部参考,也就是 REFP/REFN 引脚输入来自 Rref 两端。这样才构成比率式测量,电流源漂移才能抵消。如果选内部参考,那这套方案的精度优势就去掉一大半。

IDAC 电流和路由:配置 IDAC 输出 500µA,路由到指定的激励引脚。注意 IDAC 输出引脚要设成“始终开启”,不能只在转换瞬间开,否则 ADC 每次采样都在等电流稳定,读数会带毛刺。

转换模式:用连续转换模式,配合外部 DRDY 中断读取。单次模式适合超低功耗电池场景,但每次转换后都要重新触发,时序上多一层开销。

3.3 初始化代码与一次完整读取流程

用 STM32 HAL 库做演示,使用 USART2,波特率 19200,8 位数据、偶校验、1 位停止位。如果手册要求无校验,直接把校验位去掉即可。核心逻辑如下:

// 前置:MX_GPIO_Init() 配置 USART2_TX/RX 复用 // 配置 DOUT/DRDY 引脚为下降沿外部中断输入 void ADS122U04_Init(void) { uint8_t config[5]; uint8_t dummy; // 1. 先给 ADC 一个上电稳定时间 HAL_Delay(100); // 2. 发送写配置命令:WREG + 寄存器地址 // 这里具体命令码以数据手册为准,我只演示流程 config[0] = 0x40 | REG_ADDR_BASE; // WREG 命令 config[1] = 4; // 连续写 5 个字节寄存器 config[2] = SPS_45SPS | PGA_GAIN_8; config[3] = REF_EXTERNAL | IDAC_500uA; config[4] = IDAC_ROUTE_TO_PIN; config[5] = CH_AIN0_AIN1_DIFF | BURNOUT_OFF; HAL_UART_Transmit(&huart2, config, 6, 100); // 3. 回读验证,失败就打印错误 uint8_t rd_cmd[2] = {0x20 | REG_ADDR_BASE, 4}; HAL_UART_Transmit(&huart2, rd_cmd, 2, 100); HAL_UART_Receive(&huart2, &dummy, 1, 100); // 实际读取 5 字节与 config[2..6] 对比 }

读取转换结果时,我强烈建议用 DMA。DRDY 中断里只置一个标志位,主循环看到标志后再发 RDATA 命令并启动 DMA 接收 3 个字节:

void EXTI15_10_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(DRDY_PIN) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(DRDY_PIN); data_ready = 1; // 主循环去读取 } } void ADS122U04_ReadData(int32_t *adc_value) { uint8_t cmd = 0x10 | RDATA_OFFSET; // RDATA 命令 uint8_t raw[3]; HAL_UART_Transmit(&huart2, &cmd, 1, 10); // 阻塞式读取 3 字节,或者用 DMA 后等待完成回调 HAL_UART_Receive(&huart2, raw, 3, 50); // 24 位有符号数符号扩展 *adc_value = (int32_t)((raw[0] << 16) | (raw[1] << 8) | raw[2]); if (*adc_value & 0x800000) *adc_value |= ~0xFFFFFF; // 符号扩展 }

主循环里 100ms 采样一次已经非常奢侈。温度系统的时间常数通常在秒级,100ms 读取足够平滑,也不会让 MCU 一直忙在串口上。

3.4 数据滤波与时间节拍

24 位 ADC 不是拿来就用就稳的。即使加了 Δ-Σ 滤波,现场还是会有随机毛刺,尤其是变频器启动瞬间。我习惯两级处理:先做中值滤波,窗口 3 或 5,干掉单点毛刺;再做滑动平均,窗口 8,压低白噪声。这样一个温度点大约 0.8 秒更新一次,工业控制完全来得及,读数却能稳定到最后一位不乱跳。

时间节拍上,不要让主循环“尽量快”地去读 ADC,最好用系统 tick 定时器卡一个固定周期。因为 DRDY 中断只通知“有数据了”,如果你上一次还没读完、下一次又来了,UART 缓冲会覆盖。加一个“读取忙标志”:ADC_Read_In_Progress 置位时,忽略新的 DRDY,宁可丢一帧也不能错帧。

4. 从原始 ADC 码到温度值

4.1 比例换算和 Callendar-Van Dusen 方程

拿到 24 位 ADC 码后,第一步是换算成 PT100 电阻,第二步是换成温度。比率测量下,换算非常简单:

满量程正码为 FS = 2^23 - 1。如果 ADC 正满量程输出 FS,那么所测输入电压等于参考电压。所以:

其中 G 是 PGA 增益,本方案里 G=8;Rref=1500Ω。代入数据后,比如我读到 ADC 码为 2796202(这个数接近满量程的 1/3),对应电阻:

100Ω。也就是说,0℃ 时电阻读回到 100Ω,说明链路是通的。

第二步是电阻转温度。铂电阻最常用的是 Callendar-Van Dusen 方程,0℃ 以上用二次式:

R(t) = R₀(1 + A·t + B·t²)

PT100 的 R₀=100Ω,标准常数 A=3.9083×10⁻³,B=-5.775×10⁻⁷。反过来求 t 是一个二次方程,直接求根:

D = 1 - R/R₀(注意 R>R₀ 时 D 为负)

t = [-A + √(A² - 4·B·D)] / (2·B)

因为 B 是负数,分子分母相除得到正温度,和实测对得上。负温度区间要加三次方修正项 C=-4.183×10⁻¹²,方程变成三次,用牛顿迭代最省事。给一段可直接用的 C 代码:

float PT100_R_to_T(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; if (R >= R0) { // 0℃ 以上,二次求根 float D = 1.0f - R / R0; float disc = A * A - 4.0f * B * D; if (disc < 0.0f) disc = 0.0f; return (-A + sqrtf(disc)) / (2.0f * B); } else { // 0℃ 以下,牛顿迭代 float t = -10.0f; for (int i = 0; i < 8; i++) { float t2 = t * t; float t3 = t2 * t; float f = R0 * (1.0f + A*t + B*t2 + C*(t - 100.0f)*t3) - R; float df = R0 * (A + 2.0f*B*t + C*(4.0f*t3 - 300.0f*t2)); t -= f / df; } return t; } }

不想写迭代也可以查表插值:把 -50℃ 到 200℃ 每 1℃ 的 PT100 阻值存成数组,折半查找后线性插值,精度 0.05℃ 内足够。查表法的好处是响应快、没有求根复杂度,适合 MCU 资源紧张的场合。

4.2 校准:比率测量也要校

比率测量抵消了电流源漂移和大部分基准误差,但还有三类误差抵消不掉:参考电阻 Rref 的绝对精度误差、PGA 的增益误差和零漂、PCB 焊盘应力导致的微小阻值偏移。

所以出厂前必须做校准。我的做法:准备两个精密电阻箱标准值,一个是 100Ω(对应 0℃),一个是 138.5Ω(对应 100℃)。把传感器线路断开,短路到标准电阻箱,分别记录 ADC 码,然后建立线性校正:

R_corrected = (R_raw - zero_offset) × scale_factor

两点校准后,参考电阻误差和 PGA 增益误差被大幅抵消。注意校准电阻箱要选 0.02% 精度以上的,不然你的“标准”本身就不标准,校准等于白做。

单点校准能不能用?如果你的要求不高,或者担心两点校准改变了线性度,可以只做零点偏移校准:常温下用 0.1% 精密电阻 100Ω 测一组偏移,然后在代码里减去。这样至少把 PGA 零漂消掉,精度可以从 ±0.3℃ 改善到 ±0.1℃ 左右。

4.3 系统精度预算实测

误差来源量级处理方式
参考电阻 Rref 精度±0.1% ≈ ±0.25℃两点校准消除
PGA 增益误差±0.05% 级别两点校准消除,或查手册
ADC INL 非线性几十 ppm 级别无法消除,选型决定
激励电流自热与电流平方成正比500µA 在 100Ω 仅 25µW,可忽略
导线电阻二线制下 0.1~0.5Ω比率测量不显式分离,实为加进 RTD 串阻,尽量缩短引线

在 45SPS、PGA=8、8 次滑动平均的配置下,我实测一块制板放在恒温箱里,24 位码的噪声波动只有 ±2~3 LSB,对应温度约 ±0.02~0.03℃。这个水平已经高于很多商用温度变送器的指标。当然,如果你把采样率跳到 90SPS 以上或者 PGA 调到 32,噪声会明显变大,这是正常规律。

5. 常见问题与排查实录

5.1 第一天就是读不出数据,怎么办

遇到 ADS122U04 完全无响应,按这个顺序排查:

  1. 示波器勾 DIN 引脚,确认发送命令时有 19200bps 的高低电平变化。没有波形就先查 MCU 串口初始化。
  2. 看 DOUT/DRDY 空闲电平。正常空闲应该是高电平,如果一直是低,说明 ADC 没有正确上电,或复位脚被拉住。
  3. 回读寄存器。如果 WREG 之后 RREG 读回来的字节全是 0xFF 或 0x00,大概率是位序问题,检查命令字节的每个位。
  4. 确认 UART 校验位。ADS122U04 要求偶校验而 MCU 默认无校验时,会出现一收一错。

我自己踩过最蠢的坑:把波特率配成 115200 去和 19200 的芯片通信,还怪芯片不工作。后来示波器一量,波形周期完全对不上,两分钟解决。

5.2 数据能读,但是跳得厉害

读数跳动大首先要区分“随机跳”和“周期跳”。随机跳大概率来自电磁干扰或 PGA 增益过高,先看传感器屏蔽层是否单端接地、走线是否离开关电源太近,然后把增益从 32 降到 8 试试。周期跳则重点怀疑工频干扰,检查采样率是不是选在了 90SPS,这个速率恰好躲不开 50Hz。

还有一个隐形杀手是共用电源。MCU 的 3.3V 数字电源和 ADC 的模拟电源共用同一个 LDO,MCU 跑 Flash 读写时电源毛刺直接耦合进 ADC。有条件就分开供电,或者至少在模拟电源引脚加磁珠和 10µF 电容。

5.3 读数准但系统性偏移

如果读数稳定、波动小,但整体偏高或偏低,先检查参考电阻。

参考电阻 Rref 用普通 1% 贴片电阻,温度每变化 20℃,漂移就能造成明显偏移。换 0.1% 25ppm 低温漂电阻能解决大部分问题。还有一点,PCB 上的焊盘和走线也有电阻,如果你的 Rref 是 1500Ω,旁边走线长了可能多出 0.05Ω,按比例影响满量程。用四线开尔文方式把 Rref 两端直接引到 ADC 的 REFP/REFN,能避免走线电阻混入参考电压。

故障现象优先怀疑处理动作
完全读不到数据波特率/校验/位序示波器看波形,回读寄存器
读数乱跳PGA 过高、干扰降增益、查屏蔽、换采样率
读数整体偏移Rref 精度、导线电阻换精密电阻、两点校准
上电后前几帧数据异常ADC 未稳定上电延时 100ms 后再发命令
环境一变读数就漂温度系数、电源检查 Rref 温漂、LDO 负载调整率

刚到手的板子,第一次测试我建议先用精密电阻箱代替传感器,把 ADC 链路单独验证一遍:读到的电阻和电阻箱设定值一致,说明硬件和算法都没问题;如果不一致,问题一定出在接线或配置上,不用急着拿传感器试。这样能节省大量排查时间。

最后说一个小习惯:这系统做完后,我坚持每块板出厂前跑一遍“基准电阻回环测试”,用 100Ω、109.73Ω、138.5Ω 三个标准电阻自动算出零偏和增益系数,写进 Flash。真到了现场,客户拿计量过的温度计比对,大部分板子在 0.1℃ 内一次通过,少部分偏差大的也能立刻看出是安装问题还是传感器问题。测量系统的精度不是单靠芯片堆出来的,从选型、布局、配置到校准,每一环都得刻意处理。这套经验用到其他高精度采集项目上,一样成立。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进