两年前我接过一个制冷机组控制板的项目,主控是 STM32F334R8,要负责压缩机变频、风机调速,还要同时采集十几路温度信号,探头都埋在冷凝器和蒸发器管道上,离主板少说半米,远的接近两米。当时最头疼的还不是驱动部分,反而是温度采集这一路:模拟信号弱、距离长、现场还有变频器这种大噪声源。后来方案定下来用 ADS122U04IPWR 这颗带 UART 接口的 24 位 ADC,配合 STM32F334R8 的串口,整套温度采集链路才算真正跑顺。
这篇文章就把这套方案的完整设计过程写出来,从选型逻辑、传感器接法、寄存器配置、固件状态机,到实际调试和校准,全部按我落地的顺序来。如果你也在做工业或者 HVAC 相关的温度采集,尤其是传感器和主控板必须分开、走线距离又长的场景,这个方案很值得参考。
1. 选型逻辑:为什么在一堆 ADC 里挑中 UART 接口的 ADS122U04
1.1 传感器和主控之间的物理距离,直接把 SPI 方案排除了
做工业温度采集,第一件要面对的事不是精度,而是信号怎么传。以前用 ADS1220 这类 SPI 接口 ADC,传感器板靠近探头,主控板通过标准 SPI 去读。SPI 这种同步接口对时钟边沿敏感,PCB 上走个十几厘米问题不大,但一旦中间有接插件、线束、端子排,时钟和数据线稍微引入一点串扰和延迟,通信就开始不稳定。为了把采集板拉远,有人加电平转换、加驱动器、降低 SPI 时钟,最终方案复杂程度远超预期。
ADS122U04 直接把接口换成了 UART,这是个非常现实的工程决策。UART 是异步通信,对时钟同步的要求低很多,两三个毫秒的波特率偏差都能正常工作,普通单片机引脚电平直接走 0.5 米到 2 米的线缆基本没问题。再加上 UART 本身就内置奇偶校验,比 SPI 裸传数据要健壮得多。我最后把 ADC 采集板放在靠近探头的保温层外侧,主控和采集板之间只用四根线:电源、地、发送、接收。布线难度比 SPI 方案低了一个量级。
1.2 ADS122U04 这颗芯片肚子里装了什么
ADS122U04 不是一颗普通 ADC,它把传感器测量需要的模拟前端配套全部集成进去了,包括:
- 24 位 Δ-Σ ADC 核心,内置数字滤波器
- 4 路差分输入,也可以配置成 8 路单端输入
- 可编程增益放大器 PGA,增益 1 到 128 倍
- 内部基准电压源,2.048V
- 两个可编程激励电流源 IDAC,给 RTD 供电
- 内部温度传感器,用来做热电偶冷端补偿
- 通用 UART 接口,支持多地址寻址
这颗芯片的定位很明确,就是给工业温度传感器做前端。你用一块小板放探头旁,整个模拟链路全在芯片里,外部只需要几个滤波电容和 TVS 管。传感器信号在这个小板上完成放大、采样、数字化,最后通过 UART 变成数字字送给远处的 MCU。模拟信号根本没有机会走远路,远路走的是已经是数字信号了,抗干扰能力完全不一样。
1.3 STM32F334R8 在整套方案里的分工
STM32F334R8 是 Cortex-M4F 内核,带浮点运算单元,主频最高 72MHz,对于温度采集这种低频任务绰绰有余。真正让这颗芯片出彩的是它的高分辨率定时器 HRTIM 和片上运放/比较器,可以直接用来做压缩机的变频驱动、PFC 控制和电流环。
所以整套系统的分工非常清晰:F334R8 负责核心控制逻辑,包括压缩机电机控制、风机调速、温度 PID 调节、除霜逻辑;温度采集完全交给 ADS122U04 的独立小板上处理,F334 只通过 UART 周期性地获取温度数据。这样模块化设计的最大好处是,模拟采集部分和数字控制部分可以分开调试,采集板的精度问题不会污染主控逻辑,主控死机或者看门狗复位也不会影响采集板工作。
1.4 一个容易被忽略的隐藏优势:多从机扩展
ADS122U04 支持 UART 地址位寻址。它有一条 ADDR 引脚,可以通过外部电阻配置设备地址,一个 MCU 串口上最多能挂多个采集板。我后来做一台大型机组,需要采集 24 路温度,直接把 4 块 ADS122U04 小板挂在同一条 UART 总线上,按照地址轮流访问。要是用传统 SPI ADC,至少得多占用一组 CS 引脚,并且总线长度限制更苛刻。这个特性对 HVAC 这种传感器数量多、布局分散的场景非常实用。
2. 模拟前端设计:PT100 和热电偶的接法细节
2.1 PT100/PT1000 的三线制接法,引线电阻怎么被消掉
先说 RTD 铂电阻,这是 HVAC 用得最多的温度传感器。PT100 在 0°C 时电阻 100Ω,PT1000 是 1000Ω,两者阻值都会随温度升高近似线性增长。RTD 本身是无源器件,必须给它一个激励电流,让电阻变成电压,ADC 才能测量。这里就引出第一个工程问题:RTD 和采集板之间的连接导线本身也有电阻,如果激励电流流过这根导线,导线上产生的压降会直接叠加到 ADC 的测量结果里,这误差不可接受。
解决办法就是三线制接法。原理图可以把两根导线想象成去和回的路:激励电流 I 从采集板出发,沿一根导线流到探头,流过 RTD,再从另一根导线回到采集板,两根导线流过完全相同的电流。如果两根导线长度一致、材质一致,它们的电阻也一致,于是两根导线上产生的压降 VL 和 VL 完全相同。ADC 的全部差分输入接在 RTD 两端,测量的是 VR = I×R_RTD,而两根导线上的压降在差分测量中一正一负相互抵消。
ADS122U04 有两个可编程电流源 IDAC1 和 IDAC2,把两个 IDAC 配置成相同电流值,让你可以做到真正消除误差的三线接法。我实际项目里 PT1000 的方案是:IDAC 输出电流 500µA,PT1000 在 0°C 时产生 0.5V,在 200°C 时约 0.88V,PGA 增益设为 1,满量程留足余量。而 PT100 因为阻值低,一般电流可以给到 1mA,以提高信号电压,但也别太大,电流太大会让传感器自身发热,产生自热误差。
2.2 外部基准电阻让测量变成比率式,激励电流漂移自动抵消
这里有个进阶设计,绝大多数第一次做的人都会忽略。如果直接用内部 2.048V 基准电压,则激励电流源的微小漂移会直接变成测量误差,因为输出电压是 I×R,电流变了电压就变。更稳的做法是外部加一颗精密基准电阻 RREF,把 ADC 的基准输入接到这颗电阻两端,让 I 同时流过 RREF。
测量结果变成:code ≈ (I×R_RTD) / (I×RREF) = R_RTD / RREF。你看,电流 I 在分子分母同时出现,被约掉了。激励电流是跟随温度漂了还是随时间衰减了,都无所谓,因为基准电压和传感器电压同比例变化,数字输出比值不变。这就是比率式测量,RTD 领域的老传统,但不少人第一次看到这套 ADC 时会忽略基准输入引脚的作用。我们项目里 RREF 用的是 50ppm/°C 以内的金属箔电阻,整机温漂表现明显比内部基准稳定。
2.3 热电偶输入:mV 级信号要瞻前顾后
热电偶和 RTD 是两种完全不同的测量方式。RTD 是你给它供电然后测电阻,热电偶是自己产生电压,K 型热电偶灵敏度大约 41µV/°C,几百度的温差也只有几十毫伏。这种信号必须经过 PGA 放大。
ADS122U04 的 PGA 增益调到 64 时,满量程大约是 2.048V/64 = 32mV,对应 K 型热电偶接近 780°C;调到 128 时满量程 16mV,接近 390°C。HVAC 里测排烟温度、压缩机排气温度,K 型热电偶用量程 0 到 400°C 比较常见,我会把增益选在 64 或者 32,避免满量程吃得太死。
热电偶是直流信号,但它的静态工作点必须抬到 ADC 输入量程的中间,否则差分输入一端超过共模电压范围就会饱和。最简单的做法是在热电偶正负输入端各接一个大阻值电阻到电源电压的一半,比如两个 1MΩ 电阻串联做分压,把中点 1.65V 分别通过 1MΩ 接到热电偶两端。这样共模电压稳稳落在 1.65V 附近,差分信号不受影响,偏置电阻引入的误差在微伏级,对 0.1°C 精度来说可以忽略。
2.4 冷端补偿到底怎么做,ADS122U04 内部温度传感器用在哪
热电偶测量有个绕不开的点,叫冷端补偿。热电偶的输出电压是热端和冷端的温差函数,我们想知道热端绝对温度,就必须知道冷端温度,然后在软件里做补偿。
常见的设计错误是把 ADC 芯片放在主板角落,然后用内部温度传感器去测冷端。内部温度传感器测的是 ADS122U04 的结温,结温反映的是 PCB 局部环境温度,如果你的热电偶接线端子离芯片很远,热电阻完全不同,测出来当然不准。正确做法是让 ADC 芯片尽可能靠近热电偶的接线端子,让芯片结温和接线端子温度尽量接近,这样用内部温度传感器做补偿才有物理意义。
我实际测试过,把采集板放在保温测量盒里,内部温度传感器读到的长期稳定性和一台高精度外部测温仪差异在 0.3°C 以内,完全可以接受。如果对精度要求更高,可以外接一个 PT1000 粘贴在冷端端子上,再接一路模拟输入,软件里用外部 RTD 的温度做补偿,只是要多占一路通道。
3. UART 链路和寄存器配置:怎么把 ADS122U04 驱动起来
3.1 先理解 UART 帧格式:8 位还是 9 位
ADS122U04 的 UART 接口和普通单片机 UART 不完全一样,它的每一帧默认是 9 位有效数据,由 8 位数据加 1 位校验/地址位组成。这个接口既支持普通 8 位 + 1 位校验的 UART 模式,也支持带地址位模式,用来区分总线上的多从机。
STM32F334R8 的 USART 可以配置成 8 位数据加奇偶校验,也可以用 9 位数据模式自己解析地址位。我建议如果是单采集板场景,直接用 STM32 标准的 8 位数据 + 偶校验 + 1 停止位,运行在 115200 波特率,稳定又省事。多从机场景则改用 9 位模式,把第 9 位当作地址标志位。
波特率要注意一个细节:ADS122U04 需要 MCU 的波特率误差控制在 2% 以内,内部振荡器起振后如果晶振没有校准会产生漂移。我用的 STM32F334 是外部 8MHz 晶振,配合 PLL 生成串口时钟,波特率误差在 0.2% 左右,完全没问题。如果你直接用内部 HSI,一定要先测量实际波特率再和 ADC 匹配。
3.2 命令字是什么:START、RDATA、RREG、WREG 这条主线
ADS122U04 的寄存器读写和转换控制,全部通过 UART 命令完成。核心命令大致如下,具体字节定义一定要以实际芯片手册为准,但整体思路是通用的:
| 命令 | 功能 | 备注 |
|---|---|---|
| RESET | 复位芯片 | 上电后可发送一次确保状态 |
| START | 启动一次转换 | 单次转换模式每轮都要发 |
| RDATA | 读取转换结果 | 返回 24 位数据和状态字节 |
| RREG | 读取配置寄存器 | 调试时用来回读验证 |
| WREG | 写入配置寄存器 | 分寄存器写入,每个 8 位 |
我的惯例做法是:上电后发 RESET,等 1ms,然后连续写 4 个寄存器,再把寄存器读回来对比,确认写入成功才开始正常工作。这个"写入后回读"的习惯帮我挡掉过好几次新旧固件寄存器定义不一致带来的坑。
3.3 四组寄存器配置的思路,不背位定义也能写对
ADS122U04 的基地址一共有 4 个配置寄存器,功能划分非常清晰:
- Config0:选输入通道和 PGA 增益,同时选择基准电压来源
- Config1:选数据速率和工作模式
- Config2:设置 IDAC 电流大小和电流源 1 的输出引脚
- Config3:设置内部温度传感器使能、电流源 2 的输出引脚和比较器
每一组的位定义查手册或者参考头文件映射,但设计思路上我可以直接给一套典型参数,全部围绕 PT1000 三线制:
| 参数 | 我的取值 | 原因 |
|---|---|---|
| 输入通道 | 差分 AIN1/AIN2 | 两引脚接 RTD 两端 |
| PGA 增益 | 1 | PT1000 电压高,不需要放大 |
| 基准 | 外部 RREF(比率式)或者内部 2.048V | 推荐外部 RREF |
| IDAC 电流 | 500µA | 兼顾信号电压和自热 |
| 数据速率 | 20 SPS | 工频 50Hz 抑制效果好 |
| 转换模式 | 单次转换 | 配合 MCU 定时触发 |
热电偶的配置则把 PGA 调到 64 或 128,IDAC 关掉,使能内部温度传感器用于冷端补偿,数据速率同样选低档位以抑制噪声。
3.4 连续转换还是单次转换,这个选择不简单
ADS122U04 支持连续转换模式和单次转换模式。很多人图省事直接用连续模式,让 ADC 自己不停转换,MCU 要的时候就发 RDATA 读数据。这个方案在安静环境里没问题,但存在一个隐患:由于 UART 没有硬件 DRDY 引脚,如果你发 RDATA 的时机恰好和 ADC 内部数据更新错位,读出来可能是一帧旧数据或者正在切换的中间数据,需要自己维护状态判断。
我选择单次转换模式的理由很简单:用一个定时器以固定周期触发 START 命令,等转换完成再去读。这样每一次读取的数据必然对应一次完整转换,数据的新鲜度和一致性都是可控的。代价是每轮转换之间有一定间隔,但对于温度这种大惯性物理量,每秒采样 10 次已经非常过量了。实际我用 20SPS 数据速率,每 200ms 发一次 START,留了 50ms 的转换时间余量。
4. STM32F334R8 固件实现:状态机、DMA 和环形缓冲
4.1 串口初始化:从标准 HAL 到 9 位模式的切换
STM32F334R8 的 USART 初始化用 LL 库或者 HAL 都可以。如果是单从机、8 位数据 + 偶校验,代码最关键的是把字长和校验位配对设置好:
LL_USART_InitTypeDef USART_InitStruct = {0}; USART_InitStruct.BaudRate = 115200; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; USART_InitStruct.Parity = LL_USART_PARITY_EVEN; USART_InitStruct.StopBits = LL_USART_STOPBITS_1; USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;注意一个细节:开启偶校验后,STM32 的数据帧实际占用 9 位空间,第 9 位由硬件自动填充校验值。对 ADC 来说,它发送的数据帧同样带校验位,MCU 硬件会自动检查,如果奇偶校验失败会产生帧错误标志。在接收中断里一定要判断LL_USART_ReadReg(USARTx, ISR)的 FE 位和 PE 位,否则一帧错误数据可能被误当成有效数据。
4.2 主循环里的 ADC 访问状态机
固件如果写成阻塞式读串口,现场稍微有点噪声,一个校验错误就能把整个控制环路卡死几毫秒。我在这里用的是一套简单的状态机,由定时器驱动周期轮转:
typedef enum { ADC_IDLE, ADC_START_SEND, ADC_WAIT_CONV, ADC_READ_SEND, ADC_WAIT_RESP, ADC_PARSE_DATA } AdcState_t;主循环只做一件事:检查当前状态,决定下一步动作。200ms 定时到,状态从 IDLE 切到 START_SEND,通过 DMA 发送一个字节的 START 命令;发送完成切到 WAIT_CONV,打开一个 50ms 超时定时器;超时到了切到 READ_SEND,发 RDATA 命令;然后进入 WAIT_RESP,等待接收中断把 4 个字节(24位数据 + 状态)填进环形缓冲。数据收齐了,自动进入 PARSE_DATA 处理温度换算。
这个状态机的关键价值在于,任何一步出错,比如超时没收到响应、校验失败、数据溢出,都会回到 IDLE 并标记一次通信错误,而不是卡死在等待循环里。主控制环路完全不被阻塞。
4.3 数据帧怎么保证完整:帧头和超时重同步
ADS122U04 在 UART 层面有奇偶校验,但我要的是更高层的帧完整性。我的协议在每轮读取之后做一个简单校验:把收到的 24 位数据里的符号位和上下限范围做一次合理性检查,比如 -70°C 到 250°C 之外直接判定异常。发现异常后,强制切回 IDLE,等待下一轮定时再启动,连续三次异常就用备用温度值替代,并点亮状态灯。
对于多从机总线,我还会在命令前面加一个从机地址字节,把命令封装成"地址 + 命令 + 可选数据"三段式。这样即便某一次总线活动异常,从机也能根据地址字节判断是否是发给自己的数据,不会因为半帧残留导致后续通信全部错位。
5. 数据换算与标定流程:从原始码到摄氏度
5.1 原始码先换算电压,别急着套温度
ADS122U04 的输出是有符号二进制补码,24 位。假设你已经配置了 PGA 增益 G 和基准电压 VREF,那么原始码对应的电压值计算公式是:
V = code × VREF / (G × 2^23)
注意这里用的是 2^23 而不是 2^24 - 1,因为二进制补码的一半量程对应满量程电压。举个实际例子:VREF=2.048V,G=1,则一个 LSB 对应约 0.244µV。换句话说,ADC 后端的量化噪声远小于传感器信号的实际波动,这个精度对温度测量来说绰绰有余,所以后面真正的误差源是基准漂移、PGA 失调和引线误差。
5.2 RTD 电阻换算和 Callendar-Van Dusen 方程
得到电压后,如果用的是内部基准配置,电阻 V/I 换算:R = V / I_dac。我项目用的 500µA 激励,0°C 时 PT1000 电压就是 0.5V,200°C 时约 0.88V,换算毫无压力。如果是比率式配置,直接拿 code 和 RREF 做比值:R = code / (2^23) × RREF。
电阻到温度不能直接拿 R0×(1+αT) 去算。PT100/PT1000 在 0°C 以上有二次项,也就是 Callendar-Van Dusen 方程:
R(t) = R0 × (1 + A×t + B×t²)
其中 A=3.9083e-3,B=-5.775e-7,R0 是 0°C 电阻值。这个公式在 0°C 到 850°C 的范围内能保持 ±0.01°C 级别的拟合精度。温度可以从二次方程求根得到,或者干脆在固件里放一张 0.1°C 间隔的查找表,插值查表速度快且无需浮点运算,工业仪表常见做法。F334R8 有 FPU,我直接用二次求根公式算,代码简单,每 200ms 算一次对 CPU 占用忽略不计。
5.3 热电偶查表和冷端补偿
热电偶的电压到温度换算没有简单解析式,标准做法是 ITS-90 分度表。嵌入式里通常做成两张表:一张 mV-温度正查表,一张温度-mV 反查表。冷端补偿的流程是:
- 读内部温度传感器,得到冷端温度 Tc
- 用反查表把 Tc 换算成冷端热电动势 Ec
- 用实测热电偶电压 E 加上 Ec,得到补偿后的总电动势 Etotal
- 用正查表把 Etotal 换算成热端温度 T
K 型热电偶在 0 到 400°C 区间几乎是线性的,约 40µV/°C 多一点,如果不想做复杂表格,用一个二次多项式拟合同样能到 ±0.2°C 的精度。我建议固件里表格只放 0 到 500°C 范围,每 1°C 一个点,共 500 个点,Flash 占用很小。
5.4 校准流程:单点飘移和两点增益
即使芯片再准,系统级误差还是会来自偏置电阻、滤波电容、端子接触。所以每一块采集板出厂前必须做校准。
我用一台 Fluke 的电阻模拟器当 RTD 标准源,或者用精密电阻箱,进行两点校准:
| 校准点 | 标准阻值 | 实测原始码 | 计算电阻 |
|---|---|---|---|
| 0°C 附近 | 1000.00Ω | xxxx | 1001.2Ω |
| 200°C 附近 | 1758.40Ω | yyyy | 1760.1Ω |
拿到两组 (Rstd, Rcalc),拟合一条直线 y = kx + b,把 k 和 b 存进 STM32 内部 Flash 的备份寄存器。运行时对所有原始电阻进行补偿后再套温度公式。实际验证下来,单温度点校准能把绝对误差压到 0.2°C 以内,两点校准能压到 0.05°C 左右。
6. 干扰抑制和长期稳定:把精度保持在一台运行一年后
6.1 电源和地:模块化测量的生命线
ADS122U04 对电源噪声非常敏感,虽然内部有 PSRR,但你给它一个满是变频驱动噪声的 3.3V,再好的 ADC 也无能为力。我把采集板的供电单独走一路,用一颗低压差 LDO 从系统 24V 降压,LDO 输出端放 10µF 电解和 100nF 陶瓷电容组合。采集板的地和主控板的地只在主板上一点相连,形成星型接地,避免逆变器大电流回流在地线阻抗上产生压降扰动 ADC。
输入端的滤波电容位置也讲究。RTD 信号线进入 ADC 之前,每根线串联 1kΩ 电阻,然后在 ADC 引脚附近放 100nF 差分电容,组成低通,带宽约 1.6kHz,对高频干扰有一定衰减。加这个 RC 会引入微小增益误差,但因为它在校准时已经在范围内,所以不影响最终精度。
6.2 现场验证:变频器满载启停时该读出的温度纹丝不动
这套系统装到现场后,我做了专门的抗干扰验证。压缩机满载启停时,变频器母线电流瞬间几十安培,温度采集板就装在距离变频模块 30cm 的钣金支架上。用示波器测量模拟输入端的瞬时扰动,干扰幅度确实有十几毫伏的尖峰,但到了 ADC 输入引脚,经过 RC 滤波后只剩少量残留,而 24 位 ADC 配合 20SPS 数字滤波具备很强的工频抑制,最终固件里温度的读数纹波稳定在 ±0.05°C 以内。
真正要提防的是共模干扰。我曾经把采集板的电源地和主控板的地直接连成一根长线,结果数据读出来跳了几个 LSB。后来改成分段接地,信号地单独走,干扰立即消失。这类问题经常只出现在现场,实验室里很难复现,所以设计时就要把地的层次规划好。
6.3 固件侧的最后一道防线:看门狗和错误恢复
ADS122U04 正常工作时非常省心,最怕的反而是一根线松了、一个端子氧化了。我在采集板固件里加了三层保护:底层是 UART 字符超时判断,任何一帧超过 5ms 没收到就丢弃;中间层是温度合理性检查,超范围值直接拒收;上层是看门狗,但看门狗不是简单 1 秒清零,而是等每一轮 ADC 状态机完整跑完才喂。如果 1 秒内状态机没有成功完成一轮,就说明通信链路出问题,这时候只做复位,不复位传感器板,让主控进入安全温度模式。
这套三级策略在实际运行里帮了大忙。有一次现场端子进水,接触电阻突变,主控收到连续三帧异常数据后直接切到默认温度值启动保护,而不是用错误温度去算 PID,避免了压缩机在一个虚假高温下误停机。
6.4 长期运行验证:一个月不漂的精读表现
我最关心的指标其实是长期稳定性。设备在 40°C 恒温间里运行了一个月,每隔一小时记录一次温度读数。整机漂移曲线非常平缓,最大偏差在 ±0.08°C 以内。这个结果里,外部比率基准贡献了大部分稳定性,内部基准的方案在同一测试条件下大约会漂到 ±0.3°C。所以预算允许,RREF 一定要上,这是整套系统精度的天花板。
另外一个小技巧,长期运行时如果担心传感器自身老化,可以在固件里跑一个每日零点漂移监测:每天固定时间用内置测试电阻或者短路输入做一次自检,把偏移量自动补偿掉。这个功能让我后面维护省了不少事。
6.5 最后一个小细节:端子材料选择
温度测量链路里最容易被忽略的其实是接线端子。热电偶端子如果用的是镀锌铜端子,铜和热电偶金属之间会产生微小热电动势,在冷端附近叠加一个额外偏移。我全部换成了专用热电偶补偿端子或者至少用镀金端子,表面氧化也少,经过高温高湿环境测试后端子接触电阻变化明显小于普通端子。
实测下来,这个改动带来的冷端补偿误差改善比想象中大,长期运行温度读数稳定性直接升了一个台阶。对于追求 0.1°C 级别的工业仪表,这个细节值得留意。