温度测不准的滋味,做工业自动化和暖通的人都懂。系统一直报警,生产停了,查半天数据也没找出哪的问题,最后才发现是温度采集电路的问题——基准漂了、引线长了、传感器接错了,一百个理由等着你。这些年我做过不少温度采集方案,从分立运放搭建到集成ADC,从热敏电阻到铂电阻热电偶,踩过的坑一箩筐。最近一个项目里,用TI的ADS122U04IPWR配合ST的STM32F334R8做了一套温度采集系统,用在HVAC和工业现场,整体表现很稳,这里把思路、电路、代码和问题排查完整梳理一遍,给要做类似温度测量的朋友一个可复制的参考。
这套组合的分工很明确:ADS122U04负责把传感器信号变成数字量,STM32F334R8负责配置、读取和做后续控制。ADS122U04是一颗24位Δ-Σ架构的ADC,自带UART接口,不用SPI也不用I2C,两根线就能通信,关键是它内部集成了两个精密电流源(IDAC)和可编程增益放大器(PGA),可以直接驱动PT100、PT1000这类RTD,配合冷端补偿电路也能测量热电偶,几乎把模拟前端的所有麻烦都包圆了。STM32F334R8则是一颗带浮点运算单元的Cortex-M4内核芯片,主频最高72MHz,除了常规的UART、SPI外,高分辨率定时器也是它的招牌,很适合温度采集后直接做闭环控制。如果你的项目也是工业温度监测、HVAC机组控制、设备过热保护这类场景,这篇文章可以帮你省下不少自己踩坑的时间。
1. 为什么把温度测量交给外部ADC,而不是直接用MCU内部ADC
1.1 STM32F334R8内部12位ADC在测温上的短板
STM32F334R8内部其实有一颗12位SAR型ADC,采样速率高,用于电机电流采样、电压监测这些场景绰绰有余。但用来测温度,尤其是测PT100或热电偶这类小信号,12位分辨率显然不够看。我举个具体例子:PT100在0℃时电阻为100Ω,温度系数约为0.385Ω/℃,如果把信号调理到ADC输入范围,12位量化在3.3V参考下每一步约0.8mV,对应到PT100的温度变化大约是0.2℃,这还只是理想量化误差,实际还有参考电压漂移、运放失调和布线噪声。HVAC系统里1℃误差勉强能接受,工业现场很多设备对温度要求是±0.2℃甚至±0.1℃,12位SAR ADC根本不能满足。
更麻烦的是,直接用电桥配合仪表放大器虽然也能搭建PT100调理电路,但分立方案里有太多需要配对的电阻,温漂、失调、共模抑制每一项都在考验设计者的功底。MCU内置ADC的参考电压通常是电源电压,而电源电压会随负载变化,这直接导致测量结果的基准不稳定。温度测量本质上是一个低带宽、高精度的弱信号测量任务,和电机控制那种高速、相对、强信号的场景完全不是一个需求维度,用一个高速SAR去硬扛,性能不够还平添麻烦。
1.2 24位Δ-Σ ADC与传统SAR ADC的本质区别
ADS122U04是24位Δ-Σ型ADC,内部集成了低噪声PGA,增益最高128倍,最低满量程输入可以到毫伏级别。它有4路模拟输入,两个可配置IDAC电流源,可以给RTD提供激励电流,还内置了一个供测温使用的内部温度传感器及低侧开关,用于热电偶冷端补偿时关断和切换信号非常方便。最关键的,它直接集成了UART接口,这意味着只用两根线就能完成通信,分布式测温或者布线较长的场景下,抗干扰和接线的便利性都比SPI强不少。
Delta-Sigma ADC的精度来源和SAR完全不同。它不做逐次逼近,而是以很高的速率对输入信号采样,再通过数字滤波器把带内噪声整形掉,输出一个高分辨率的码值。这个过程换来了极低的噪声和极高的有效分辨率,代价是单次转换时间较长——ADS122U04在20SPS数据速率下,单次转换约50ms,但这对温度信号来说完全不是问题,温度本身变化就是慢的,咱要的就是稳定和精度,不是速度。20SPS下24位输出码中有效分辨率大约19位,对应PT100测温时分辨率可以到0.003℃级别,这个数字对任何HVAC或者工业控温场景都绰绰有余。
有人说既然MCU内置ADC够用为什么还要外置,实际上工业现场的温度传感器往往远离控制板,信号线长、环境干扰多,内置ADC的模拟前端结构简单,没有电流源也没有完善的断线检测,一旦传感器引线氧化或断线,系统只能给出一个毫无意义的满量程读数。ADS122U04这类专用ADC把传感器激励、放大、转换、通信整合在一起,电路复杂度大幅下降,而且可以通过配置寄存器快速切换传感器类型,适配不同项目时灵活很多。
2. 硬件设计:RTD和热电偶与ADS122U04的实际接入
2.1 RTD的三种接线方式怎么选
RTD有2线、3线、4线三种接法,我全部在板子上验证过,在这里说清楚差异。2线制最简单,两根线直接接到RTD两端,IDAC电流源提供激励电流,测量RTD上的电压。缺点非常明显:导线本身有电阻,如果传感器装在几十米外,导线电阻可能有5~10Ω,对应的温度误差直接就是十几摄氏度,完全不可用。做HVAC项目时传感器线短,比如1米以内,2线还能凑合,工业现场千万别这么干。
3线制是工业现场最常用的折中方案。RTD一端接一根线,另一端接两根线,利用IDAC1和IDAC2分别提供电流流过RTD,通过测量通道分别采集两个回路的电压,在软件里做差,可以把引线电阻的大部分误差消掉。前提是三根线线阻一致,常规的三芯屏蔽线完全满足。我在项目里用的就是这个方案,实测在30米线缆下,测温误差能控制在±0.1℃以内,前提是做好屏蔽层单端接地,信号质量才有保证。
4线制最彻底,两根线用于激励电流,两根线用于测量电压,测量回路几乎不流过电流,所以线阻对测量完全零影响,但要多布一根线,成本略高。需要长线高精度测量的场合可以选。ADS122U04的IMUX寄存器支持把IDAC1和IDAC2分别路由到任意AIN引脚,这意味着4线制连接也不需要额外外围器件,直接在配置里改通道映射就行,硬件上非常省事。
2.2 比例法测PT100:激励电流与参考电阻的参数匹配
ADS122U04支持比例式RTD测量,这是我强烈推荐的方式。电路结构是这样的:IDAC输出恒流流过RTD和精密参考电阻Rref,两次测量分别得到RTD上的电压Vrtd和Rref上的电压Vref。把外部参考电阻接到REFP和REFN引脚,ADC的转换结果以Vref为基准,最终RTD电阻等于Rref乘以Vrtd/Vref的比值。这样做的好处是IDAC电流的温漂、电源电压的波动都会被比例运算消掉,不需要用绝对参考电压,省掉一颗高精度基准芯片的成本。
我给PT100选用的参数组合:IDAC电流0.5mA,Rref选择2.05kΩ,PGA增益8倍。这样Vref=0.5mA×2.05kΩ=1.025V,PGA增益8倍时满量程差分输入为1.025V/8≈128mV。PT100在400℃时电阻约247Ω,0.5mA电流下信号电压只有123.5mV,离满量程还有4%左右的余量,既不会削波也不会浪费分辨率。这里有个关键点:如果量程扩大或者缩小,增益和IDAC参数要重新匹配,原则是让最大测量温度对应的信号电压不超满量程,同时尽量让信号在量程内占满ADC的输入范围,用足有效位数。
我实际调参时试过IDAC=1mA加Rref=2.05kΩ,Vref达到2.05V,PGA增益8倍时满量程变成256mV,看似量程更大,但PT100在800℃时信号电压超过195mV,依然在范围里,这样的配置也没问题,只是PT100自热效应更明显。PT100这类铂电阻在三五毫安的电流下就会因为自热产生附加温度误差,所以保守起见我都用0.5mA,精度和自热之间取一个平衡。Rref的温漂系数尽量选低,25ppm级别的就可以,比例法虽然不敏感于Rref的绝对值误差,但它会直接影响参考电压的稳定性和ADC的标度因子,温漂还是要在可控范围内。
2.3 热电偶测量与冷端补偿的电路要点
热电偶的难点在于冷端补偿。热电偶测的是热端和冷端的温差电势,冷端温度一变,读数跟着变,如果不做补偿,测量结果在环境温度变化时漂得没法看。ADS122U04内部自带温度传感器,可以读取芯片结温,把它当作冷端温度。把热电偶接到模拟输入通道,比如AIN0接正极、AIN1接负极,同时把芯片放在靠近热电偶接线端子的位置,让冷端温度尽量接近芯片结温,这样补偿才准。
热电偶信号是毫伏级,K型热电偶在1000℃时约41mV,ADS122U04的PGA增益设32倍比较合适。如果用内部2.048V参考,增益32时满量程输入约64mV,既覆盖K型热电偶全量程(最高约55mV),又保留了足够的余量,分辨率也够。软件上,读取热电偶电压后,再读芯片内部温度作为冷端温度,查找热电偶分度表得到冷端对应的等效电动势,加到测量电压上,再反查热端温度。这一套流程ADS122U04的数据手册里有公式,TI也提供了NIST多项式系数,直接拿过来用就可以。
2.4 供电和PCB布局的实战建议
ADS122U04的模拟供电AVDD和数字供电DVDD需要单独去耦,至少每个引脚一颗1μF陶瓷电容,靠近引脚放置,另外在电源入口再加一颗10μF电解电容吸收低频波动。STM32F334R8的参考地必须和ADC的地星形连接,避免数字噪声通过地平面串到模拟电路。模拟输入端的RC滤波也要加,R取100Ω,C取1nF,转折频率约1.6MHz,能滤掉大部分高频干扰,又不影响温度信号的幅值。
电源方案上,我建议AVDD和DVDD都从3.3V线性稳压器输出,但中间加一个磁珠或一个小电阻分隔开,防止MCU的数字开关噪声污染ADC的模拟电源。ADS122U04在UART通信时会产生一定的数字开关噪声,这个噪声如果耦合到模拟输入,显示的读数就会在最后几位上跳动。按上述方式分电源、分地方案做下来的板子,我实测在电源纹波±30mV的恶劣输入下,温度读数波动不超过0.02℃,比共用一个电源时明显稳定。
3. 基于STM32F334R8的软件实现
3.1 UART通信与DRDY中断的正确处理
我第一次用ADS122U04时,在通信上卡了整整一下午,原因说出来很丢人:这颗芯片上电后默认波特率是9600,而我初始化STM32的USART时习惯性写了115200,怎么发命令都没应答,最后查资料才反应过来。所以第一步一定要把USART配成9600、8位数据位、无校验、1位停止位,等芯片正常通信后再考虑提高波特率。ADS122U04支持最高4Mbps,但实际做温度采集时通信量不大,9600完全够用,低速换来的抗干扰能力反而更适合工业现场。
DRDY引脚的处理也很关键。这颗芯片转换完成后会把DRDY拉低,我在代码里用外部中断检测这个边沿,而不是轮询。轮询的问题在于:如果主循环里还有其他耗时操作,比如处理PID算法或者刷新LCD,很容易漏掉DRDY的瞬间变化,导致丢失数据或者重复读取旧数据。把DRDY接到STM32的一个GPIO外部中断引脚,下降沿触发,在中断服务函数里只置一个标志位,主循环检测到标志后再启动RDATA读取,逻辑干净,也不会因为中断处理太长影响其他实时任务。
3.2 寄存器配置的完整代码解析
ADS122U04共有6个寄存器,全部8位,初始化时通过WREG命令一次性写入。WREG命令的格式是命令字节后紧跟数据字节,命令字节的构造规则是:0x40加上起始寄存器地址左移2位,再加上寄存器数量减1。因为要从寄存器0写6个寄存器,所以命令字节是0x45。下面是我在项目里实际用的初始化代码:
uint8_t regs[6]; // reg0: AIN0与AIN1组成差分测量通道,参考电压选择外部REFP/REFN // reg1: PGA增益=8,正常滤波器 // reg2: 数据速率=20SPS,连续转换模式 // reg3: IDAC电流=0.5mA,低侧开关关闭 // reg4: IDAC1输出到AIN2,IDAC2输出到AIN3 // reg5: 禁止比较器,数据格式=有符号24位 regs[0] = 0x01; regs[1] = 0x03; regs[2] = 0x00; regs[3] = 0x01; regs[4] = 0x3C; regs[5] = 0x00; uint8_t wreg_cmd[7]; wreg_cmd[0] = 0x45; // 写寄存器,从寄存器0开始,共6个 memcpy(&wreg_cmd[1], regs, 6); HAL_UART_Transmit(&huart1, wreg_cmd, 7, 100); HAL_Delay(10); // 等待寄存器写入完成 uint8_t start_cmd = 0x08; // START命令 HAL_UART_Transmit(&huart1, &start_cmd, 1, 100);这里有个UART时序的细节:WREG命令的所有字节之间必须保持一定间隔,不能像普通UART发送那样连续无间隔地把7个字节一口气打完,否则ADS122U04把后续数据当作新命令处理。我给每个字节之间加了约100μs的延时,实测很稳。如果你用的是DMA加环形缓冲区,也要在软件上保证帧间隔,这一点最容易踩坑。
读取转换结果的RDATA命令时序也类似:
uint8_t rdata_cmd = 0x10; uint8_t raw[3]; HAL_UART_Transmit(&huart1, &rdata_cmd, 1, 10); HAL_UART_Receive(&huart1, raw, 3, 100); // 将3字节拼接成有符号24位整数 int32_t code = (raw[0] << 16) | (raw[1] << 8) | raw[2]; if (code & 0x800000) { code |= 0xFF000000; // 符号扩展 } float ratio = (float)code / 8388608.0f; // 除以2^23 float r_rtd = ratio * REF_RESISTOR;这里的REF_RESISTOR是我们硬件上选的2.05kΩ精密电阻,ratio就是ADC码值与参考电压的比值。STM32F334R8带FPU,这些浮点计算开销极小,一次温度换算是微秒级,完全不用担心性能。
3.3 RTD温度换算:从原始码到摄氏度
拿到RTD电阻值后,下一步就是把它换算成温度。PT100的电阻-温度关系在0℃以上可以用简化二次方程描述:
Rt = R0 × (1 + A×t + B×t²)其中A=3.9083×10⁻³,B=-5.775×10⁻⁷。这是一个标准二次方程,用求根公式可以直接解出温度t:
float pt100_res_to_temp(float r) { float r0 = 100.0f; float A = 3.9083e-3f; float B = -5.775e-7f; float delta = (r / r0 - 1.0f); float disc = A * A - 4.0f * B * (-delta); if (disc < 0.0f) return -999.0f; // 输入非法 float t = (-A + sqrtf(disc)) / (2.0f * B); return t; }注意这个公式在0℃以上精度足够,0℃以下因为材料特性变得更复杂,需要用包含C系数的三次方程。我在HVAC项目里基本都在0℃以上工作,用二次方程就够了。如果做冷库或者低温工业设备,建议把Callendar-Van Dusen完整公式实现进去,或者直接用查表法。
另外一个常见的坑是比例法计算时浮点运算带来的温度毛刺。我做了一批连续测温数据,发现偶尔会出现一个跳变0.1℃的野值,排查后发现是ADC码值低位数位的随机抖动在低温度段放大了相对误差。解决办法是加一个简单的一阶低通滤波,比如filtered = filtered + (new - filtered) * 0.2,或者取连续4次采样的中位值。温度信号本来就慢,滤波不会损失有效响应速度,但能显著提升数字的稳定性。
3.4 热电偶查表与多项式换算
热电偶的温度换算是另一种玩法。标准做法是先根据测量电压和冷端温度,计算出热端电动势,再用分度表反查温度。ADS122U04内部温度传感器可以直接读到芯片结温,但我建议在冷端补偿的精度要求超过±1℃时,在冷端位置外接一个NTC或PT100做更准的冷端测量,因为芯片结温和实际连接器端子温度之间还是有几度的差值,这个差值最终会直接变成测量误差。
分度表查表法适合任何类型的热电偶,代码结构简单:把NIST分度表按温度步长存储,温度换算时先在表中找到电压所在区间,再线性插值。K型热电偶从-200℃到1370℃,如果按5℃一个间隔存,大约315个点,每个点存一个浮点值约1.2KB,STM32F334R8的64KB Flash完全放得下。如果不想维护查表,也可以直接用NIST提供的逆函数多项式系数,一组多项式就能在-200℃到1370℃范围内把电压换成温度,拟合误差小于0.05℃,比查表还节省存储空间。
我在代码里选了多项式法,因为它代码短而且没有插值毛刺。TI的手册和网上开源的coefficient文件都是现成的,直接用就行,但要注意热电偶分度表每个温度区间的系数不同,代码里一定要做温度区间的正确选择,我刚开始就是忽略了低温区间和高温区间用错了多项式,结果在500℃以上出现了明显的非线性偏差,折腾半天才排查出来。
4. 实测数据、精度标定与问题排查
4.1 实验室实测数据与精度评估
我在实验室用恒温槽和六位半万用表做了比对,测试了PT100在0℃到150℃范围内的表现,也让整个系统连续跑了一个月的老化测试。0℃时用冰水混合物,系统读数在-0.03℃到0.02℃之间跳动;50℃时恒温槽设定值与系统读数相差0.04℃;100℃时误差0.08℃;150℃时误差0.12℃。误差随温度升高而增大,这个趋势说明有很大一部分误差来自PT100的非线性和我用的简化二次方程在高温端精度下降,而不是ADC本身的能力限制。
对HVAC应用来说,±0.1℃精度已经算很奢侈,但工业加热设备要求高,我这边又做了一步优化:在软件里加了两点标定。用精密电阻箱模拟PT100在0℃和100℃的阻值,采集对应的ADC原始码,算出每路的零点偏移和增益系数,然后对测量结果做线性修正。标定之后,实测在0~300℃范围内误差可以保持在±0.05℃以内,这个水平对于绝大多数控温场景都够用了。
标定要注意的一点是:必须在系统完成所有滤波和换算之后做,标定数据只对同一套软硬件有效。如果改了PGA增益、换了参考电阻或者改了采样速率,标定系数要重新计算,因为这些参数的改变会影响ADC码位和温度换算的比例因子。另外,PT100传感器本身也有误差等级,Class A的PT100允许±(0.15+0.002×|t|)℃的偏差,所以系统标定前的传感器固有误差要先测出来,不然标定的意义就被传感器误差吃掉了。
4.2 现场干扰的解决实录
现场干扰是我这次遇到的最大问题。最初的板子和现场设备共地,电机一启动,读出的温度值直接跳了2℃,持续一两秒才恢复。排查过程很有意思:我把示波器探头直接点在ADC输入脚上,看到的是正常的毫伏级信号,但温度数据就是跳,说明干扰不是直接从输入进来,而是通过地线耦合的。电机启动时大电流在公共地上造成电压差,这个电压差直接抬高了ADC的模拟地参考点,相当于在差分输入上加了一个共模电压,虽然ADC对共模有一定抑制能力,但PGA高增益下共模抑制比急速下降,最终表现在数据上就是大幅跳变。
解决的办法做了三层。第一层在模拟输入前端加RC低通滤波,R=100Ω、C=1nF,把高频干扰挡在外面;第二层把模拟地和数字地分开铺铜,最后在电源入口处单点汇合,切断MCU数字电路和ADC模拟电路之间的地环路;第三层最彻底,在MCU和ADS122U04之间加了数字隔离器,UART信号通过隔离器后,地环路被物理切断。实测这三层全部实施后,电机频繁启停现场连续运行一周,温度读数纹丝不动,只在数据末尾一位跳动,相当于0.005℃的对地噪声水平。
4.3 常见问题速查表
这里整理我在实际调试过程中遇到的一些典型问题,都带着血泪教训:
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| 温度读数一直满量程 | 传感器接反或断路,IDAC电流没建立 | 检查RTD两端是否有电流,确认传感器接线极性 |
| 读数随电源电压变化 | 用了绝对电压方式而不是比例方式 | 改成Rref比例接线法,参考电压接REFP/REFN |
| 读数跳动超过0.5℃ | 干扰严重或PGA增益过高 | 加RC滤波,降低增益并增大IDAC电流重新匹配 |
| 通信失败,DRDY永不拉低 | START命令未发、波特率不匹配 | 确认初始化用9600波特率,发送START命令 |
| 热电偶读数在室温附近漂移 | 冷端补偿没做或冷端温度不准 | 开启内部温度传感器并做补偿,让芯片靠近接线端子 |
| 多路传感器读数互相干扰 | IDAC电流源配置了同一个输出引脚 | 检查IMUX寄存器,避免两个电流源共用通道 |
最后一条坑最深。我当时配了4路传感器,通道A的读数变化会带动通道B跟着变,折腾了好久,最后拿万用表量引脚电流才发现是IMUX寄存器把两个IDAC都配置到了同一个输出引脚,两个通道的激励电流串在一起了。这类配置问题在数据手册里写得很清楚,但没实际遇到之前,根本想不到会这么巧。
4.4 芯片选型的横向对比与扩展建议
如果你的项目是HVAC控制器,传感器数量2~4路,成本敏感,ADS122U04加STM32F334R8是一个非常合适的组合。前者一颗就能接多路RTD,后者浮点运算能力强,做PID控制和变频输出都富余。但如果需要同时采集几十路温度,比如大型楼宇自控系统,一颗只有4个模拟输入明显不够,建议用多片ADS122U04挂同一组UART总线,半双工模式下支持多设备寻址,或者用模拟开关扩展输入。
横向来看,ADS122U04同系列还有不带UART的ADS1220,引脚兼容但接口是SPI,如果MCU的SPI空闲,用SPI版本可能更方便;更高端的ADS125系是24位带SPI的,但多通道配置复杂,不适合简单测温。ADS122U04的优势恰恰在于内置IDAC和UART,两颗芯片加几个无源器件就是一套完整的测温前端,无需任何外部放大器或基准,对中小型项目的开发效率提升非常明显。
STM32F334R8这边,同系列F334有两种Flash容量,R8是64KB,R6是32KB,如果你的代码里还要加Modbus协议栈和显示驱动,建议直接选R8,省得后面挤Flash。它和F301、F302系列的引脚不是完全兼容的,替换时不能盲目套原理图,特别是高分辨率定时器的引脚映射要重新核对,这个我有过惨痛教训。
以我这些年的习惯,温度采集系统的设计要把可靠性放在第一位,其次才是成本和精度。ADS122U04配上STM32F334R8这套方案,最大的感受是省心——模拟前端的坑大部分被这颗ADC挡掉了,而MCU又留下了足够的算力和外设余量。最后分享一个小技巧:到量程边界时,不要一味靠增大PGA增益去追分辨率,因为噪声和信号是一起放大的,不如把激励电流和参考电阻重新匹配,让信号尽量占满ADC的输入范围,这样既稳又准。温度测量没有太多花活,把比例测量、冷端补偿、滤波布线和标定这几件事做扎实,现场基本不会出大问题。