去年一个工业温控项目结题后,我把整套 BOM、固件和调试记录整理了一遍,发现 ADS122U04IPWR 加 ATmega1280 这个组合,很适合工业与 HVAC(暖通空调)场景里的温度测量。这次项目里我用 ATmega1280 做主控,外接 ADS122U04IPWR 这颗 24 位 Δ-Σ ADC,采集 PT100/PT1000 热电阻和 K 型热电偶,跑了管道温度、风系统回水温度、设备表面温度好几路信号。这套方案从选型到现场投运都比较顺利,但中间也踩了几个只有现场才能遇到的坑。下面按我实际动手的顺序把整个设计讲一遍,从选型逻辑、硬件连接、前端电路、UART 通讯协议,到校准和验收,尽量写到能直接照着复现的程度。
1. 选型逻辑:为什么用 24 位 UART 型 ADC 来测温
1.1 ATmega1280 内置 ADC 为什么不直接用来测温
很多第一次做温度采集的人会问:ATmega1280 不是自带 10 位 ADC 吗?为什么还要外挂 ADS122U04IPWR?
ATmega1280 内置的是 10 位逐次逼近型 ADC,满量程按 5V 算,一个 LSB 大约对应 4.88mV。在 0 到 100℃ 范围内,PT100 的电阻变化约 0.385Ω/℃,如果激励电流取 1mA,电压变化是 0.385mV/℃。也就是说,即使不考虑噪声,4.88mV 的量化分辨率也只能勉强分辨约 12.7℃,这根本满足不了暖通和工业现场 ±0.1℃ 量级的需求。
有人会想:我把激励电流加大到 5mA,再把信号放大后送给 ADC。但 5mA 流过 PT100 会产生自热,100Ω 的电阻上发热功率达到 2.5mW,对高精度测温来说已经不可接受了。所以正确的思路不是去迁就单片机内置 ADC,而是换一颗真正面向高精度慢速信号测量的外部 ADC。
1.2 为什么选 UART 接口而不是 SPI 或 I2C
ADS122U04IPWR 最特别的地方是它带 UART 接口。常见的精密 ADC 大多用 SPI 或 I2C,但我这次做的是分布式 HVAC 和工业设备温度采集,传感器前端和主控之间往往隔着几十厘米到一两米线缆,甚至更长。SPI 有四根线(时钟、数据进出、片选),线一长,时钟信号上升沿变差、片选时序容易错乱,而且抗干扰能力弱,容易被电机变频器干扰。I2C 虽然线少,但速率低、地址冲突麻烦,加上长线电容大,上拉电阻调整困难,稳定性不好。
UART 的优势很直接:数据线只有 TX/RX 两根,通信双方各自有时钟,线缆长度容忍度远高于 SPI。工业现场常见的做法就是把模拟前端做得靠近传感器,用 UART 把数据送回主控,再进一步转成 Modbus RTU 或 4-20mA 给 PLC。ADS122U04IPWR 本身也带数据就绪引脚,主机完全可以通过 DRDY 判断什么时候去读结果,不用像 SPI 那样掐着时序反复查询状态位。
这里还要补充一个选型理由:隔离方便。UART 协议是最容易做光耦隔离的串行协议,不需要传时钟,只要隔离 TX/RX 两根信号线就行。工业环境里如果现场和主控之间的地电位有差异,加上光耦或数字隔离器就能切断地环路,SPI 隔离就要复杂得多。
1.3 ADS122U04IPWR 的关键参数与温度测量定位
ADS122U04IPWR 是 TI 推出的 24 位 Δ-Σ ADC,内部集成了可编程增益放大器(PGA)、两个激励电流源、内部电压基准以及温度传感器。它提供 4 个模拟输入通道,可以组成差分输入或单端输入;内部基准电压典型值是 2.048V;PGA 增益可以从 1 倍一路调到 128 倍;两个激励电流源可以给热电阻提供激励,这样便不需要外部搭恒流源电路。
对温度测量来说,这几个资源正好覆盖了最常用的三类传感器场景:PT100/PT1000 需要恒流激励和较高分辨率;热电偶输出只有几十微伏每摄氏度,需要高增益 PGA 来放大;冷端补偿需要知道 ADC 附近的温度,内部温度传感器正好承担这个任务。此外,ADS122U04IPWR 支持低功耗模式,在不需要高采样率时可以降到很低的电流,对控制柜内散热和长期在线运行都有好处。
我实际用下来,24 位分辨率里真正能稳定使用的有效位数大概在 19 到 20 位左右,配合合适的 PGA 和滤波,做到 0.01℃ 的分辨率是没问题的。当然,分辨率高不等于精度高,后续还要靠基准电阻、校准和布线来保证准确性,这点后面单独说。
2. ADS122U04IPWR 与 ATmega1280 的硬件连接细节
2.1 先从引脚理解 ADS122U04IPWR 的供电与信号分组
ADS122U04IPWR 供电分模拟电源和数字电源两部分。模拟电源接 AVDD/AVSS,数字电源接 DVDD/DGND。我这次使用单 3.3V 供电,AVDD 和 DVDD 都接 3.3V,但中间要用磁珠或者小电阻分开,模拟地和数字地单点连接,目的还是减少数字开关噪声窜到模拟输入。
芯片的模拟输入通道是 AIN0 到 AIN3,可以配置成多路差分输入。内部还有一个多路复用器,可以轮流切换不同通道进行测量。不过要注意,逐次切换通道后需要等待转换稳定,尤其是前面的通道刚切完,建议弃掉前面一两个转换结果再取数。
与 ATmega1280 相连的关键引脚主要是三根:DIN(数据输入)、DOUT(数据输出)、DRDY(数据就绪)。DOUT 是芯片向主机回报数据的引脚,DIN 是主机向芯片发送指令和数据的引脚。DRDY 用来指示转换结果是否准备好,准备读数据时它会拉低。UART 的波特率可以配置,实际应用中我会倾向于选择 9600 波特率或 115200 波特率,具体看线长和干扰情况。
2.2 ATmega1280 的 UART 资源分配
ATmega1280 一共有 4 个硬件 UART,这是选它做主控的重要原因。一个项目里往往不只有一路温度信号,可能还要同时连接温度、湿度、压力等多个传感器。4 个 UART 可以灵活分配:我这次用 USART0 接 ADS122U04IPWR,USART1 保留给调试串口,USART2 接上位机或 Modbus 网关,USART3 还可以再接第二块 ADC 做扩展。如果传感器数量很多,也可以让一个 ADC 打开内部多路开关,把所有模拟输入轮流扫一遍,减少主控串口占用。
ATmega1280 工作在 16MHz 晶振,UART 波特率误差计算中要注意 UBRR 寄存器取值。公式为:
UBRR = F_CPU / 16 / baud - 1
以 9600 波特率计算:16000000 / 16 / 9600 - 1 = 103.17,取 103 后实际波特率误差约 0.15%,这是可以接受的。115200 波特率时取 8,误差也很小。现场如果线缆较长,我更倾向降到 9600,误码率明显降低。
2.3 数字地与模拟地的连接原则
PCB 布局里最容易出问题的就是地。ADS122U04IPWR 的 AGND 和 DGND 建议在芯片附近的单点汇合,不要让数字电流流经模拟信号下方的地平面。我做过几块样板对比:分开地平面、单点汇合的板子,测量结果的峰峰噪声明显小于随便铺铜的板子。
如果你的系统是开关电源供电,还要在 AVDD 引脚附近加一个 10μF 钽电容和 0.1μF 陶瓷电容并联。开关电源的纹波通常有几十毫伏,直接进 ADC 会表现为转换结果周期性跳动。ADS122U04IPWR 内部虽然有电源抑制能力,但不能完全替代外部滤波电容。
2.4 隔离和长线传输的工程处理
如果 ADC 板离主控板比较远,且两点之间的地电位不同,建议在 DIN/DOUT 和 DRDY 三根线上加数字隔离器。我这次没有真的把现场供电隔离开,但因为控制柜内布局紧凑,中间有一段 40cm 的排线,我把 DOUT 线做了串联 33Ω 电阻,减少信号边沿的反射和振铃。
DRDY 信号也要注意上拉电阻。ADS122U04 的 DRDY 是开漏输出还是推挽输出,具体看手册,我实际习惯给它加一个 10kΩ 上拉到 DVDD,保证主机端电平干净。
3. RTD 与热电偶的前端电路:从毫伏到温度
3.1 比例式测量的核心思路
ADS122U04IPWR 内部有两个激励电流源,这个功能做 RTD 测温时非常关键。常规做法是用外部恒流源给 PT100 供电,然后单独读电压,但恒流源精度有限,且温度漂移不可控。比例式测量的思路是:让同一个电流源同时流过参考电阻和热电阻,ADC 的参考电压也从同一个电流源获得。这样电流源的波动会被抵消,因为电压信号和参考电压同比例变化。
以 PT100 为例:电流源输出 500μA,流过 PT100 后产生电压 V_RTD = 500μA × R_RTD。再把同一个 500μA 电流流过精密参考电阻 R_REF,产生参考电压 V_REF = 500μA × R_REF。ADC 转换结果是 V_RTD / V_REF 的比值,约等于 R_RTD / R_REF,电流源的漂移被完全消掉。
如果选 R_REF = 1500Ω,那么 V_REF = 0.75V。PT100 在 0℃ 时是 100Ω,对应 V_RTD = 50mV;在 200℃ 时约 175.86Ω,对应 V_RTD = 87.9mV。这个信号量级对 24 位 ADC 来说非常充裕,PGA 增益可以设成 1 倍到 8 倍之间,不需要盲目放大。
3.2 两线、三线、四线 RTD 怎么选
两线制最简单:一根线给电流,一根线采样电压。但导线本身有电阻,而且导线电阻随温度变化,这会直接引入误差。比如 1 米长的普通铜线,截面积 0.5mm²,电阻大约 0.03Ω 到 0.05Ω,对应 PT100 的误差相当于 0.1℃ 左右。长距离现场布线下这个误差完全不可忽略。
三线制是工业现场最常见的做法。三根导线中,两根走激励电流,一根只用来采样电压,电流不经过这根采样线。如果三根线的长度和线径一致,导线电阻误差可以通过差分测量基本抵消。HVAC 项目里,水管温度、风管温度传感器基本都是三线制,因为成本和精度平衡最好。
四线制精度最高。两根线提供电流,另外两根线独立采样电压,采样回路几乎不取电流,所以导线电阻造成的误差可以被消除掉。缺点是线缆成本和接线工作量大。通常只有实验室标准级探头才用四线制。
我这次在关键回水温度测点用了三线制,在设备出厂校验用的高精度比对测点用了四线制。两者实测下来的差距在长线情况下能到 0.2℃,三线制已经满足暖通控制要求,四线制则用于计量级应用。
3.3 热电偶的接入、PGA 增益与冷端补偿
热电偶的输出电压非常小,K 型热电偶大约 40μV/℃,J 型大约是 55μV/℃。如果直接进 24 位 ADC 而不放大,虽然理论分辨率够,但噪声会淹没信号。所以必须用 PGA 把热电偶信号放大到合适的量程。
ADS122U04IPWR 的 PGA 增益设置由寄存器控制。我测 K 型热电偶时使用增益 32,对应的差分满量程约 64mV,正好覆盖 K 型在 0 到 800℃ 左右大约 33mV 的输出。如果只需要测 0 到 300℃,可以把增益提高到 64,分辨率更好。增益也不是越高越好,PGA 增益越大噪声越小,但可测范围缩小,超出范围后输出会饱和,变成错误读数。
热电偶测温还必须处理冷端补偿。热电偶的输出电压是热端和冷端的温差电压,如果接线端子那端温度变化,读数就会跟着漂。ADS122U04IPWR 内部有一个温度传感器,我利用它读取热电偶接线端子附近的温度,然后在软件里把那部分电压补偿回去。具体的补偿计算是对热电偶输出加上冷端对应的电压值,再查温度-电压表得到最终温度。
一定要强调:内部温度传感器读的是芯片附近温度,如果接线端子距离芯片比较远,或板子局部发热严重,必须在接线端子附近额外布一个 NTC 或另一路硅温度传感器来做冷端补偿。我后来在 PCB 上把热电偶的冷端引出到靠近芯片的位置,才把补偿误差压到 ±0.5℃ 以内。
3.4 模拟滤波与工频干扰
ADS122U04IPWR 是 Δ-Σ 型 ADC,本身有数字滤波特性,但输入端仍需要加一阶 RC 低通滤波器。工业环境里大量使用 50Hz 交流电源,变频器也会产生丰富的谐波。RC 滤波器的截止频率可以设在 1kHz 到 10kHz 之间,电阻不宜太大,否则会降低 ADC 输入阻抗,影响测量精度。
如果数据速率选择在 50SPS 左右,数字滤波器本身对 50Hz 工频干扰有很好的抑制,这是采用了整周期平均的原理。如果项目所在区域电网是 60Hz,则要选择对应抑制的采样速率。这一点在配置寄存器时就要提前考虑好,不然后面在 PLC 柜里看到读数波动会很难查到原因。
4. 配置寄存器与固件实现:让 ATmega1280 和 ADC 真正跑起来
4.1 同步时序:自动波特率检测、DRDY 和命令帧
ADS122U04IPWR 需要先建立 UART 同步。上电后,芯片会有一段稳定时间,主机不要立刻发有效数据。我实际的上电流程是这样的:等电源稳定,延时 100ms 以上,然后发送一串 0x00 同步字节,让 ADC 的 UART 模块自动检测波特率。同步完成后,芯片会返回响应,之后才能执行寄存器读写和转换启动命令。
DRDY 引脚是读取转换结果的信号灯。每次转换完成,DRDY 拉低,主机可以发送读取命令,然后从 DOUT 上连续读取 3 个字节,组成 24 位数据。如果主机不管 DRDY,直接发 RDATA 命令,有可能读到旧数据,甚至读到正在更新的中间值。所以不管代码怎么写,一轮轮询里必须判断 DRDY 电平。
ADS122U04IPWR 的命令字格式并不复杂,常见操作包括:写寄存器、读寄存器、读转换结果、启动转换、进入低功耗待机。具体命令字数值建议以数据手册为准,下面代码里我给出的是通用框架,重点不在默写命令字,而是让通信逻辑可复用。
4.2 初始化代码示例
下面是一段基于 AVR 库的示意代码,重点是 UART 初始化和写寄存器流程。实际寄存器值和位域要对照 ADS122U04IPWR 的最新数据手册,不同批次芯片虽有兼容性保证,但应以前后参考手册一致为准。
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #define F_CPU 16000000UL static void uart0_init(uint32_t baud) { uint16_t ubrr = F_CPU / 16 / baud - 1; UBRR0H = (uint8_t)(ubrr >> 8); UBRR0L = (uint8_t)ubrr; UCSR0B = (1 << RXEN0) | (1 << TXEN0); UCSR0C = (3 << UCSZ00); /* 8数据位 1停止位 无校验 */ } static void uart0_putc(uint8_t c) { while (!(UCSR0A & (1 << UDRE0))); UDR0 = c; } static uint8_t uart0_getc(void) { while (!(UCSR0A & (1 << RXC0))); return UDR0; }写寄存器逻辑:
static void ads122u04_write_reg(uint8_t reg, uint8_t value) { /* 先发送 WREG 相关命令字节,再发数据; 这里命令字和寄存器地址编码具体查手册 */ uart0_putc(0x40 | (reg & 0x07)); uart0_putc(0x00); uart0_putc(value); } static void ads122u04_init(void) { /* 上电稳定 */ _delay_ms(100); /* 发送同步字节,触发自动波特率检测 */ for (uint8_t i = 0; i < 16; i++) { uart0_putc(0x00); _delay_ms(1); } /* 切换通道、PGA、基准、激励电流等按需求写寄存器 */ ads122u04_write_reg(0, 0x00); /* MUX配置、PGA增益 */ ads122u04_write_reg(1, 0x00); /* 基准电压、电流源配置 */ ads122u04_write_reg(2, 0x00); /* 数据速率与滤波 */ ads122u04_write_reg(3, 0x00); /* 其他功能设置 */ }有人会问,代码里这些 0x00 是不是不准确?确实,真实项目里每个寄存器都有明确的位域组合,我这里只展示框架,避免读者照抄却在寄存器上踩坑。调试时最好用逻辑分析仪抓一次 UART 波形,确认命令字节和返回数据是否正确。
4.3 读取与换算:从原始码到电阻值
读数据的过程相对固定:等待 DRDY 拉低,发送 RDATA 命令,连续读 3 个字节,拼成一个 24 位数据。ADS122U04 的输出是二进制补码格式,正满量程对应参考电压,负满量程对应负参考电压。拼数据时要注意符号扩展。
以比例式 RTD 测量为例,原始码和电阻值的关系是:
R_rtd = raw_code / 2^23 × R_REF / PGA
此处 PGA 是实际增益,R_REF 是外部精密参考电阻。注意,如果参考电压是由同一个激励电流流过 R_REF 产生,则这个公式成立,这是比例式测量的关键。
完整读取和换算函数可以这样写:
static int32_t ads122u04_read_raw(void) { uint8_t buf[3]; int32_t raw; /* 等待 DRDY 引脚拉低 */ while (PINB & (1 << DRDY_PIN)); /* 假设DRDY接在PBx */ uart0_putc(0x10); /* RDATA命令,按手册确认 */ buf[0] = uart0_getc(); buf[1] = uart0_getc(); buf[2] = uart0_getc(); raw = ((int32_t)buf[0] << 16) | ((int32_t)buf[1] << 8) | (int32_t)buf[2]; if (raw & 0x800000) { raw |= 0xFF000000; /* 24位补码扩展为32位 */ } return raw; }得到原始码后,RTD 电阻值计算公式:
double raw_to_rtd_resistance(int32_t raw, double rref, double pga) { return (double)raw / 8388607.0 * rref / pga; }这里 8388607 就是 2^23 - 1,正值满量程。如果是负温度,原始码为负值,直接乘除即可。
PT100 的温度换算是把电阻值换算成温度,比较常用的近似公式是:
R(T) = R0 × (1 + A×T + B×T²)
其中 R0 = 100Ω,A = 3.9083×10⁻³,B = -5.775×10⁻⁷。代入 T 范围 0 到 200℃,这个公式精度大约在 0.1℃ 以内。如果需要负温度,还要加一个三次项。认真做的项目,我建议用查表法加上插值,把国际温标表保存在 Flash 里,查表精度最高。
4.4 断线检测和软件滤波
温度传感器在现场最常出的问题就是断线。PT100 断线后,测量回路变成开路,ADC 输入会被拉到某个固定电平,通常表现为满码或负满码。我做的固件里会检查原始码是否接近正负满量程,如果连续 5 次读到的都是临界值,就判定传感器断线,直接把这个测点置为故障状态,防止把异常温度送到 HVAC 控制器。
热电偶断线就更常见了,因为热电偶线很细,震动容易断。热电偶断线时,输入阻抗极高,ADC 会收到一个接近电源轨的电压,输出满码。判断逻辑和 RTD 一样,但要特别留意冷端补偿的正常范围,不能因为冷端温度变化误报。
软件滤波我用的是滑动平均加中值滤波结合。先取 5 个点做中值滤波,剔除突刺,再对连续 20 个中值结果做平均。这种做法的好处是响应速度不算慢,又能压掉变频器产生的周期性尖峰。实测下来,滤波后的温度曲线非常平滑,稳定度能到 ±0.01℃ 以内。
5. 校准、现场问题与长期稳定性的经验
5.1 两点校准和电阻箱标定
ADC 芯片再精准,系统层面仍可能存在增益误差和零点误差。最常见的原因是 R_REF 电阻精度不足、PCB 铜箔电阻导致测量偏差、以及 ADC 偏移电压。所以出厂前最好做一次两点校准。
我的做法是准备两个高精密电阻箱,一个设置在 0℃ 对应的 100Ω,一个设置在 100℃ 对应的 138.51Ω,分别接到 RTD 通道,读原始码。按公式反算实际增益:
实际增益 = (码值差异 / 理论码值差异)
然后把这个增益系数写入固件作为校正因子。如果现场用冰水混合物做 0℃ 校准也可以,但冰水法的人为误差较大,不如电阻箱稳定。对于热电偶通道,我会用标准毫伏信号源输入几个已知电压,把电压到温度的曲线做一次平移和缩放。
校准后,整套系统在 0 到 100℃ 范围内实测误差可以控制在 ±0.05℃ 以内,远远满足 HVAC 和一般工业控制的 ±0.2℃ 到 ±0.5℃ 要求。
5.2 现场 EMC 干扰:从屏蔽到逻辑滤波
这个项目里第一个现场问题出现在一台大功率风机旁边。风机刚启动时,温度读数偶尔会跳 0.5℃ 左右,持续几百毫秒,然后恢复正常。开始我以为是传感器接插件松动,后来用示波器去抓 ADC 输入波形,发现是风机的变频器产生的共模干扰叠加在长线上。
处理分为三层:第一层是模拟输入端的 RC 电容由 1nF 加大到 10nF,把高频干扰在 ADC 前端滤掉;第二层是把热电偶和 RTD 的连接线换成屏蔽双绞线,屏蔽层只在 ADC 板一侧接地;第三层是软件中值滤波,把偶发的干扰尖峰剔除。三层做完以后,风机启停时读数波动基本看不出来了。
这里有个容易忽略的点:屏蔽层不能两端接地。两端接地会在屏蔽层上形成地环路电流,反而把干扰耦合到信号线里。我只在靠近 ADC 板的一端接地,另一端悬空或者通过一个小电容接地,实测效果最好。
5.3 长期部署后的几个细节
项目投运半年后,我复查了一次数据,发现有一个测点的温度比相邻测点一直偏高约 0.15℃。排查发现是那个 RTD 探头安装位置太靠近水管外壁导热硅脂的局部热点,不是 ADC 的问题。这个经验提醒我:精度是信号链的事情,但准确性也依赖探头安装方式,两者要分开看待。
还有一个细节是关于 R_REF 电阻的,我在前面提了好几次,但值得单独强调。R_REF 一定要选低温漂、高精度的金属膜电阻,温度系数至少在 25ppm/℃ 以下,精度 0.1% 或更好。普通 1% 贴片电阻做 R_REF,ADC 再干净,最终读数也会随着环境温度变化。这颗电阻是整个测量链里最关键的无源器件,不要省。
最后一个和 MCU 相关的经验:ATmega1280 的 UART 接收端要加简单的帧超时保护。ADS122U04 的通信偶尔会因为干扰漏掉一个字节,让后面所有字节错位。我在代码里加了同步检测:如果连续收到不合理的地址或长度,就重新发送同步字节,再初始化一次寄存器配置,这样能自动恢复通信,不需要人工重启控制器。这个自恢复逻辑放在工业设备上非常实用,因为现场维护人员不会用示波器去抓串口波形,只能靠看门狗和软件自愈。
整套项目做下来,我的体会是:ADS122U04IPWR 和 ATmega1280 的组合在温度采集领域非常成熟,前者负责把微弱模拟信号变成干净的数字量,后者负责通信、逻辑、换算和故障处理。硬要说哪个环节最难,反而不是芯片本身,而是参考电阻、传感器安装、滤波和校准这些容易被忽视的工程细节。希望这篇记录能给正在做工业或 HVAC 温度测量的人一些参考,少走几步弯路。