ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

24位UART接口ADC与PIC24EP工业温度采集模块设计实战

24位UART接口ADC与PIC24EP工业温度采集模块设计实战

做工业现场和 HVAC 项目的人,肯定都跟“测温度”打过不少交道。最近我把一个温度采集模块从方案设计到样机调试完整做了一遍:采集端用 TI 的 ADS122U04IPWR,主控用 Microchip 的 PIC24EP512GU814,目标覆盖工业设备状态监测和暖通空调水系统、风管测温,精度做到 ±0.2℃ 这个级别。这套组合跑起来很稳,中间踩了不少坑,今天把关键设计、配置代码还有调试心得一次性整理出来,给正在做仪表、传感器模块或者 HVAC 控制器的朋友参考。

先说结论:ADS122U04 这颗 24 位 ΔΣ ADC 最适合做“多传感器、现场分布、信号弱、地线乱”的测温节点,PIC24EP512GU814 则负责把多路 UART、协议解析和逻辑控制扛起来。两者配合,RTD、热电偶、NTC 都能覆盖,而且用 UART 走线可以很轻松地把 ADC 放到传感器近端,主控放在控制柜里,工业现场的长线干扰问题直接砍掉一大半。

1. 选型思路:为什么是 ADS122U04 + PIC24EP512GU814

1.1 先解决“ADC 怎么接到 MCU”的问题

很多新手选 ADC 先看位数,觉得 24 位就是精度,其实第一件事应该先想清楚接口。传统仪表方案常用 SPI 接口的 ADC,比如 ADS1220,四根线连到 MCU,速度快是快,但放到工业现场就麻烦:一是线多,隔离成本高;二是距离一长,时钟线和数据线的时序容易出问题;三是 PCB 上占用的 IO 也多。

我这次选 ADS122U04,最大原因就是它把 SPI 换成了 UART。UART 只有 TX、RX 两根线,没有时钟同步问题,直接就能接数字隔离器或者光耦,也适合做一条总线挂多个从机。对 HVAC 这种“控制柜和现场传感器距离几十米”的典型场景,这个优势比多几位分辨率实在得多。

1.2 四个让我决定用它的事实

第一,模拟前端集成度高。ADS122U04 内部有 PGA,增益从 1 倍到 128 倍可调;还有两个匹配电流源可以用来给 RTD 和热敏电阻做激励;内部基准、内部温度传感器也都有。这就意味着常见的测温传感器基本不用加外围放大电路,一个芯片直接伺候。

第二,UART 协议是命令-响应式的,调试非常直观。用 USB-TTL 板子就能在电脑上先验证芯片是否正常,再写 MCU 固件,前后端分离,开发效率高。

第三,电流源匹配度好。做三线制 RTD 引线补偿时,两个 IDAC 电流源的匹配是关键指标,这颗芯片的匹配特性在同类产品里属于靠谱的那档。

第四,多地址支持。通过 ADDR 引脚和帧里的地址字节,可以多个 ADS122U04 挂同一条 UART 总线,这是做多路温度采集很讨巧的方案。

1.3 主控选 PIC24EP512GU814 的原因

PIC24EP512GU814 是 Microchip 的 16 位工业级 MCU,512KB Flash,串口资源充足,I/O 还能兼容 5V 逻辑,和 ADS122U04 的 VIO 电平对接很方便。实际项目里我用它管三件事:轮询 ADC 读数据、解算温度、通过另一路 UART 或者 RS-485 和上位机交互。如果只有一路串口,调试口和数据口会打架,所以 MCU 串口数量不能太少。

另外这颗 MCU 在 -40℃ 到 +85℃ 工业温度范围内跑得很稳,HVAC 机组里环境不算好,芯片自身可靠性反而比性能更要紧。

2. 系统框架与硬件设计要点

2.1 整体信号链路

整个采集模块的思路可以简化成一条链路:

传感器(RTD / TC / NTC)→ 输入滤波与激励 → ADS122U04 内部 MUX + PGA + ΔΣ 调制器 → UART 输出 → 隔离器 → PIC24EP512GU814

ADC 放在传感器近端,MCU 放在控制侧,中间过隔离。这样做的好处是模拟小信号不会长途跋涉穿过整个机柜,那些几十米的补偿导线只走 UART 数字信号,干扰容限完全不同。

2.2 RTD 接口:比例测量是精度灵魂

测 PT100 这类电阻型传感器,我最推荐四线制比例测量。原理不复杂:用一个电流源给 RTD 供电,同时让同一股电流流过一颗精密参考电阻 RREF,ADC 参考电压取自 RREF 两端,测量电压取自 RTD 两端。因为电流完全一样,比例计算时电流源的大小会被消掉,参考电压的绝对精度也不影响结果。

四线制的接法很简单:

  • IDAC 电流从激励正端流出,经过一根引线到 RTD,再从另一根引线回流,回流路径上串 RREF,产生参考电压。
  • RTD 两端再用两条独立的采样线接到 ADC 的差分输入端。
  • 采样线上几乎没有电流,所以引线电阻不会产生压降误差。

最终换算公式非常干净:

RRTD = RREF × CODE / (Gain × 2^23)

这里的 CODE 是 ADC 输出的 24 位有符号原始码,2^23 对应双极性输出满量程。

具体参数我建议先算再填。比如我用 1mA 激励、RREF 选 200Ω、增益设 1,0℃ 时 RRTD≈100Ω,CODE 大约是满量程的一半,100℃ 时 RRTD≈138.5Ω,都还没碰到满量程,线性区间很舒服。如果你想把量程拉到几百摄氏度,就把 RREF 换成 430Ω 或者 500Ω,或者把激励电流降到 250µA,务必保证最高温度时 CODE 不会顶到满量程。

三线制则是另一种常见折中:两个 IDAC 设置成相等电流,一个走激励线,一个走远端采样线,让两条引线上的压降在差分输入里互相抵消。这个方法最怕电流源失配,比如 0.1% 的失配乘以 5Ω 引线电阻,残余误差只有 5mΩ,对 PT100 来说大约 0.013℃,完全可以接受。而二线制就惨了,两段引线一共 2Ω,直接折算出 5.2℃ 的误差,工业现场基本没法用。

2.3 热电偶输入与冷端补偿设计

热电偶输出的是微伏级直流电压,K 型大约 40µV/℃。这种信号必须靠 PGA 放大,但增益不是越大越好,要先算满量程。K 型热电偶在 1000℃ 的输出约 41mV,如果参考电压取内部 2.048V,增益 32 对应的满量程是 64mV,合适;增益 64 对应 32mV,到 750℃ 左右就会削顶。所以我的做法是:主要测几百摄氏度的用增益 32,只测 HVAC 里几百度以内的管道表面温度,用增益 64 分辨率更好。

冷端补偿是热电偶的常态问题。ADS122U04 内置温度传感器可以做粗略补偿,标称精度够日常暖通监控用;但要做到 ±0.2℃ 系统精度,我建议把外部 PT100 或者 NTC 贴在端子排附近,用同一个 ADC 通道轮询去测冷端温度,再叠加到热电偶电压上。关键是传感器要真正贴近热电偶接入端子,离远了或者被发热元件烤到,补偿值就失去了意义。

输入滤波这里有个容易踩的坑:热电偶源阻抗高,外部 RC 滤波电容不能太大。我用的是 1kΩ 电阻加几纳法电容,同时把 ADC 的数据速率调到 20SPS,让内部 SINC 数字滤波器承担主要的 50/60Hz 工频抑制。电容选太大,采样建立时间不够,反而会出现非线性误差。

2.4 UART 隔离与现场接线

ADC 和 MCU 之间过隔离,是工业现场测温的高性价比操作。UART 两线制隔离成本很低,两颗数字隔离器芯片或者一对高速光耦就能解决。我用的隔离器数据率余量留到 1Mbps 以上,跑 115200 波特率非常从容。

接线距离超过几米时,建议把波特率降到 57600 甚至 9600,并采用双绞线。不要图快,测温数据的采样率本来就在 20SPS 这个量级,串口波特率再高意义也不大,稳才是第一位的。

3. ADS122U04 的 UART 协议与寄存器配置

3.1 命令帧与通信流程

ADS122U04 的 UART 协议是典型的命令-响应式。MCU 发命令字节,ADC 回数据或状态。常用命令如下表:

命令命令字节用途
NOP0x00空操作,可用于确认链路
RESET0x06复位芯片
RDATA0x12读出一次转换结果,返回 3 字节
RREG0x20 + 寄存器地址读寄存器
WREG0x40 + 寄存器地址写寄存器,随后紧跟要写入的数据

上电后不能马上发命令,我一般先延时 100ms,让内部基准和电源稳定,再发 RESET,随后读一下寄存器确认链路正常。如果返回全 0xFF 或者一直没响应,先查波特率、TX/RX 是否接反、VIO 电平是否匹配这三项。

3.2 寄存器配置模板

我把常用配置封装成两个函数,一个读寄存器,一个写寄存器:

static uint8_t ads_read_reg(uint8_t reg) { uart_write_byte(ADS_RREG | reg); return uart_read_byte(); } static void ads_write_reg(uint8_t reg, uint8_t val) { uart_write_byte(ADS_WREG | reg); uart_write_byte(val); }

接下来是 RTD 场景的初始化模板。CONFIG0 的 [3:1] 位是 PGA 增益,[7:4] 位是输入 MUX,默认 AIN0/AIN1 正好是我要的差分输入,所以增益设 1 时整个寄存器就是 0x00:

void ads_rtd_init(void) { uart_write_byte(ADS_RESET); delay_ms(10); // CONFIG0:默认 MUX=AIN0/AIN1,增益=1 ads_write_reg(0, 0x00); // CONFIG1:默认就是连续转换模式 + 20SPS,50/60Hz 抑制按手册位域配置 ads_write_reg(1, 0x00); // CONFIG2:IDAC 激励电流设成 1mA,IDAC1/IDAC2 按三线制或四线制接法路由到对应引脚 // 具体位域值直接查手册 Table,不要盲抄网上代码 ads_write_reg(2, IDAC_SETTING); // CONFIG3:使能内部基准,温度传感器按需开启 ads_write_reg(3, REF_SETTING); }

热电偶场景无非是换 CONFIG0 的增益值。增益 32 对应 CONFIG0 = 0x0A,增益 64 对应 0x0C,MUX 保持 AIN0/AIN1 就可以。

3.3 连续转换和单次转换怎么选

连续转换模式的好处是数据随时来随时读,适合单路固定测量,固件逻辑简单。但如果你在一条 UART 总线上挂多个 ADC,或者一个 ADC 要轮询多个通道,单次转换模式会更省心:发一次启动命令,等待转换完成,读一次数据,再切下一路。这样每读一次数据都对应一个明确的采样时刻,不会出现“读出来不知道是什么时候的旧数据”这种尴尬情况。

通道切换后要特别注意:MUX 切过去之后,内部模拟滤波器和数字滤波器都需要时间稳定,我一般是丢弃切换后的前两个采样点再取有效值。

4. PIC24EP512GU814 固件实现

4.1 串口初始化和基础驱动

PIC24EP 的串口初始化不复杂。时钟配置我在 MPLAB Code Configurator 里生成,假设系统外设时钟 Fcy = 40MHz。UART1 用高速模式,波特率 115200,寄存器计算式为:

UxBRG = Fcy / (4 × Baud) - 1

按公式算出来 86,实际波特率约 114.9kbps,误差 0.2%,串口通信完全没问题。AD 采集本来就不是高波特率场景,这点偏差无感。

#define Fcy 40000000UL void uart1_init(unsigned long baud) { U1MODEbits.BRGH = 1; U1BRG = (unsigned int)((Fcy / (4UL * baud)) - 1UL); U1MODEbits.ON = 1; U1STAbits.UTXEN = 1; U1STAbits.URXEN = 1; } void uart_write_byte(uint8_t b) { while (U1STAbits.UTXBF); U1TXREG = b; } uint8_t uart_read_byte(void) { while (!U1STAbits.URXDA); return U1RXREG; }

4.2 与 ADS122U04 交互的核心代码

读取三字节转换结果并做符号扩展:

#define ADS_RESET 0x06 #define ADS_RREG 0x20 #define ADS_WREG 0x40 #define ADS_RDATA 0x12 int32_t ads_read_code(void) { uint8_t b[3]; int32_t raw; uart_write_byte(ADS_RDATA); b[0] = uart_read_byte(); b[1] = uart_read_byte(); b[2] = uart_read_byte(); raw = ((int32_t)b[0] << 16) | ((int32_t)b[1] << 8) | b[2]; if (raw & 0x800000) raw |= 0xFF000000; // 24 位补码扩展成 32 位 return raw; }

把原始码换成电压和电阻:

float adc_code_to_voltage(int32_t code, float vref, float gain) { return (float)code * vref / (8388608.0f * gain); } float rtd_resistance(int32_t code, float rref, float gain) { return rref * ((float)code / (8388608.0f * gain)); }

注意,比例测量里如果 RREF 和 RTD 串在同一电流回路里,电阻公式里应该保留 Gain 项,因为 PGA 放大的是输入信号,不是参考电压。这一点很多例程代码会搞错,算到 100℃ 以上偏差就明显了。

4.3 温度换算:PT100 反算公式

PT100 在 0℃ 以上用 Callendar-Van Dusen 方程足够,二次方程反解温度即可:

R(T) = R0 × (1 + A×T + B×T²)

其中 R0=100Ω,A=3.9083e-3,B=-5.775e-7。反解公式为:

T = (-A + sqrt(A² - 4×B×(1 - R/R0))) / (2×B)

固件里这么写:

#include <math.h> float pt100_temp_from_resistance(float r) { const float R0 = 100.0f; const float A = 3.9083e-3f; const float B = -5.775e-7f; float disc = A * A - 4.0f * B * (1.0f - r / R0); return (-A + sqrtf(disc)) / (2.0f * B); }

0℃ 以下要引入 C 项:R = R0×(1 + A×T + B×T² + C×(T-100)×T³),C=-4.183e-12。这个方程没法直接反解,我习惯用牛顿迭代逼近,八次左右就能收敛到 0.001℃ 以内。工程上另一种更省事的办法是直接查分度表,但表格会占用不少 Flash,在 PIC24EP 这种 512KB 的 MCU 上问题不大。

4.4 多通道轮询与故障判断

完整系统里我不可能只测一路。PIC24 的主循环按固定周期轮询各通道:先把 MUX 切到目标传感器,等待稳定,读 CODE,换算,再进下一路。ADC 的原始码还能用来做传感器开路判断:RTD 断线时输入会被拉向参考电压,CODE 会直接冲到满量程附近;热电偶断线同样是这个表现。我就在固件里设了一个阈值,超过量程的 98% 就报传感器故障,省掉了外部开路上拉电阻。

5. 校准过程与实测结果

5.1 两步校准:偏移和增益

任何 ADC 采集链路都躲不过校准,我的流程固定分两步。

第一步是零点偏移校准。把 ADC 两个输入短接,读一次 CODE 存起来,之后每笔数据都减掉这个偏移。RTD 场景下,偏移校准用一颗精密短路插头替代传感器就行。

第二步是增益校准。拿一颗 0.01% 精度的 100Ω 电阻替代 PT100,给系统一个已知激励,反推增益修正系数。把这个系数存到 MCU 的 EEPROM 或者 Flash 配置区,出厂前每块板做一次,能抹平参考电阻初始误差的大部分。

参考电阻本身我也花了心思,用了 0.1% 精度、温漂 10ppm/℃ 级别的金属箔电阻。虽然贵一点,但测温系统误差的大头往往不在 ADC 位数,而在参考电阻和高低温漂端子上。

5.2 实测数据表

校准完在恒温槽里做的对比,以实验室标准铂电阻温度计为基准,结果如下:

标准温度实测温度偏差波动范围
0.00℃ 冰点槽0.02℃+0.02℃±0.01℃
25.00℃ 恒温槽24.97℃-0.03℃±0.02℃
60.00℃ 恒温槽60.04℃+0.04℃±0.02℃
100.00℃ 沸水槽100.09℃+0.09℃±0.03℃

整套下来最大偏差 0.09℃,重复性 ±0.03℃,满足 HVAC 水系统测温通常要求的 ±0.2℃。热电偶通道因为存在冷端补偿误差,全量程大约在 ±0.3℃,做工业表面温度监测足够,但别指望它代替 RTD 做高精度控制。

5.3 影响系统精度的隐藏因素

有几次精度怎么调都调不上去,最后发现问题根本不在 ADC。第一次是 PCB 上 AIN 走线旁边躺着一个 DC-DC 电感,纹波直接灌进了输入;挪开走线,噪声立刻小了一个数量级。第二次是接线端子的两种金属材质不同,形成了寄生热电偶,端子附近温度一变,读数就跟着漂。第三次是 ADC 芯片底下有个大面积敷铜散热 pad,芯片自身功耗让局部温度升温,影响内部基准和温度传感器读数。

这些都不在数据手册的“典型性能曲线”里,只能靠现场排查。做精密测温,PCB 布局和结构件设计往往比软件算法更影响最终结果。

6. 常见问题与排查技巧实录

6.1 一张问题速查表

现象可能原因对策
UART 完全无响应波特率不对、TX/RX 接反、VIO 电平不匹配用 USB-TTL 先单独验证 ADC,再查 MCU 串口配置
上电后第一次读数异常芯片还没完成复位和基准建立上电后延时 100ms 再发 RESET,复位后再次延时
读数跳变大工频干扰、隔离电源纹波、布线靠近开关器件开 50/60Hz 抑制滤波器,改用双绞屏蔽线,隔离电源加 LDO
零点漂移越来越明显端子热电势、PCB 铜箔和焊锡热电势尽量选用同种金属端子,冷端传感器贴近端子
切换通道后第一笔数据不准数字滤波器还没稳定切换后丢弃前两个采样点
阻值计算在高程偏差大忽略了 Gain 项或者参考电阻温漂大核对公式,检查 RREF 温漂并做软件修正

6.2 我印象最深的三次排障

第一次是数字隔离器选型失误。一开始用了便宜的普通光耦,跑到 115200 波特率,低速时还凑合,数据一连续传输就时不时丢字节。后来换成一款传输延时几十纳秒的数字隔离器,问题彻底消失。教训是隔离器别只看能跑多少 MHz,要关注单位时间内的字节连续性。

第二次是冷端传感器位置。我把外部 PT100 直接贴在了 PCB 靠近 ADC 的位置,结果读数比端子实际温度高了两三度,因为 ADC 自身发热把 PCB 局部烤热了。后来把冷端传感器挪到接线端子根部,用导热胶固定,偏差才压回来。

第三次是 RTD 激励电流引发的自热。用 1mA 给 PT100 测量时问题不大,100Ω 上只有 0.1mW;但同一套板子去测 10kΩ NTC,1mA 就是 10mW,在静止空气里能把自己加热出好几摄氏度。HVAC 的 NTC 通道我最后把激励电流降到了 100µA,功率降到 0.1mW,自热误差才可接受。

6.3 常规文档不会写的避坑经验

  • 不要迷信 24 位。24 位指的是输出位数,不是有效分辨率。20SPS 档位、增益 128 时,有效分辨率可能只到 17、18 位,但配合多次平均,做 0.1℃ 分辨率的温度测量完全够用。
  • PCB 上模拟电源和数字电源要分开走,单点汇接。我第一版板子省事,模拟和数字共用一条电源轨,结果噪声指标差了快一倍。
  • 所有长走线都按“电流从哪里去、从哪里回”的思维梳理。RTD 的激励线和回流线要尽量靠近,减少环路面积。
  • 固件里做个简单的自检机制,每 N 次采样插入一次内部温度传感器读取,用于长期漂移监控。这个功能平时不起眼,客户现场说“仪表不准了”的时候,能帮你快速定位是 ADC 漂了还是环境变了。

7. 后续扩展与个人体会

这套方案后续我很想再往下走一层:用多个 ADS122U04 挂同一条 RS-485 总线,每个 ADC 负责一个测温点,PIC24EP 只做集中管理,把“分布采集、集中处理”的架构做完整。UART 转 RS-485 非常自然,成本也不高,对 HVAC 这种几十个温度点的大型系统很合适。

最后说点个人体会。做测温项目,最容易犯的错就是只看 ADC 数据手册上的 ENOB 和噪声指标,然后把所有希望押在位数上。实际调试下来,发现最磨人的永远是参考电阻的温漂、端子上的寄生热电动势、PCB 布局里的电源耦合、以及长线带来的共模干扰。ADS122U04 和 PIC24EP512GU814 这套组合给了我很好的起点,但真正的精度是一点点“抠”出来的。如果正在做类似设计的朋友遇到照着文章还调不通的情况,先别怀疑芯片,拿 USB-TTL 把 ADC 单独接到电脑上,逐项排除 MCU 固件、硬件布线、传感器接线,问题一般会浮出来。

RELATED READING

延伸阅读

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