做温度采集项目,很多人上来就怼MCU内部ADC,接个PT100、再软件滤波,好像也能测。可一旦走进工业现场或者HVAC系统,你会发现根本不是那么回事:信号线长、干扰大、传感器类型多,要求分辨率高、自校准还得出稳定数据。最近我做完的一个工业/HVAC温度测量节点,核心方案就是用TI的ADS122U04IPWR——一颗带UART接口的24位Delta-Sigma ADC,配合瑞萨R7FA2L2094CFM(RA2L1系列,Cortex-M23内核MCU),把PT100、K型热电偶的信号统一采集进系统。这个组合看起来低调,但实际用下来非常稳,测温度这件事能做得既省事又专业。这篇文章就把整套方案从选型逻辑、硬件接法、固件配置到现场调试踩坑的过程完整写出来,给正在做温度采集或者楼宇自控项目的朋友作参考。
1. 方案概览:为什么是这个组合
1.1 先把需求拆开再选型
接手一个温度测量项目,我第一件事不是打开EDA画原理图,而是把需求一条条写清楚。工业HVAC场景里的温度采集和实验室里完全是两码事:传感器可能被拉出去几十米,处于变频器、风机、水泵旁边,供电电压波动,主板空间有限,还要考虑成本和后续维护。具体到我手上这个项目,要求大概是这样的:
- 支持PT100三线制,也支持K型热电偶,两种传感器要走同一个采集节点。
- 温度范围覆盖-30到150摄氏度,精度优于0.5摄氏度,现场有长期稳定性的要求。
- 主控和采集电路放在一个紧凑箱体里,不能为了采集温度就搞出一块很大的模拟板。
- 通信接口要简单,考虑到后续可能接入Modbus RTU总线,主控和ADC之间最好用串口这类常见接口,不要额外占用太多IO。
把这些需求摆出来,选型逻辑就很清晰了。如果只用MCU内部ADC,增益、偏置、冷端补偿、恒流源全部得自己搭。RA2L1是有12位ADC,但12位配上模拟前端,要做到0.5摄氏度精度很难,尤其量程覆盖100多度时,分辨率根本上不去。与其在模拟前端上花大功夫,不如选一颗专用ADC,把信号调理和转换一次做完。
1.2 ADS122U04IPWR 到底强在哪
ADS122U04IPWR的最大特点是“模拟前端集成度高+UART接口”。它内部集成了多路复用器、可编程增益放大器PGA、24位Delta-Sigma调制器,还有两个匹配的激励电流源IDAC,专门给PT100/PT1000提供恒流激励。传感器来的微小信号,直接进ADC内部放大转换,不需要外搭仪表放大器,也不需要外加精密恒流源。
更关键的是接口,它不走SPI、不用I2C,而是走UART。这一点在工程上很实用,主控MCU几乎都带SCI/UART外设,而且UART本身就是单端两根线,不用像SPI那样考虑时钟极性和毛刺问题。对R7FA2L2094CFM来说,UART驱动是现成的,直接用官方FSP配置好串口就行,省去了大量底层时间。
另外这颗ADC的功耗很低,供电范围宽,2.7伏到5.5伏都能工作,内部还有温度传感器和基准电压监测,用来做热电偶冷端补偿很方便。24位的分辨率配合可配置的PGA增益,既能读mV级的热电偶信号,也能读PT100经过激励电流产生的几十毫伏压降,灵活性很高。
1.3 R7FA2L2094CFM 在系统里扮演什么角色
R7FA2L2094CFM是瑞萨的RA2L1系列MCU,Arm Cortex-M23内核,主频跑48MHz。它的定位很明确:低功耗、高性价比、控制逻辑和小型协议栈全都能装下。在这个项目里,它主要负责三件事:
第一,通过UART与ADS122U04IPWR通信,下发配置命令、读取转换结果。第二,把原始码值换算成实际温度,包括PT100的线性化、热电偶的冷端补偿查找表计算。第三,跑Modbus RTU从站协议,把温度数据以标准寄存器的方式供上位机或DDC控制器读取。RA2L1的Flash和RAM对于这种任务完全够用,而且它的外设时钟配置简单,FSP图形化配置工具生成底层代码后,我只需要写应用层,开发效率高了不少。
选这个MCU还有一个原因,它在低功耗模式下依然能维持串口唤醒和定时采样,对需要做电池供电或低功耗节能的HVAC设备很有价值。虽然这个项目是用市电供电,但设计之初我也为后续变体留了余地。
2. 系统设计与硬件实现
2.1 模拟输入通道的接法(RTD和热电偶)
我这次一共规划了4路模拟输入,但并非全接同一种传感器。实际分配是这样的:
| 通道 | 传感器类型 | 接法说明 |
|---|---|---|
| CH0 | PT100 三线制 | 用于冷冻水/热水管温度 |
| CH1 | PT100 三线制 | 用于回风温度测量 |
| CH2 | K型热电偶 | 用于设备表面高温监测 |
| CH3 | 预留单端 | 备选NTC或0-10V信号 |
PT100三线制接法是这类方案里最值得讲的。原理其实不复杂:两路由IDAC输出的激励电流分别流入RTD和导线,第三根线用来作电压采样参考。因为电流源是匹配的,导线电阻带来的误差可以被抵消掉。接线上,RTD一端接IDAC1输出,另一端接AIN0正输入,同时通过另一根导线回到AIN0负输入附近的公共点;IDAC2则模拟同样的导线电阻回路。这样在AIN0正负之间测得的电压,只反映RTD本身电阻产生的压降,而不会把线缆电阻算进去。
热电偶的接法更简单,热偶正负极直接进第二个差分通道。但前提是冷端必须处于已知温度点,工程上最方便的办法就是用ADS122U04内置的温度传感器来推算冷端温度。注意,热电偶信号是微伏级别的,K型大约每摄氏度只有40微伏,所以PGA增益要选高一些,同时布线要远离数字开关信号,否则最后几位会跳得很厉害。
实操时有几个细节:IDAC电流我选的是0.5mA,因为500微安流过PT100在0摄氏度时正好产生50毫伏电压,这个量级落在PGA增益8到16的合适输入范围内。如果你用的是PT1000,可以把电流降到0.25mA,这样同样阻值范围下电压相同,功耗更小。电流太大,RTD会因为自热效应发热,造成测量偏高。
2.2 电源、基准与PCB布线
ADS122U04的模拟供电AVDD、数字供电DVDD我统一用了3.3伏,用一颗低噪声LDO从24伏现场电源降压后再供电。要注意它并不是不能5伏工作,但3.3伏可以和MCU共用逻辑电平,减少电平转换的麻烦。
基准电压方面,这颗ADC支持内部基准,也可以外部基准。对于这个项目,我直接用内部基准就行了。因为采用比例式测量思路:IDAC电流、基准电压和ADC的转换都是相对同一个参考,即使基准有温漂,PT100电阻测量的比例关系也不会变。这种“比率测量”是工业电阻测温精度的一个重要来源。
PCB布线我踩过的坑比较多,这里重点说几条:
- 模拟输入和激励电流走线尽量短,而且IN+和IN-要平行走线,减少环路面积。
- 数字UART线不要和模拟输入线平行长距离走,会使转换噪声明显增大。
- 模拟地AGND和数字地DGND在ADC下方单点连接,电源入口加磁珠和10uF+0.1uF电容组合。
- 热电偶输入可以加一个小电容做差分滤波,比如说10nF,放在靠近ADC引脚处。
还有一点容易被忽略,ADS122U04的参考电压输入引脚的滤波电容尽量靠近芯片,用1uF的X7R或C0G电容。如果外部走线长,电容小了会造成转换码值抖动。
2.3 与R7FA2L2094CFM的UART对接细节
R7FA2L2094CFM有多个SCI通道,我用的是SCI0作为UART。波特率我配成了9600,虽然这颗ADC支持更高波特率,但现场线缆如果较长,9600更稳。UART连接只要注意TXD和RXD交叉就可以,逻辑电压都是3.3伏,不需要额外电平转换。
软件上,我用FSP配置UART,设置8位数据、无校验、1位停止位。RA2系列FSP生成的UART驱动有回调函数机制,数据收进来之后放到环形缓冲里,主循环解析命令。这样读写命令都不会阻塞主流程,后续跑Modbus RTU也不会互相卡顿。
有一点需要特别提醒:ADS122U04不是一上电就自动出数据的。它上电后处于待机状态,必须先发送命令进行配置,然后发送启动转换命令,之后根据设置决定是读取单次数据还是连续数据。很多第一次用这颗芯片的人,代码里只配置了ADC寄存器,忘了发启动命令,结果读回来永远是零,这个坑后面还会详细讲。
3. 固件实现全过程
3.1 驱动初始化和寄存器配置
我在R7FA2L2094CFM上的固件分三层:UART驱动层、ADC命令层、应用换算层。UART驱动层直接由FSP生成,ADC命令层封装了Reset、读寄存器、写寄存器、发送数据等函数。
初始化流程大概是这样:
// 复位ADS122U04 adc_uart_send_cmd(CMD_RESET); delay_ms(10); // 写CONFIG0:设置PGA增益和测量通道 adc_uart_write_reg(REG_CONFIG0, 0x00); // 写CONFIG1:选择数据输出速率和转换模式 adc_uart_write_reg(REG_CONFIG1, 0x00); // 写CONFIG2:配置IDAC电流大小和使能 adc_uart_write_reg(REG_CONFIG2, 0x00); // 写CONFIG3:保留,按默认配置 adc_uart_write_reg(REG_CONFIG3, 0x00); // 启动连续转换 adc_uart_send_cmd(CMD_START);这段代码里的具体寄存器值我就不展开到底哪些bit了,手册里写得非常清楚,不同版本的片子定义略有区别。我想强调的是配置思路:先把PGA增益设成项目实际需要的值,再配置数据输出速率,最后才是IDAC电流。顺序错了,或者写寄存器后没有等待内部稳定,读回来的数据就会很奇怪。
这个项目中PGA增益我分传感器走:PT100用增益8,热电偶用增益32。速率统一用20SPS,因为温度本来就变化慢,20SPS的噪声抑制反而更好,工频干扰滤得更干净。如果需要现场快速响应,也可以提到90SPS甚至更高,但分辨率会略降,需要取舍。
3.2 启动转换与连续读数
ADS122U04支持单次转换和连续转换两种模式。我选用连续模式,省得每次都要发启动命令。代码逻辑是:
uint8_t rx_buf[3]; // 每次需要新温度值时,发送读取数据命令 adc_uart_send_cmd(CMD_RDATA); adc_uart_read_bytes(rx_buf, 3); int32_t raw = (int32_t)((rx_buf[0] << 16) | (rx_buf[1] << 8) | rx_buf[2]); if (raw & 0x800000) { raw |= 0xFF000000; // 符号扩展 }三个字节是有符号24位。由于我是连续转换模式,芯片内部的数据寄存器会被不断刷新,读数据的时机会影响结果。稳妥的做法是:发送RDATA命令后,芯片返回当前寄存器的数据快照,主控读走就行。不用太担心读到新旧交界的数据,因为UART读取本身是即时锁存的,不像SPI那样有延迟对齐问题。
读出来的24位码值换算成电压的公式是:
电压 = 码值 / 2^23 × VREF / PGA举个例子,内部基准按2.048伏算,PGA=8,码值=1638400时,对应输入电压:
电压 = 1638400 / 8388608 × 2.048 / 8 = 0.05伏这个0.05伏正好是500微安电流流过100欧姆PT100产生的电压,对应的温度就是0摄氏度。整个过程逻辑很简单,但非常有用,调试时可以先算好预期码值,再去对ADC读数,能迅速定位硬件问题。
3.3 温度换算与线性化的实际算法
PT100的电阻-温度关系不是纯线性,但工程师常用多项式或查表法处理。我的项目对精度要求中等、存储空间也够,所以采用查表加线性插值:把-50到200摄氏度的温度点每隔1摄氏度存一个电阻值,形成201个表项,每个表项两个字节,总共也就400字节左右,RA2L1完全扛得住。
实际换算步骤是:
- 根据采集到的电压反推RTD当前电阻值。
- 在查找表里找到两个邻近温度点对应的电阻值。
- 用线性插值算出当前温度。
如果你不想维护查表,也可以用Callendar-Van Dusen方程直接解,但计算量比查表大。ARM Cortex-M23做浮点运算虽然也没问题,但考虑到低功耗场景,查表加整数运算更省电。代码片段如下:
float temp; float r_rtd = voltage / IDAC_CURRENT; int idx = find_table_index(r_rtd); float t0 = table[idx].temp; float r0 = table[idx].res; float r1 = table[idx + 1].res; temp = t0 + (r_rtd - r0) / (r1 - r0);这个做法对PT100这种近似线性的传感器精度足够,尤其是-30到150摄氏度区间,误差可以控制在0.2摄氏度以内。如果做宽量程或者要做高精度计量,那就得用更高阶的多项式或分段线性拟合,原理一样,表格更密集。
3.4 热电偶冷端补偿与自校准
热电偶测温本身测的是热端和冷端的温差,冷端补偿做得不好,精度直接崩。ADS122U04内部温度传感器是接在芯片封装附近,我用它作为冷端参考。
做法分三步:
- 每隔固定周期读取内部温度传感器的码值,换算成芯片温度,作为冷端温度Tc。
- 根据热电偶类型,反查其在冷端温度Tc时对应的热电势。
- 把测量到的热端热电势加上冷端热电势,再正查总热电势对应的温度。
用到代码里就是两个查表函数:
float emf_cold = lookup_emf_by_temp(Tc, TC_TYPE_K); float emf_hot = voltage_from_adc * 1000.0f; // 转成mV float emf_total = emf_hot + emf_cold; temp = lookup_temp_by_emf(emf_total, TC_TYPE_K);注意热电偶的冷端补偿精度基本决定了整条链路的精度。内部温度传感器虽然省事,但误差有正负1到2摄氏度。如果要更准,可以用外部精密NTC贴着冷端,但这又会复杂化。我的处理是,把补偿结果做一个软件偏移,在恒温槽里校准一次,然后把校准值存进Flash。实测下来现场重复性很好。
4. 实测数据与问题排查
4.1 温度精度实测
硬件完成后,我做了一轮系统测试,用恒温槽和精密温度计对比。下面是一组有代表性的结果:
| 目标温度(摄氏度) | 精密温度计读数 | 本系统读数 | 误差 |
|---|---|---|---|
| -20 | -20.1 | -19.9 | +0.2 |
| 0 | 0.05 | 0.0 | -0.05 |
| 25 | 25.02 | 25.1 | +0.08 |
| 60 | 60.1 | 60.3 | +0.2 |
| 100 | 99.95 | 100.3 | +0.35 |
| 150 | 149.9 | 150.5 | +0.6 |
注意150摄氏度时误差变大,这是因为PT100在这个温度区间非线性更强,而我的查表表项是每1摄氏度一个点,插值带来的误差会累积。如果对这个点不满意,可以把表项加密到0.5摄氏度一档,误差会明显下降。
热电偶通道测试我用了一个小型干体炉,300摄氏度时误差约1.5摄氏度,这已经算中规中矩,主要误差来源还是冷端补偿和热电偶本身的精度等级。如果现场人员要求高精度,我会建议选用更高等级的K型热电偶,或改用PT100。
4.2 调试中遇到的典型问题
这次项目调试比较顺利,但有几个坑非常典型,单独拿出来说一下。
第一个坑是UART完全不通。上电以后,主控发命令芯片没反应,用逻辑分析仪一看,TXD是有波形出去的,但RXD永远没数据。排查之后发现问题出在复位引脚:ADS122U04有个复位/RESET引脚,我在硬件上让MCU的普通GPIO控制它,但固件初始化里没有把这个引脚拉高并延时。芯片一直处于复位状态,自然不响应任何命令。重新上电后先拉高复位脚,等10毫秒再通信,问题解决。
第二个坑是数据码值乱跳。PT100读数在稳定温度环境下,有一次发现最后十几位码值抖动非常厉害,算成温度能跳0.5摄氏度。检查后发现是我布的I2C总线从旁边走过,数字信号耦合进了模拟输入。后来把UART线移到板子的另一侧,并且给模拟输入加了差分电容,抖动就消失了。这属于纯布线教训。
第三个坑是热电偶显示温度明显偏低。K型热电偶在百米外的一台设备上,现场用表量热端电压是准的,但系统显示低了好几度。原因就是冷端温度传感器被系统电源加热了,芯片附近比实际环境温度高3摄氏度。我把冷端补偿的温度源做了软件滤波,并且把MCU和ADC通信频率降低,减少数字电路发热,最终误差控制在范围内。
4.3 常见问题速查表
最后整理一个现场排查速查表,按出现频率排序:
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| 读不到任何数据 | 复位引脚未释放;UART没有交叉连接;波特率配置不对 | 检查复位时序,示波器确认TX/RX波形 |
| 转换结果一直为0 | 未发送START命令;寄存器写失败 | 手动发起始转换命令,写寄存器后读回校验 |
| 数值跳动大 | 数字干扰;激励电流不稳 | 增加输入滤波,调整布线,检查LDO |
| PT100温度整体偏高 | IDAC电流偏大;接触电阻未完全补偿 | 校准实际电流,改用更高精度电阻 |
| 热电偶显示低几度 | 冷端补偿不准 | 做单点偏移校准或改用外部NTC |
| 数据更新过慢 | SPS设置过低;UART读取周期太长 | 提高数据输出速率,优化读取时序 |
5. 实操经验
这套方案做完,我的最大体会是:专用模拟前端芯片的价值不在于“分辨率高”这一点,而在于把复杂的模拟信号调理问题变成可配置、可复制的数字操作。用ADS122U04IPWR配合R7FA2L2094CFM,硬件的调试工作量比传统“运放+M CU+分立恒流源”方案少了至少一半,而且后期换传感器类型只需要改配置,不用改板子。
如果让我重新调整,我会把查表温度表做成外部可更新数据,通过Modbus写入Flash,这样现场校准就不用烧录程序了。另外,UART接口虽然方便,但在抗干扰能力上比RS485还差一档,如果系统距离超过三米,建议在UART后面加隔离/差分转换芯片,这个升级非常值得。
最后再分享一个小技巧:调试时永远先发一个读寄存器命令,把ADC当前的配置读回来对比。这一招能救你很多次,因为所谓“配置写不进去”往往只是你写的那条命令被噪声或时序破坏了,读回校验是成本最低的定位手段。