做暖通空调和工业控制的朋友应该都有体会:温度这一路,看着简单,实际上最容易出幺蛾子。压缩机一启动,变频器一干活,控制板上原本稳稳当当的温度采样就开始跳来跳去。我最近给项目里的一套 AHU(空气处理机组)控制系统升级测温链路,主控用的 STM32F334R8,传感器选的是 PT100,模数转换交给了 TI 的 ADS122U04IPWR——一颗 24 位 delta-sigma ADC,而且它跟 MCU 之间走的居然还是 UART 串口。这篇就把这套方案从头到尾拆开讲讲:为什么这么选型、寄存器怎么配、PT100 三线制怎么接、代码里怎么从原始码换算到摄氏度,以及从实验室搬到现场之后,我踩过的那几个坑。适合正在搞工业采集、HVAC 控制板,或者想了解高分辨率 ADC 怎么落地的朋友参考。
1. 为什么偏偏是“UART 口的 24 位 ADC”:选型逻辑先讲透
1.1 温度测量对 ADC 的精度要求,比你想象的高
先说个最基本的账。PT100 的灵敏度大约是 0.385Ω/°C,也就是说温度变化 0.1°C,阻值只变约 0.0385Ω。如果用 500μA 的恒流源去激励,这个阻值变化对应的电压只有:
0.0385Ω × 500μA = 19.25μV
而 MCU 内置 ADC 大多还是 12 位。以 3.3V 参考、满量程 3.3V 计算,一个 LSB 就是:
3.3V / 4096 ≈ 806μV
换句话说,MCU 内置 ADC 一个字的跳动,差不多就是 4°C 的变化。就算把信号放大 100 倍,能分辨的也不过是零点几摄氏度,而且这还是在完全没有噪声的理想情况下。所以正经做 RTD 测温,前端基本都得挂一颗真正的精密 ADC——24 位 delta-sigma,带可编程增益放大器,最好还自带激励电流源,这样传感器驱动和采集一气呵成,不用外挂运放和恒流源。
ADS122U04IPWR 就是这一类芯片。它的后缀 IPWR 对应 TSSOP-16 封装、工业级温度范围,但真正让我盯上它的不是封装,而是那个不太常见的 UART 接口。
1.2 SPI/I2C 方案在工业现场的两个短板
大部分 ADC 的通信接口是 SPI,好一点的有 I2C。但到了工业控制板这种环境,SPI 有几个绕不开的麻烦。
第一是隔离成本。控制板上的 MCU 和现场传感器之间,电气上通常要隔开——隔离电源、数字隔离器这么一套。SPI 至少需要四根信号线:SCLK、MOSI、MISO、CS。每根都过隔离器,通道多、成本高,而且 SPI 是同步时钟协议,隔离器本身的传播延迟会吃掉时序裕量,高速下容易不稳定。I2C 虽然只要两根线,但工业环境里的上拉电阻、电平、速率,折腾起来不比 SPI 少。
第二是调试体验。SPI 和 I2C 都得靠 MCU 主动驱动时序,想用 PC 上的串口助手直接跟芯片对话?基本不可能。UART 就不一样,USB 转 TTL 线一插,串口助手里直接打字发命令就能操作 ADC。对搞过调试的人来说,这种“能用串口工具直接读芯片内部寄存器”的体验,比什么高大上的协议都实在。
ADS122U04 的方案只需要 TX 和 RX 两根数字线,隔离器选双通道的就行。异步串口对时钟沿不敏感,对隔离器延迟的容忍度高很多。这也是它适合工业测温的根本原因,不是功能上比 SPI 强,而是系统集成时省事、可靠、好排查。
1.3 STM32F334R8 在这套方案里的角色
STM32F334R8 是一颗 Cortex-M4F 内核的 MCU,带 FPU 和 DSP 指令,主频最高 72MHz,128KB Flash、16KB SRAM,64 引脚 LQFP。它在项目里的定位不是“跑复杂算法”,而是当控制核心。
F334 最有名的是 HRTIM 高分辨率定时器和内部的模拟比较器、运放,很多人拿它做数字电源和电机控制。放在暖通空调场景里其实也顺:一路温度采样进来,MCU 根据温差算控制量,再去调风机转速、压缩机频率或者电加热器占空比。F334 的 HRTIM 正好擅长干这种 PWM 控制。内置 12 位 ADC 也不是没用,可以拿来快速采样母线电压、NTC 辅助温度之类要求不高的量。真正要求高精度的温度测量,交给外置的 ADS122U04,各司其职。
这里插一句,ADS122U04 的核心模块大致是这几块:
| 模块 | 作用 |
|---|---|
| PGA | 可编程增益放大器,增益 1~128,放大微弱传感器信号 |
| IDAC1 / IDAC2 | 两路匹配恒流源,用于 RTD 激励,10μA 到 1.5mA 可配 |
| 内部基准 | 2.048V 低温漂基准,省掉外部基准芯片 |
| 数字滤波器 | delta-sigma 调制器配合数字滤波,抑制带外噪声 |
| UART 接口 | 点对点串口通信,方便做隔离和调试 |
2. 让 ADS122U04 开口说话:UART 握手、ASCII 命令与寄存器配置
2.1 上电后的自动波特率检测,是这颗芯片最妙的地方
ADS122U04 不用你预先烧写波特率配置,它上电后处于等待握手的状态。主机通过 UART 给它发一段特定格式的同步序列,它根据每个位的高低电平脉宽,自动算出波特率并锁定,然后返回应答信号。之后双方就按这个波特率通信。
这意味着什么?意味着你的 STM32F334 代码里连波特率匹配都不用纠结,只要在初始化时把串口打开,按固定的握手流程走一遍,ADC 自己就乖乖跟上了。这在板级调试时特别舒服:先用 USB 转 TTL 接上芯片,在 PC 串口助手里手动发同步序列,收到应答就说明硬件链路通了。等通信没问题了,再去写 MCU 代码,排错范围一下子缩小很多。
实际操作时注意两点:
- 串口工具的发送端接 ADC 的 RXD,接收端接 ADC 的 TXD,别接反。
- 双方必须共地。用 USB 转 TTL 调试时,USB 线的地和 ADC 板的地要连在一起,否则电平参考漂移,握手很难成功。
2.2 用串口助手直接接管 ADC,调试效率翻倍
这颗片子的通信协议是 ASCII 命令,核心命令就那么几条,例如读数据、写寄存器、读寄存器、进入连续读取模式,以及停止连续读取。具体助记符和应答格式以你手里这版数据手册为准,不同批次可能略有差异,但套路是一致的,基本就是:
- 写寄存器:指定寄存器地址,附上数据字节,ADC 写完后回一个应答。
- 读寄存器:指定地址,ADC 返回寄存器当前值。
- 读转换数据:ADC 返回最近一次转换结果,通常是 24 位数据加上状态字节。
我推荐的调试顺序是:先在串口助手里手动发命令,读一遍寄存器默认值,确认芯片活着;然后按下面的配置思路改寄存器;最后尝试读取转换数据,看数值随温度变化的趋势是否正常。这一套跑通了,再写 STM32 代码,几乎不会遇到协议层面的坑。
有一点要提醒:每次命令的结尾都有规定的结束符,通常是回车类字符。串口助手发送时记得打开“发送新行”选项,否则命令发过去 ADC 不响应,你会误以为是芯片坏了。
2.3 寄存器配置:动手填之前,先想清楚这五件事
ADS122U04 的寄存器不算多,但每个寄存器的位都各管一摊。对照手册配置时,别急着抄网上的默认值,先回答下面五个问题,答案确定了再逐位填:
- 输入通道选哪路,是单端还是差分?PT100 建议选差分输入,把 AIN0 和 AIN1 作为测量对。
- PGA 增益选多少?取决于激励电流和传感器量程,目标是让满量程信号在 ADC 输入范围内尽量摆满,又不削顶。
- IDAC1 和 IDAC2 输出电流选哪挡?要匹配传感器类型,PT100 常用 250μA 或 500μA,PT1000 常用 100μA 或 250μA。
- IDAC 输出路由到哪个引脚?三线制接法里,IDAC1 和 IDAC2 要分别接到 RTD 的两端,这个必须提前规划好。
- 数据速率选多少?默认速率也许能用,但工业现场为了抑制工频干扰,通常要刻意选 20SPS 或 60SPS 这类与 50Hz/60Hz 对齐的档位。
配置顺序也有讲究:先配输入通道和 PGA,再配 IDAC 的电流挡位和输出路由,然后配数据速率,最后启动转换。顺序反了,可能出现电流源先接入错误的引脚、短暂过载的情况。虽然芯片有保护,但养成良好的配置习惯,现场少很多麻烦。
3. PT100/PT1000 接线与 IDAC 配置:三线制到底在抵消什么
3.1 两线、三线、四线制的误差模型
RTD 测温,绕不开导线电阻的问题。传感器的引线通常有几米甚至几十米,铜线电阻按截面积不同,每米从几十毫欧到上百毫欧不等。如果只用两根线,导线电阻会直接串进测量回路,变成系统性误差。
把接线方式分成三种看,误差来源一目了然:
| 接线方式 | 误差来源 | 适用场景 |
|---|---|---|
| 两线制 | IDAC × 2 × 导线电阻,全进测量结果 | 引线极短、精度要求低的场合 |
| 三线制 | IDAC × 两根导线电阻之差 | 工业现场最常用,性价比高 |
| 四线制 | 电流回路和电压测量回路分开,导线电阻的影响可忽略 | 实验室标准和计量级应用 |
两线制的问题最容易理解:激励电流流经 RTD 的同时,也流经两根引线,ADC 量到的电压是 RTD 和两段导线电阻之和。每根引线 1Ω,PT100 在 0°C 时是 100Ω,直接多了 2%,换算成温度就是差不多 5°C 的误差。这肯定不能用。
四线制当然最好,但传感器要多一根引线,现场很多现成的 PT100 探头就是三线制的,改不了。所以工业界最普遍的做法是三线制。
3.2 两个匹配电流源是如何把导线电阻抵消掉的
ADS122U04 正好有两个 IDAC,这个设计几乎就是为三线制 RTD 准备的。接法大致是这样:
把 RTD 的一端接到 AIN0,同时把 IDAC1 的输出也接到 AIN0 这一端;RTD 的另一端接到 AIN1,同时把 IDAC2 的输出也接到 AIN1 这一端。两个 IDAC 配置成相同的电流值,比如都是 500μA。
从电位上看,IDAC1 的电流流经 RTD 和右侧引线回到地,在 AIN0 测量点产生了 RTD 上的压降,也产生了右侧引线上的压降。IDAC2 的电流不流经 RTD,只流经右侧引线,同样在 AIN1 测量点产生一个引线压降。两个测量点做差分,右侧同一根引线的压降被减掉了:
V(AIN0) - V(AIN1) = IDAC × (R_RTD + R_wire) - IDAC × R_wire = IDAC × R_RTD
前提是两个 IDAC 电流匹配,且 RTD 两侧引线电阻接近。ADS122U04 的两个 IDAC 在芯片内部做了匹配设计,这也是选这颗芯片的直接理由之一。
这里有个容易忽略的点:三线制抵消的是“同一根引线”上的压降,而不是你随便接三根线就行。必须严格按照上面这个拓扑来接,让一路电流流经 RTD、另一路电流只流经引线,差分结果才干净。我见过不少产线把三根线随意接到接线端子上,以为是三线制,其实根本没用,误差还是两线制水平。
3.3 自热效应与激励电流的工程取舍
激励电流越大,RTD 上的信号电压越高,越有利于抑制噪声。但电流在传感器上会发热,这就是自热效应。以 500μA 激励 100Ω 的 PT100 为例,功耗是:
I² × R = (500μA)² × 100Ω = 25μW
这点功耗本身不大,但关键看探头封装。如果是带金属套管的工业探头,热量很容易导出去,温升可以忽略。如果是细玻璃封装的小探头,再塞进密闭的保温管道,热量散不掉,长时间下来传感器自身会比环境高零点几度,这个误差就不可忽视了。
我的经验是做 HVAC 管道温度、冷水机组这类常规场景,PT100 用 250μA 或 500μA 都行,优先选 500μA,信号大噪声低。但如果是小封装探头、贴片式测温点或者对绝对温度精度要求高的地方,用 250μA 更稳。
那信号幅度不够怎么办?交给 PGA。500μA 时 RTD 在 0°C 有 50mV 信号,PGA 选 16 倍后约 800mV;换 250μA 只有 25mV,PGA 选 32 倍也能到 800mV。两挡的最终等效输入差不多,但高增益下的噪声性能和带宽会差一些。所以原则是:激励电流优先满足信号强度需求,PGA 做剩下的放大,不要一味压小电流去硬扛 PGA。
4. STM32F334R8 侧的程序骨架:从原始码到摄氏度
4.1 USART 初始化与命令收发:寄存器直写示例
下面的代码以 STM32F334 的 USART1、PA9/PA10 为例,直接操作寄存器,方便你理解初始化顺序。实际项目里用 STM32CubeMX 生成 HAL 代码也没问题,思路一样。
void uart_init(void) { // 使能 GPIOA 和 USART1 时钟 RCC->AHBENR |= RCC_AHBENR_GPIOAEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // PA9 = TX, PA10 = RX, 复用功能 GPIOA->MODER &= ~(0x3UL << 18); GPIOA->MODER |= (0x2UL << 18); GPIOA->MODER &= ~(0x3UL << 20); GPIOA->MODER |= (0x2UL << 20); // USART1_TX 在 PA9 上选 AF7, USART1_RX 在 PA10 上选 AF7 GPIOA->AFR[1] &= ~(0xFUL << 4); GPIOA->AFR[1] |= (0x7UL << 4); GPIOA->AFR[1] &= ~(0xFUL << 8); GPIOA->AFR[1] |= (0x7UL << 8); // 波特率 115200, USART1 时钟 72MHz USART1->BRR = 72000000UL / 115200UL; // 使能 USART、发送、接收 USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; }发送命令就一个阻塞等待的模式:
void uart_send_string(const char *s) { while (*s) { while (!(USART1->ISR & USART_ISR_TXE)); USART1->TDR = *s++; } }实际发送命令时,把要写的寄存器配置串成文本,调用 uart_send_string 发给 ADC。例如先发握手同步序列,再依次发寄存器配置命令。
4.2 采样调度:20SPS 下最简单的方案就够用
ADS122U04 的数据速率设到 20SPS 时,一个转换周期正好 50ms。在这个速率下,用查询方式轮询完全足够,CPU 占用可以忽略。但有一个更好的做法:让 ADC 工作在连续转换模式,MCU 侧用串口的 IDLE 中断配合接收缓冲,收到一帧完整的响应后置一个标志位,主循环或定时器里检测到标志就处理数据。
串口接收中断的一个精简示例:
volatile uint8_t rx_buf[16]; volatile uint16_t rx_len = 0; volatile uint8_t frame_ready = 0; void USART1_IRQHandler(void) { if (USART1->ISR & USART_ISR_RXNE) { uint8_t b = USART1->RDR; if (rx_len < sizeof(rx_buf)) { rx_buf[rx_len++] = b; } else { rx_len = 0; // 溢出保护 } } if (USART1->ISR & USART_ISR_IDLE) { USART1->ICR |= USART_ICR_IDLECF; // 清 IDLE 标志 frame_ready = 1; // 一帧结束 } }这段代码里 IDLE 中断是关键。ADC 返回的 ASCII 帧长度不定,用这个方式就不用预先知道有几个字节,只要收到最后一个字节后总线空闲,一帧就完整了。记得在初始化时打开 IDLE 中断使能位,并在 NVIC 里使能 USART1 中断。
主循环里的处理逻辑大致是:如果 frame_ready 置位,说明 ADC 回了一帧数据,解析出 24 位转换码;然后根据当前的 PGA 增益和 IDAC 电流,换算电阻和温度。注意每次发送命令前,先把 frame_ready 清零,避免误用旧帧。
4.3 从 ADC 原始码到温度值:三步换算
第一步,把 24 位原始码换成差分电压。ADS122U04 的输出编码格式要对照手册确认,如果是双极性补码,满量程对应 2^23,换算就是:
Vin = ADC_Code / 8388608 × Vref / PGA
第二步,用电压除以激励电流得到 RTD 电阻:
R_RTD = Vin / I_IDAC
第三步,用 RTD 电阻反算温度。PT100 的电阻-温度关系不是纯线性的,0°C 以上的关系按 Callendar-Van Dusen 方程描述:
R = 100 × (1 + A×t + B×t²)
其中 A = 3.9083e-3,B = -5.775e-7。0°C 以下还要加一个三次修正项。反算时用牛顿迭代最省事:
#include <math.h> float pt100_res_to_temp(float r_rtd) { const float A = 3.9083e-3f; const float B = -5.775e-7f; const float C = -4.183e-12f; // 先用线性近似做初始值 float t = (r_rtd / 100.0f - 1.0f) / 0.00385f; for (int i = 0; i < 8; i++) { float r_calc, drdt; if (t >= 0.0f) { r_calc = 100.0f * (1.0f + A*t + B*t*t); drdt = 100.0f * (A + 2.0f*B*t); } else { r_calc = 100.0f * (1.0f + A*t + B*t*t + C*(t - 100.0f)*t*t*t); drdt = 100.0f * (A + 2.0f*B*t + C*(4.0f*t*t*t - 300.0f*t*t)); } t -= (r_calc - r_rtd) / drdt; } return t; }这个函数覆盖 -200°C 到 850°C 的完整范围,对 HVAC 场景绰绰有余。实际应用里,提前算好一个 -50°C 到 200°C 的 RTD 电阻查值表,运行时线性插值,速度更快;迭代法更省内存,两者都是成熟做法。
换算完之后,强烈建议做一次合理性判断。比如 HVAC 正常测温范围设定为 -40°C 到 150°C,对应 PT100 电阻约 84.3Ω 到 157.1Ω。超出这个范围,大概率是传感器断线或者 ADC 输入过载,直接置错误标志,不要让异常数值进控制回路。
4.4 软件滤波:在转换结果的“合理”基础上再加一道
ADS122U04 的 delta-sigma 调制器本身有数字滤波,到了 20SPS 的低速档,输出已经相当干净。但工业现场的偶发干扰,比如变频器突然启停、接触器吸合,仍然可能打出一两个异常点。软件上做一层简单的滑动平均或者中值滤波,成本很低,收益明显。
HVAC 温度变化本身很慢,热惯性以秒甚至分钟计,滑动窗口取 8 到 16 次采样,也就是 0.4 到 0.8 秒的延迟,完全不影响控制效果。具体做法:维护一个固定长度的环形缓冲,每来一个新值,去掉一个最旧值,求平均。再做一次中值过滤,专门剔除那种“突然跳到 200°C 又跳回来”的尖峰。两层下来,控制回路拿到的温度曲线会平滑得多,PID 输出也不会跟着 ADC 的毛刺来回抖。
5. 从实验室到现场:噪声、校准与那些“看似正常但不对”的坑
5.1 工频干扰、RC 滤波与数据速率的选择
国内工业现场 50Hz 工频无处不在,电源线、电机、照明回路全是干扰源。对付这类干扰,最有效的手段不是靠后级软件滤,而是在 ADC 的转换参数上做文章。
delta-sigma ADC 的数字滤波器有一个特点:在某些数据速率下,对工频及其整数倍频率有天然的陷波特性。20SPS 的积分时间是 50ms,正好是 50Hz 的整数倍,对工频有很强的抑制作用。60SPS 则对应 60Hz 地区。如果你的设备可能跨区域使用,选 60SPS 能兼顾 50Hz 和 60Hz 的衰减,只是噪声会略大一点。
另外,在 AIN 引脚前面加一个简单的 RC 低通,截止频率放低一些,比如几十 Hz 到几百 Hz,能滤掉更高频的干扰。这里有个注意点:RC 的电阻不能太大,太大跟 ADC 输入端的采样电容配合会产生建立时间误差,直接影响精度。常规做法是电阻取几百欧到一两千欧,电容取几 nF 到几十 nF,具体值看手册里的输入驱动要求。RC 的位置要尽可能靠近 ADC 引脚,远了他没意义。
5.2 零点校准和增益校准:两点法实操
芯片本身的绝对精度已经不错,但工程上要做到“读数可信”,最好还是做一次系统级校准。因为误差不只来自 ADC,还来自 PCB 走线、接线端子、导线电阻、探头本身的出厂偏差。
校准方法很简单,两点法:
- 取一个 100Ω、精度 0.01% 的精密电阻,替换掉 PT100 接到 ADC 输入端,测出此时的 ADC 换算电阻值,记作 R1。理论上应该是 100.00Ω。
- 取一个 138.5Ω 的精密电阻,对应 PT100 在 100°C 时的阻值,测出实际读数 R2。
- 计算增益系数和偏移量:
gain = (138.5 - 100.0) / (R2 - R1) offset = 100.0 - R1 × gain
之后每次测量的电阻,都先做一次线性修正:
R_corrected = R_measured × gain + offset
这样校准完,整个信号链路的误差都能压下来。现场没有精密电阻的话,就用冰水混合物做 0°C 点,沸水做 100°C 点,但要注意沸点随大气压变化,海拔高的地方实际沸点不到 100°C,要查表修正。
校准数据建议在设备出厂前存到 MCU 的 Flash 里,现场不要频繁动不动就校准,传感器老化后再定期复核。另外,校准前先把设备通电运行半小时以上,让板子热稳定了再操作,否则温漂会混进校准结果里。
5.3 PCB 布局与线缆屏蔽的几条硬规矩
这类高分辨率 ADC 的性能上限,基本由外围电路决定。芯片本身的指标再漂亮,PCB 上一塌糊涂,出来的数据照样没法看。
我的几条经验,按重要性排:
- 模拟电源和数字电源分开走,AVDD 侧用 LDO 单独供电,避免 MCU 的数字噪声通过电源串进 ADC。
- VREF 引脚的旁路电容一定要就近放置,走线短而粗,这是参考电压稳定性的关键。
- AIN 差分信号对走线尽量贴近、等长,减少串模干扰引入。
- ADC 芯片正下方不要走大电流或者开关节点,尤其不要走 HRTIM 的 PWM 输出线。
- 传感器线缆用屏蔽线,屏蔽层单端接地,不要两端都接,否则会形成地环路。
- 如果传感器线缆要走比较远的路径,接头处做好防水防氧化处理,工业现场端子氧化是头号隐形杀手。
5.4 现场排查实录:温度稳定偏低 0.8°C 的根因
最后分享一个真实案例,是我在现场排查过的。一套 AHU 机组,六个测温通道,校准时每个通道都对得上标准表。结果投入使用两周后,有一个通道开始缓慢漂移,最后稳定在比参考温度低 0.8°C 左右,不跳变,就是稳定地偏低。
一开始我怀疑是探头本身漂移,换了新探头,问题依旧。然后怀疑 IDAC 配置有问题,重新读寄存器,配置没问题。最后用万用表去量接线端子排上三根线的电压降,发现第三根线的接触电阻在几十毫欧到几百毫欧之间波动,随着机组振动时不时变化。三线制接法里,第三根线承担着抵消导线电阻的作用,它一旦接触不良,补偿就失效,误差等同于两线制的一部分,导线电阻直接进了测量结果。0.8°C 这个量级,和那根线上二三百毫欧的接触电阻完全对得上。
处理办法:重新压接端子,打了螺纹胶固定,之后把这根线的压降列为巡检项。同时在软件里加了一把锁——如果 ADC 读数对应的电阻值超出了正常量程,直接判定为断线或接触异常,控制回路切到安全状态,而不是傻乎乎地拿错误温度去控压缩机。
这类问题最坑的地方在于,数据不跳变、不报错,单单数值不对,很容易让人怀疑是传感器漂移,白白换件。实际上工业现场超过一半的“传感器不准”,根子都在接线、端子、屏蔽层这种不起眼的地方。排查时养成“先量端子上每根线的压降,再怀疑传感器本身”的习惯,能省很多冤枉时间。
我自己做下来的体会是,ADS122U04 加 STM32F334R8 这套组合,性能上完全够用,真正的功夫全花在选型、接法和现场排查上。高分辨率 ADC 不是接上就能出好数据的,PGA、IDAC、数据速率、PCB 布局、校准,每一环都是精度的一部分。把这套链路理顺了,后续不管是做恒温恒湿控制、冷热量计量还是设备防冻保护,温度这一路的数据质量都能稳稳拿住。