1. 项目选型:为什么是 ADS122U04 + STM32F411RE 这对组合
做工业温度测量的人,迟早会碰到一个尴尬的环节:传感器信号已经调理得干干净净,结果 ADC 和 MCU 之间通信掉链子,或者 ADC 分辨率够,但接口协议吃掉了太多 MCU 资源。
我这两年做 HVAC 控制器和工业现场温度采集模块,试过不少方案。ADS122U04IPWR 这颗芯片第一次出现在我面前时,我第一反应是"怎么又是 24 位 Σ-Δ ADC",但再一看,它的通信接口不是传统 SPI,而是UART。这让我停下来多看了两眼。再配上手头常用的 STM32F411RE,两者组合的意义就很有意思了。
1.1 ADS122U04 到底是什么来路
ADS122U04 是德州仪器推出的 24 位 Δ-Σ 型 ADC,四通道,带可编程增益放大器(PGA),内置 IDAC 电流源,还带内部温度传感器。这些配置单拿出来不稀奇,ADS1220、ADS1232 等同类芯片已经有类似规格。但 ADS122U04 的特别之处在于,它的主机通信接口走的是 UART,而不是 SPI 或 I2C。
这颗芯片的具体调制方案是:主控制器通过 UART 接口,以双向异步通信的方式与寄存器交互、发起转换、读取数据。这在当时市面上不多见。为什么这么说?做过多路温度采集的老工程师应该都有感受:SPI 接口虽然速度快,但布线一多、干扰一大,时序就容易出问题,尤其当 MCU 引脚不够用、还得软件模拟 SPI 时,整个项目的稳定性和代码复杂度都是噩梦。UART 则完全不同,几乎所有单片机都有硬件 UART,你不需要额外的 GPIO 去模拟时序,直接用一根 TX、一根 RX就能建立通信。
ADS122U04 的数据输出速率支持 5 SPS 到 1 kSPS,内部可编程增益最高 128 倍,对于 PT100、PT1000、热电偶这类工业温度传感器来说,都处于非常合适的量程范围。加上它在低噪声模式下能达到 24 位分辨率,做高精度温度采集是够用的。
1.2 STM32F411RE 在这里扮演什么角色
STM32F411RE 是一颗基于 ARM Cortex-M4 内核、带 FPU、主频最高100 MHz 的 MCU。它最让我满意的地方:一是价格适中,供货稳定,非常适合中小批量工业产品;二是外设资源丰富,有多个 USART 和 SPI;三是功耗控制灵活,工业现场需要长期在线监测时,可以很方便地做低功耗策略。
更重要的是,F411 的 USART 硬件支持直接对接 ADS122U04 的 UART 接口。这种"MCU 硬件 UART 对 ADC 硬件 UART"的通信方式,不需要搞什么位带操作、软件模拟时序,代码写起来清爽,线上的鲁棒性也有保证。再加上 F411 的 DMA 配合串口接收不占用 CPU 时间,可以腾出大量算力去处理 PID 控制、人机交互、通信协议栈等其他任务。
我之前在一个 HVAC 控制器项目里,就是用 F411 同时管理 4 路 ADS122U04,每路采集 1 路 PT1000 和 1 路 NTC 备用。因为每颗 ADC 占用一路 UART,加上 DMA,系统压力很低,CPU 还能留着一半以上算力去跑 Modbus 协议和液晶显示。这一点换了 SPI 接口芯片几乎做不到这么省心。
1.3 这个方案解决的是哪些真实场景
工业现场和 HVAC 领域的温度测量,普遍存在三个共同痛点:
- 传感器种类杂。现场既有 PT100 铂电阻,也有热电偶,甚至还有一些需要电流激励的半导体温度传感器,一个项目里往往要混着接。ADS122U04 的 PGA 和 IDAC 可以分别适配不同传感器,不需要在外围堆太多调理电路。
- 信号经过长线传输,干扰大。HVAC 设备内部电机启停频繁,工业现场就更不用说了,变频器、继电器、大功率开关电源,电磁环境相当恶劣。高分辨率 Δ-Σ 型 ADC 配合合适的数字滤波,比传统逐次逼近型 ADC 抗噪表现好不少。
- 上位机轮询要求稳定。设备在严苛环境下可能几个月不休机,通信绝对不能因为偶发时序错位就挂死。UART 接口的容错性比 SPI 好处理,重新同步的机制也更贴近串口通信的标准习惯。
说到这基本可以下个初步结论:ADS122U04 + STM32F411RE 的方案,特别适合那种"多点多类型传感器、现场干扰大、又要求长期稳定运行"的温度采集项目。接下来我会从硬件接线、寄存器配置、固件实现到实测校准,完整梳理一遍整个落地过程。
2. 硬件接线与无源器件选型
很多人拿到新的 ADC 芯片,先急着写代码,结果板子打出来之后各种莫名其妙的问题——读数漂、信号跳变、偶发通信失败。我自己的经验是,ADC 这类模拟前端芯片,硬件设计决定了 80% 的成败,固件只能救回来剩下 20%。尤其是 ADS122U04 这种高分辨率器件,外围无源器件的选型和布局,比通信代码重要得多。
2.1 供电方案与去耦设计
ADS122U04 的模拟供电(AVDD)和数字供电(DVDD)我习惯分开处理。虽然它的标称工作范围是 2.7V 到 5.5V,可以直接用 3.3V 统一供电,但工业板子上我会选择 5V 给模拟部分、3.3V 给数字部分,这样有几个现实的好处:
- 5V 供电时,内部 PGA 和 IDAC 的动态范围更宽,尤其在使用 PT100 需要激励电流、又要放大微弱信号时,头上有更多的电压余量。
- 模拟地与数字地在 ADC 芯片下方单点连接,再把整个模拟地平面与数字地平面分割开,可以有效阻止 MCU 那边的高速开关噪声窜入模拟前端。
去耦电容方面,AVDD 到地放 10 μF 钽电容并联 0.1 μF 陶瓷电容,这是标准组合。特别要注意的是,0.1 μF 的陶瓷电容要尽量靠近芯片的电源引脚,距离超过 3 毫米效果就大打折扣。ADS122U04 的参考电压输入 REFP0/REFN0 同样需要去耦,我通常也会在基准引脚上放一组 1 μF 和 0.1 μF 的并联电容,避免基准线上的高频噪声直接耦合进转换结果。
关于这个板子的电源拓扑,有一个容易被忽略的细节:如果板子上有开关电源或者 DC-DC,那么 ADC 的模拟供电最好再接一级 LC 滤波,甚至用低压差线性稳压器二级稳压。我自己踩过一次坑,第一批样片直接共用了一路 buck 输出的 5V,结果 ADC 在低速转换模式下读数一直有 20 到 30 LSB 的周期性跳动,后来查出是 buck 的开关纹波窜进来的。加了 22 μH 电感和 10 μF 电容组成 LC 滤波后,问题当场消失。
2.2 输入通道怎么接
ADS122U04 提供四个模拟输入通道,可以配置成差分输入或单端输入。做温度测量时,我按照传感器类型来区分接法:
- PT100/PT1000 铂电阻:采用比率式三线或四线制接法。四线制最简单,两根线给激励电流,两根线测电压,完全消除导线电阻影响。三线制需要做一点算法补偿。ADS122U04 内置 IDAC 电流源,可以直接配置一个恒流源输出接到 RTD 上,然后差分输入测 RTD 两端的电压。这样省掉了外部恒流源电路,只需要在 IDAC1 输出引脚上串联一个电阻限制电流,在测量回路中并联一个滤波电容。
- 热电偶:热电偶输出的是微伏级电压信号,需要 PGA 高增益放大。常见的 K 型热电偶灵敏度约为 41 μV/°C,即使温度变化 1000°C,输出电压也不过 41 mV,必须经过高倍数放大后才能被 ADC 有效采集。此时把热电偶正极接 AIN0,负极接 AIN1,配置 PGA 增益为 32 或 64 比较合适。同时必须做冷端补偿,因为热电偶测量的是测量端和参考端之间的温差,参考端(就是接线端子处)的温度变化也会直接反映到输出上。ADS122U04 内部自带温度传感器,可以快速读芯片附近温度当冷端参考,这是这颗芯片做热电偶方案最方便的地方。
- NTC 热敏电阻:相对简单,用 IDAC 激励加单端测量或者直接外部上拉到参考电压构成分压都可以。不过在 HVAC 这种追求成本的场景,NTC 也足够用了。
我画了一个简易的接线示意来说明 PT100 四线制接法(文字描述版):
- IDAC1 输出 → 接 PT100 的激励正端。
- AIN0 → 接 PT100 电压感应正端。
- AIN1 → 接 PT100 电压感应负端。
- IDAC2 输出 → 接 PT100 激励负端,同时返回 AVSS(也可以直接接地,取决于 IDAC 配置)。
这种接法里,IDAC 电流经过 RTD 后回到地,测量通道只在 AIN0/AIN1 间测电压,完全不受到引线电阻的影响。滤波器我习惯在每个模拟输入引脚对地放一个 1 nF 的陶瓷电容,配合内部 PGA 的输入结构,构成低通滤波,能有效抑制射频干扰。
关于基准电压,ADS122U04 内部基准是 2.048V,但对于比率式测量,最佳做法是把 REFP0 和 REFN0 直接接到 RTD 测量回路的两端,这样激励电流的波动会同时影响传感器电压和参考电压,比例关系保持不变,就能消除电流源漂移带来的误差。这叫比率式测量,是 RTD 测量的标准做法,实现起来就是在固件里把参考源选择寄存器配置成外部参考。这点后面寄存器配置会细讲。
2.3 PCB 布局的三条经验
第一,模拟输入走线尽量短且对称。AIN0 和 AIN1 如果是一对差分信号,在 PCB 上应该并行走线,间距保持一致,避免形成环路面积过大,否则空间里的电磁干扰会在差分线上产生差值电压,直接进入转换结果。
第二,UART 的 TX/RX 线要远离模拟输入走线。ADS122U04 的数据输出速率虽然不高,但 UART 线上的沿跳变依然可能耦合到高阻抗的模拟输入上。我通常将 UART 线走底层,模拟输入走顶层,中间用地平面隔离,效果很明显。
第三,参考电压的走线要单独处理,不能和数字信号混在一起。参考电压是转换的"尺子",尺子不稳定,测出来的数据必然不准。务必保证基准走线的低阻抗和干净度。
3. 固件侧:UART 通信协议与寄存器配置要领
硬件搭好之后,接下来就是固件。ADS122U04 比较特殊的点在于用 UART 传输指令和数据,它的命令字结构和 SPI 版本 ADS1220 略有不同,配置寄存器的序号也不完全一样。第一次上手,拿数据手册对着看容易看晕,我在这里把关键点直接拆开讲。
3.1 通信时序和 UART 参数
ADS122U04 的 UART 通信采用标准的异步串行格式,一个起始位、8 个数据位、无校验位、一个停止位。默认波特率是 9600,也可以通过配置寄存器改成更高速率。我在项目里默认用 9600,原因只有一个:工业现场长线传输,速度越慢越稳。9600 波特率下每字节大约 1.04 ms,对温度采集这种低频应用完全够。
通信开始前,需要把 CS(片选)引脚拉低。重点来了:ADS122U04 的 CS 在 UART 模式下,不仅起到片选作用,还承担了通信同步的功能。每次发起新的命令序列前,主机必须先把 CS 拉低,然后发送命令字节。在一整轮数据交换结束前,CS 都要保持低电平。如果通信中途 CS 被拉高,芯片会认为这轮通信异常终止。所以我习惯把所有对 ADC 的读写操作封装成原子操作:拉低 CS → 发送命令 → 读取数据 → 拉高 CS,全程禁止中断打扰。
用 STM32F411 的 USART 对接时,需要注意电平匹配。ADS122U04 的逻辑电平范围支持 1.8V 到 3.6V,和 STM32F411 的 3.3V 供电可以直接对接,不需要额外电平转换。但 UART 空闲状态应为高电平,务必确认 MCU 的 GPIO 初始化和串口配置没有把它初始化为低电平,否则芯片会误判起始位。
3.2 寄存器逐位拆解
ADS122U04 的寄存器一共有四个,每个 8 位。我们需要重点关注的配置都集中在前三个寄存器里。下面我用自己项目里的实际配置逐位说明。
寄存器 0(配置寄存器)控制输入多路选择器、PGA 旁路和增益。
| 位 | 名称 | 我设置的值 | 说明 |
|---|---|---|---|
| 7:6 | MUX[1:0] | 00 或 01 | 差分输入通道选择,00 表示 AIN0 与 AIN1 组成差分对 |
| 5 | PGA_BYPASS | 0 | 0 表示启用 PGA,1 表示旁路 PGA |
| 4:1 | GAIN[3:0] | 以实际传感器确定 | 增益设置,0001 为 1 倍,到 1000 为 128 倍 |
| 0 | 保留 | 0 | 写 0 |
PT100 四线制测量时,通常 PGA 增益设置在 8 到 16 之间。因为 IDAC 给 1 mA 激励时,PT100 在 0°C 时电阻约 100 Ω,压降约 100 mV,增益 16 时满量程范围内分辨率依然足够。K 型热电偶则要用到 64 或 128 倍增益。
寄存器 1(配置寄存器)控制数据速率和转换模式。
| 位 | 名称 | 我设置的值 | 说明 |
|---|---|---|---|
| 7 | 保留 | 0 | - |
| 6:4 | DR[2:0] | 100 | 数据速率选择,100 表示 20 SPS |
| 3 | MODE | 1 | 1 表示单次转换模式,0 表示连续转换 |
| 2:0 | 保留 | 0 | - |
数据速率这里我要多说两句。ADS122U04 可以跑到 1 kSPS,但温度测量真的没有必要。反而是低数据速率下,数字滤波器能发挥更好的抑制工频干扰效果。20 SPS 是我在大多数工业场景下的首选,因为 50 Hz 工频干扰正好能被内部滤波器有效衰减。如果环境干扰特别严重,我甚至会用 5 SPS。
单次转换模式 vs 连续转换模式,这取决于应用场景。HVAC 控制器一般温度变化慢,单次转换就够,而且可以只在需要时唤醒 ADC,省一点功耗。工业过程控制如果希望实时性高,可以开连续转换,但要注意 UART 的数据流解析。我个人的偏好是用单次转换加数据准备好中断,逻辑简单,出错了也容易排查。
寄存器 2(配置寄存器)控制 IDAC 电流源。这是我使用 PT100 时最关键的一个寄存器。
IDAC1 和 IDAC2 的电流大小都是由同一个字段控制的。常用配置有 250 μA、500 μA、1000 μA、1500 μA。测 PT100 时我通常用 1000 μA,这样传感器的自热效应较小,又能产生足够高的电压幅度供 ADC 测量。测 PT1000 时,因为电阻本身较大,用 250 μA 或 500 μA 就够了,能把自热降到最低。
还有一点必须提醒:IDAC 的上电默认状态是关闭的。你配置了 IDAC 电流值,但没告诉它往哪个引脚输出,传感器上不会有电流流过,读数自然为零。寄存器 3 里才是设定 IDAC1 输出到哪个引脚的地方。很多人第一次调试找半天"为什么 RTD 上没电压"就是漏了这个。
寄存器 3(配置寄存器)控制 IDAC 输出引脚分配以及内部温度传感器的使能。
我测 PT100 配置 IDAC1 输出到 AIN2,IDAC2 输出到 AIN3,传感器差分电压由 AIN0/AIN1 测。这个接法非常通用。测热电偶时,如果不需要电流激励,IDAC 可以全部关闭,把内部温度传感器使能(用于冷端补偿),读取内部温度后就换算成冷端温度。
3.3 固件读写流程和命令格式
ADS122U04 通过 UART 收到的每个字节都是命令。常用命令有:
- 写寄存器命令:0x40 地址字段 + 数据字节 + 后续要写的数据。
- 读寄存器命令:0x20 地址字段 + 数据字节,芯片会回传对应数据。
- 启动转换命令:根据不同协议,通常发送 0x08 或类似命令后,芯片会在转换完成后在 DOUT/DRDY 引脚产生指示信号。
我举个实际写寄存器的例子。假设要把寄存器 0 配成:AIN0/AIN1 差分输入、PGA 使能、增益 16,寄存器 0 的值应该是 0x09(二进制 00001001,MUX=00,PGA_BYPASS=0,GAIN=1000)。发送序列就是:
拉低 CS 发送 0x40 // 写寄存器命令,起始地址指向寄存器 0 发送 0x09 // 寄存器 0 的值 拉高 CS如果是多寄存器连续写,在第一个地址命令后依次发送后面寄存器的值即可。读回验证非常有必要,我会在固件初始化完成后,再读取一遍寄存器值,确认写入成功,再启动转换。这个习惯帮我避免过很多次"莫名其妙配置没生效"的问题——大多数情况都是 CS 时序没拉对导致命令被忽略。
3.4 STM32F411RE 串口初始化代码
下面贴一段我实际使用的 STM32 HAL 库串口初始化配置。注意波特率 9600,8 数据位、无校验、1 停止位,这是与 ADS122U04 匹配的关键。
// 串口句柄 UART_HandleTypeDef huart2; void ADS122U04_UART_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART2_CLK_ENABLE(); // PA2 = TX, PA3 = RX GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart2); }串口初始化后,写寄存器命令的封装函数如下:
uint8_t ads122u04_write_reg(uint8_t reg_addr, uint8_t *data, uint8_t len) { uint8_t cmd = 0x40 | (reg_addr << 1); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_UART_Transmit(&huart2, &cmd, 1, 100); for (uint8_t i = 0; i < len; i++) { HAL_UART_Transmit(&huart2, &data[i], 1, 100); } HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); return 0; }读寄存器和触发转换也类似,只是命令字节不同,读取时用 HAL_UART_Receive 等待相应字节返回。这里要注意超时保护,工业环境下串口偶发数据错位是有可能的,超时后把 CS 拉高再重新发起,能非常有效地避免通信死锁。
4. 从原始 ADC 值到真实温度
配置好寄存器、能读到原始 ADC 码之后,真正的工作才刚开始。很多工程师卡在"ADC 出数了但温度不对"这一步。这部分涉及计算、补偿和滤波,我按传感器类型拆开讲。
4.1 PT100 的线性化计算
PT100 的电阻和温度之间有一个接近线性但并非完全线性的关系。IEC 60751 标准定义了 -200°C 到 850°C 范围内的电阻-温度特性。工程上最简单实用的方法是查表加线性插值。预先在 MCU Flash 里存放一张 -50°C 到 300°C 的分度表,每 10°C 一个点,然后对测量得到的电阻值做插值计算,精度可以轻松做到 ±1°C 以内,甚至更好。
计算电阻值的公式如下(比率式测量):R_RTD = (ADC_Code / 2^24) × R_REF。其中 R_REF 是参考电阻的阻值。举个例子,如果读出 ADC 原始码值为 0x19999A(约 1.68 million),24 位满量程是 16,777,216,参考电阻 1500 Ω,那么 R_RTD ≈ (1.68M / 16.78M) × 1500 ≈ 150 Ω。这个阻值对应约 130°C。
实际代码里我更推荐用"按比例查表 + 分段线性插值"的办法:
float rtd_resistance_to_temp(float resistance) { // PT100 分度表示例,单位 ohm,温度间隔 50 度 static const float table_temp[] = {-50, 0, 50, 100, 150}; static const float table_res[] = {80.31, 100.00, 119.40, 138.50, 157.32}; if (resistance <= table_res[0]) return table_temp[0]; if (resistance >= table_res[4]) return table_temp[4]; for (int i = 0; i < 4; i++) { if (resistance >= table_res[i] && resistance < table_res[i+1]) { float ratio = (resistance - table_res[i]) / (table_res[i+1] - table_res[i]); return table_temp[i] + ratio * (table_temp[i+1] - table_temp[i]); } } return 0; }4.2 热电偶的冷端补偿
热电偶测量比 RTD 麻烦在冷端。ADC 测量得到的是热端和冷端之间的电势差,冷端温度如果不稳定,换算出的热端温度就是错的。做法是这样的:
- 从 ADS122U04 内部温度传感器读取芯片附近温度,作为冷端参考温度 T_cold。
- 根据热电偶分度表,查表得到冷端温度对应的热电势 E(T_cold)。
- 将 ADC 测得的电压加上 E(T_cold),得到补偿后的总热电势 E_total。
- 再根据热电偶分度表,反查 E_total 对应的温度,就是实际热端温度。
这套方法手工做很啰嗦,但在固件里用查表实现并不复杂。需要注意的是,ADS122U04 内部温度传感器反映的是芯片自身温度,如果你的 PCB 布局让芯片远离接线端子,冷端温度读出来就不可靠,那补偿精度就很差。所以做热电偶方案时,芯片要尽可能靠近热电偶的接线端子,或者干脆在端子上放一颗外部温度传感器。
4.3 数字滤波与稳定性处理
现场应用里,单次 ADC 读数的波动幅度往往会让人焦虑。这不是 ADC 的错,更多是环境噪声和传感器本身的热噪声。我通常做两级处理:
第一级是滑动平均滤波。取最近 16 次转换结果做平均。温度变化本身是惰性量,16 次平均带来的延迟完全可以接受。关键是能滤掉大部分随机噪声。
第二级是变化率限幅。也就是把当前温度值和上一拍温度值比较,如果变化超过设定阈值(比如 0.5°C),就认为可能出现了干扰或者传感器断线,采用上一拍的数值并置一个报警标志。这对工业现场特别有用,传感器偶尔接触不良、连接线瞬间抖动,都会被这层保护吸收掉。
实际效果上,这套滤波组合处理完的曲线,比裸读 ADC 数据的波动从 ±1.5°C 压制到了 ±0.1°C 以内。在 HVAC 系统里,±0.1°C 的稳定性完全满足舒适度控制的需求。
5. 实测中的常见坑与排查思路
再完美的方案,到了现场也有可能出幺蛾子。最后这部分,我把这段时间实测踩过的坑和排查思路完整记录下来。这些经验在数据手册和参考设计里通常找不到,但直接决定了项目能不能顺利跑起来。
5.1 读取数据全是 0xFFFFFF 或 0x000000
这是最常见的现象。先说 0xFFFFFF:这通常意味着 ADC 输入的正端电压远大于负端电压,超出了 PGA 的量程范围。排查步骤:用万用表测量 AIN0 和 AIN1 之间的电压,确认是否在一个合理的范围内;再看 PGA 增益是不是设得过大,导致小信号被放大后削顶;最后查 IDAC 是否真的有电流输出。
如果是 0x000000,那就是负端电压大于正端,或者 AIN0 根本没信号进来。大概率是 IDAC 配置的问题——电流没灌到传感器上。
我印象最深的一次调试经历,是第一批板子回来,所有通道读到的值都异常偏大,检查来检查去,最后发现是 IDAC 电流设置正确,但 IDAC 输出引脚在寄存器 3 里的配置忘了写。因为没有电流经过传感器,AIN0 和 AIN1 实际上处于浮空状态,PGA 放大的是纯粹的噪声,读数自然荒谬。后来每次改完寄存器,我都会读回寄存器 2 和 3 确认一遍,至少能排除 50% 的"灵异问题"。
5.2 UART 通信偶发错位
ADS122U04 偶尔会出现"发了命令不回应"或"回的数据对不上"的问题。经验里有两类原因:
一类是 CS 时序被中断破坏。如果命令发送过程中有高优先级中断插入,CS 保持低电平的时间超过了芯片的通信超时,芯片可能就退出这轮通信了。解决方法是发送命令期间屏蔽相关中断,或者用 DMA 一次性把命令发完再打开中断。
另一类是 UART 空闲电平被拉低。如果 MCU 的 RX 引脚内部上拉没有配置好,或者外部走线把 RX 拉低了,芯片会误以为一直有数据进来,状态机就乱了。用示波器看 UART 线在空闲时是否为高电平,这个是最快的排查办法。
5.3 温度读数整体偏高或偏低
如果读数稳定但偏差一致,问题多半不在 ADC,而在传感器本身。PT100 的精度等级、导线电阻补偿是否做对、参考电阻的精度,这些都会带来系统性偏差。我之前在 HVAC 项目里踩过一次:PT1000 的三线制接法少做一路补偿,导致每个通道读数统一偏高约 8°C。最后在程序里修正了导线电阻补偿算法,把第三根线的电压降也计算进去,才把误差压下来。
校准方法上,我推荐零点和满度的两点校准。把传感器放在冰水混合物(0°C)和沸水(100°C)中,记录 ADC 原始码,然后计算增益误差和偏移量,写入 Flash 的校准参数区。这样批量生产时每台设备都可以做独立的校准参数标定,互换性和一致性都能显著提高。
5.4 电磁干扰导致读数跳变
HVAC 设备里压缩机和风机的启停,是温度采集信号最大的干扰源。板子放在设备内部,电机一启动,传感器线上就能感应出很大的共模噪声。我的应对措施有三步:
第一步,确认传感器线用屏蔽线,屏蔽层单端接地,不要两端都接,否则会形成接地环路。 第二步,软件上的滑动平均滤波加变化率限幅,这套组合对付瞬时干扰非常有效。 第三步,如果干扰实在太大,考虑增加外部 RC 低通滤波,在 AINx 引脚前各串联一个 10 kΩ 电阻并联 100 nF 电容,把截止频率压到几百赫兹以下。
做完这三步,温湿度读数在电机启停瞬间几乎纹丝不动。
5.5 长期运行温漂
工业设备连续运行几个月,如果发现温度读数有缓慢的偏移,先不要急着怀疑 ADC。我遇到过的情况是参考电阻温漂导致的——板子长时间工作后内部温度上升,参考电阻阻值变化,比率式测量的基准就变了,温度读数自然跟着偏。后来我把参考电阻从普通厚膜电阻换成了低温度系数的精密电阻(25 ppm/°C 以下),问题得到明显改善。
另一个经验是,如果项目对长期稳定性要求高,建议定期(比如每小时)读一次 ADS122U04 的内部温度传感器,把它和外部传感器温度做对比。如果两者差值越来越大,说明板子内部正在升温,甚至可能已经出现了异常发热,用来做预测性维护也很有价值。
5.6 单次转换模式下的启动等待时长
最后给一个固件时序上的小提醒。ADS122U04 在单次转换模式下,发送启动命令后,不是马上就能读数据的。转换时间取决于设定的数据速率。20 SPS 对应的单次转换周期是 50 ms,如果你发完启动命令立刻去读数据,读到的多半是上一次转换的旧值或者空数据。我写完驱动后加了一个硬性延时或者用 DRDY 引脚中断来指示转换完成,初始化时也先丢弃前两三次转换结果,等输出稳定了再正式启用以太网,不对,是正式启用数据链路,这套流程就稳了。
最后的实操体会
这套 ADS122U04 + STM32F411RE 的组合,我用到现在,最大的感受是"省心"。硬件上 UART 接口简化了布线,软件上 STM32 的 HAL 库和 DMA 让驱动代码量大幅减少,而高分辨率加上合理的滤波算法,让最终温度数据既准确又稳定。
如果你正要开始类似的工业或 HVAC 温度采集项目,我建议你按这样的顺序推进:先把硬件按前面第二部分做好,尤其是电源去耦和参考电压部分不要省;然后花两天时间把寄存器配置读通,写一个寄存器回读函数验证所有配置是否生效;最后再去做温度换算和滤波。这个顺序看起来慢,实际却是最快的路径。
另外一个小建议是,所有驱动函数尽量带超时控制和返回值检查,不要假设每次通信都成功。工业设备不像开发板,现场环境不会对你客气。把通信异常当成正常现象来设计,你的系统才能真正扛得住长期运行。