去年调一套空调箱控制器的时候,我被一个现象卡了很久:控制板上PCB的温度才四十多度,功率管的壳体却已经将近九十度了,而系统里显示的温度还总是慢半拍。问题很简单,板上那颗NTC热敏电阻测到的是“板温”,热源却在电路板的另一侧、隔着铜箔和变压器。后来我把测温方案换成了带远程二极管通道的PJ85718DM配STM32L4A6RG,一颗芯片同时读本地板温和几米外的远端结温,才把温度监测这事真正理顺。这篇文章把我的选型思路、原理理解、硬件连接、软件驱动到实测校准和踩坑记录完整写出来,给正在做HVAC控制器或嵌入式板级温度监测的朋友一个可直接抄作业的参考。
1. 本地加远程的温度监测,不是多此一举
1.1 HVAC和嵌入式场景里,温度测点到底在哪儿
做HVAC控制的人应该都有同感:一个空调箱或者风机盘管控制器上,真正关心的温度往往不在控制板附近。回风温度要伸进风道里去测,盘管防冻要贴在翅片或者管壁上,压缩机排气温度则可能在金属管路上。这些测点距离主控板少则半米,多则好几米。如果只用板载热敏电阻,测的是板子自己发热后的温度,跟风道里的实际空气温度完全是两回事。
嵌入式场景其实更苛刻。CPU、GPU、功率MOSFET这些热源都在封装内部,常规做法是把热敏电阻贴在散热器上测外壳温度,但外壳温度跟芯片结温之间有明显的滞后和温差。想直接测芯片内部温度,只能靠芯片自带的温度传感器,或者利用芯片内部二极管的温度特性,用一颗外部的远程温度传感器芯片去读取。而大多数功率器件和高端处理器,并不会把测温二极管引到外部来。
1.2 一颗PJ85718DM,同时解决板温和远端测温
PJ85718DM这颗芯片的核心价值,就是在一个小封装里集成了一路本地温度测量和一路远程温度测量。本地通道测的是芯片自身所在的PCB环境温度,远程通道通过两根引脚接一个外部二极管或三极管,就能把测温点延伸到任意需要的位置。这样主控板上只需要一个小探头,就能把风道温度、盘管温度、功率器件结温这些远端温度读回来。
跟传统的NTC方案相比,这种做法的优势非常明显。NTC的阻值-温度关系非线性,需要查表或者用Steinhart-Hart公式拟合,而且导线越长,引线电阻带来的误差就越大,四线制接法成本又高。PJ85718DM的远程测温用的是二极管正向压降随温度变化的特性,信号是毫伏级的电压差,对引线电阻不敏感,线长一些影响也小。再加上它本身就是I2C/SMBus接口的数字输出,直接连STM32L4A6RG,不需要额外接调理电路,PCB面积和物料成本都能压下来。
1.3 远程测温的底层原理:用ΔVBE换温度
远程通道能测温度,靠的是半导体PN结的正向压降VBE与温度之间的近似线性关系。不过单个PN结的VBE绝对值和工艺参数关系很大,直接测绝对压降很容易引入器件误差。所以PJ85718DM实际用的是ΔVBE方法:用两个不同的电流I1和I2分别流过外部二极管,测出两次的压降变化量ΔVBE,再根据物理公式算温度。
这个公式写出来就是:ΔVBE = (kT/q) × ln(I2/I1),其中k是玻尔兹曼常数,q是电子电荷,T就是绝对温度。由于ln(I2/I1)里两个电流都是芯片自己设定的固定值,所以整个表达式里唯一的变量就是温度T,二极管本身的饱和电流、串联电阻、工艺偏差都做了差分消除。这也就是为什么要用外部二极管或二极管接法的三极管,而不是随便一个稳压管。实际项目中我用的是小信号三极管把基极和集电极短接,基极接D+,发射极接D-,这样等效成了一个纯正的PN结,测量一致性非常好。
理解了这层原理,后面所有硬件设计和故障排查都有了依据:D+和D-引脚上任何影响微小电压测量的因素,比如走线电阻、寄生电容、外部噪声,都可能直接干扰温度读数。
2. 硬件电路设计:从PJ85718DM到STM32L4A6RG的完整链路
2.1 传感器端的最小电路与引脚说明
PJ85718DM的外围电路非常简单,供电脚加一个0.1μF和1μF的陶瓷电容做去耦,I2C的SCL和SDA各接一个上拉电阻到3.3V,地址选择脚接高或接低确定从机地址,报警输出引脚ALRT接MCU的一个EXTI输入,D+和D-两个引脚单独引出去接远程探头。我实际用的上拉电阻是4.7kΩ,I2C总线速率配置成100kHz标准模式,跟传感器的手册要求兼容。
这里特别提醒一下供电问题。大多数这类SMBus温度传感器的工作电压范围是3.0V到5.5V,STM32L4A6RG的GPIO和I2C接口是3.3V逻辑,如果传感器也供电3.3V,接口电平自然匹配。如果系统里已经有5V电源想直接给传感器供电,那就需要确认传感器引脚是否兼容3.3V逻辑,或者做电平匹配,否则I2C总线高电平可能会对MCU引脚造成不安全的应力。我习惯统一用3.3V供电,省事。
ALRT引脚值得多说一句。这个引脚是开漏输出,所以也必须外接一个上拉电阻,否则MCU读不到电平变化。ALRT可以配置成比较器模式或事件模式,温度超过上限或低于下限时触发低电平脉冲或锁存信号,非常适合在低功耗系统里把MCU从停止模式唤醒。
2.2 远程二极管的接法与关键滤波细节
远程测温探头的接法是整个硬件设计里最容易被忽视的部分。我推荐使用MMBT3904这种小信号NPN三极管,把基极和集电极直接短接,基极接到D+,发射极接到D-。注意,一定用的是基极-发射极的PN结,不是基极-集电极那个结,因为发射结的VBE特性更理想,离散性更小。
探头的物理位置决定了温度测量的意义。做HVAC应用时,可以把探头封装在一个细小的金属套管里插入风道;做功率器件监测时,把贴片三极管贴在散热器上,用导热硅脂填充间隙,引脚引回D+和D-。这根两芯线的走线要成对、贴近,尽量远离开关电源的MOSFET驱动走线、PWM风扇线这类强干扰源。我在PCB上留了一个可选的1nF电容位置,放在靠近D+/D-端子处,跨接在两条线之间,用来滤掉高频共模噪声。这个电容太小会影响信号建立时间,太大会导致温度读数误差,1nF是个比较均衡的值。
还有一个常见问题是D+和D-引脚的极性。接反之后芯片不会烧,但读出来的远程温度要么是无效值,要么明显异常,比如显示-128℃或者+127℃这类满偏值。我调试时花了不少时间排查,最后拿起万用表二极管档一测才发现是探头那边焊反了。
2.3 与STM32L4A6RG的连接
STM32L4A6RG的I2C外设资源很富裕,有好几路独立的I2C控制器。我用了I2C1,SCL配置在PB6,SDA配置在PB7。这两条线在STM32L4系列上有5V容忍能力,配合3.3V上拉到VCC,电平上没有问题。如果希望抗干扰更强,也可以在SCL和SDA对地各加一个几十皮法的电容,但注意总线电容太大会拖慢上升沿,影响通信。一般我只有在探头线特别长或者现场干扰严重时才加,容量从22pF开始试。
MCU的配置很简单:I2C标准模式、时钟100kHz、7位地址、软复位关闭、模拟噪声滤波开启。需要确认传感器的从机地址和I2C总线上其他设备不冲突。PJ85718DM的地址一般由ADD引脚的接法决定,接GND、接VDD或者悬空会对应不同的地址,具体查手册。我这条总线上还挂了一个EEPROM和一个RTC,地址都不相同,所以没有冲突问题。
2.4 电源完整性和地平面处理
温度传感器的模拟前端信噪比直接取决于供电和地的质量。不要为了让走线简洁,就把传感器放在靠近开关管的地方。PJ85718DM的VDD引脚去耦电容要靠近电源引脚放置,地引脚通过短走线直接接到PCB的模拟地或者主地平面。如果板上同时有继电器驱动或可控硅,建议传感器区域和驱动区域之间做一点隔离,至少不要让高温端子的强电流回流路径穿过传感器的地。
另外,PJ85718DM本身就是一颗低功耗器件,待机模式下电流只有微安级别。STM32L4A6RG在STOP2模式下的静态电流同样非常低,这个组合做电池供电的温控器也完全可行。不过低功耗设计有个细节:远程通道如果不需要一直测量,可以配置成单次转换模式,MCU唤醒后发命令触发一次测量,读完寄存器再睡,这样能省不少电。
3. STM32L4侧的软件驱动与温度换算
3.1 I2C初始化与传感器寄存器读写
用HAL库初始化I2C1非常快,关键是正确设置I2C时钟频率。我用的时钟树是MSI或者PLL倍频到80MHz,然后给I2C分频到100kHz,HAL的初始化结构体里填好时序参数即可。初始化完成后第一步不是直接读温度,而是读一个固定寄存器确认通信正常。比如设备的ID或版本寄存器,读出来的值如果符合手册预期,再继续往下配置。
读写PJ85718DM这类SMBus器件,建议用HAL_I2C_Master_Transmit和HAL_I2C_Mem_Read这类函数。写配置时先发从机地址,再发寄存器地址和配置数据;读温度时可以把寄存器地址设为温度数据寄存器的高字节,然后连续读取两个字节。连续读的好处是能保证高字节能和低字节在时间上足够接近,减小采样间隔内的波动影响。SMBus有PEC校验功能可选,HVAC控制器里总线距离不长,现场干扰不严重时我一般不开,但如果是工业现场,开上PEC能避免不少偶发错值。
3.2 温度寄存器格式与补码换算
这个环节是新手翻车重灾区。PJ85718DM这类传感器的温度寄存器通常是12位或13位的二进制补码格式,高字节是符号位和整数部分,低字节是小数部分。不同分辨率模式下LSB权重不一样,常见的是0.0625℃/LSB,也有0.0078125℃/LSB的高精度模式。我用的配置是12位分辨率,0.0625℃/LSB。
读回两个字节后,先拼成一个16位整数,然后做符号扩展。假如读到的原始值是0x0190,符号位是0,直接乘以0.0625,就是400×0.0625=25.0℃。如果读到的值是0xFF30,符号位是1,需要先扩展成带符号的16位值,也就是减掉0x10000,得到-208,再乘以0.0625就是-13.0℃。写成代码就是:
int16_t raw = (int16_t)((uint16_t)(reg_hi << 8) | reg_lo); float temp_c = (float)raw * 0.0625f;关键在于raw要定义为有符号16位,左移操作时reg_hi要先转成uint16_t再移位,避免符号位被扩展到int类型里产生难以察觉的错值。这个坑我换了好几种写法才确认,低字节虽然只有高4位有效,但读取时多余的位要直接忽略,不要去做什么四舍五入,不然反而会引入误差。
远程通道的读取流程和本地通道完全一样,只是寄存器地址不同。我在程序中把本地温度和远程温度分别封装成独立的读取函数,返回float类型温度值。上层逻辑做滤波和应用时,只跟这两个函数打交道,方便后续替换其他型号的传感器。
3.3 报警阈值配置与MCU主动唤醒
PJ85718DM支持设置高低温报警阈值,温度超过设定值时ALRT引脚会产生有效电平。这个功能对实时性要求高的温度保护场景特别有用。普通轮询方式下,MCU需要不停读取温度寄存器,既浪费CPU,又不能在两次读取间隔内第一时间捕捉到温度突变。报警方式下,MCU可以长时间待在低功耗模式,温度超标时传感器主动拉低ALRT引脚,把MCU从STOP模式唤醒,MCU再读取具体温度值做处置。
配置报警的流程是:先设置本地和远程通道各自的THIGH和TLOW寄存器,再设置ALRT引脚的工作模式。锁存模式需要MCU读状态寄存器来清除报警,比较器模式则在温度回到回差范围后自动恢复。HVAC应用里我一般用锁存模式,避免温度在阈值附近抖动时ALRT反复跳变。回差大小可以根据控制精度要求设定,比如高温报警设到75℃,回差5℃,这样温度降到70℃以下才会解除报警,现场人员能明显感知保护和恢复的过程。
3.4 软件滤波与迟滞处理
即使传感器精度够高,现场环境的瞬时扰动还是会让读数出现小幅跳变。我做了两层处理。第一层是一次采样内做多次转换求平均,PJ85718DM本身支持平均模式,设置成连续转换加上平均,硬件上就能滤掉一部分噪声。第二层是在MCU侧做一个滑动平均滤波器,队列长度取了8次,权重均分,效果相当于把采样周期内的毛刺平滑掉。
但滤波也不是越重越好。HVAC温度对象本身变化很慢,滤波重一点影响不大;而嵌入式散热保护场景里,结温可能在几秒内上升十几度,滤波太深会把过温信号淹没,保护动作严重滞后。我最后的做法是双路判据:滤波后的平均值用于控制逻辑,比如风扇PWM占空比调节;原始读取值用于快速保护,一旦超过硬阈值立即触发中断响应,不经过滤波。这样既保持了温度曲线平滑,又不牺牲保护响应的实时性。
4. 从-20℃到90℃的实测标定与误差分析
4.1 标定平台怎么搭
标定工作不能省,尤其是远程通道。我把PJ85718DM的本地通道部分和远程探头同时放进恒温槽里,以恒温槽的实测温度作为参考值。恒温槽用的是油浴式,温度范围覆盖-20℃到90℃,最小步进5℃。每个设定点让系统稳定至少15分钟,等待探头和槽体内温度完全平衡,再记录MCU通过I2C读到的本地温度、远程温度,以及参考温度计读数。
远程探头的固定方式很关键。探头悬空浸在油浴里,和直接贴在槽体金属壁上的读数会有差异。我选择的是把探头封装在一个铜套管里浸入油浴,模拟HVAC现场把探头插入金属测温套管的场景。这样做出来的标定结果更接近实际。
4.2 实测结果整理
测试数据大致如下,取几个典型点位的均值展示:
| 恒温槽参考温度 | 本地通道读数 | 远程通道读数 | 本地误差 | 远程误差 |
|---|---|---|---|---|
| -19.8℃ | -19.6℃ | -20.1℃ | +0.2℃ | -0.3℃ |
| 4.9℃ | 5.1℃ | 5.2℃ | +0.2℃ | +0.3℃ |
| 25.0℃ | 25.2℃ | 24.9℃ | +0.2℃ | -0.1℃ |
| 49.8℃ | 49.9℃ | 50.2℃ | +0.1℃ | +0.4℃ |
| 70.1℃ | 70.0℃ | 70.5℃ | -0.1℃ | +0.4℃ |
| 89.9℃ | 89.5℃ | 89.7℃ | -0.4℃ | -0.2℃ |
这个结果在0℃到70℃区间内非常理想,本地和远程都控制在±0.5℃以内。低温段误差略大,但仍然可以接受。考虑到传感器本身的标称精度通常在±1℃左右,这个实测表现说明整个硬件链路没有引入明显的系统性误差。
要注意的是,这套数据是在探头线只有30厘米、远离干扰源的前提下测出来的。如果现场把探头线延长到两米以上,或者走线贴近变频器输出线,误差和跳变都会变大。因此标定最好在接近最终安装形态的状态下进行,而不是在干净的工作台上测完就认为万事大吉。
4.3 误差来源逐项拆解
测出来的误差不是随便就能忽略的,我把主要误差来源列一下方便排查:
- 远程二极管的VBE离散性。不同批次的三极管基极-发射极电压会有几个毫伏差异,等效成温度可能就是一两度。解决方法是尽量采购同一品牌同一批次的管子,或者在软件里对每个探头做单点偏置修正。
- D+/D-线上的串联电阻。虽然ΔVBE方法对串联电阻有抑制作用,但电阻过大时仍然会引入误差,尤其是电流切换瞬间造成的压降。我用的是0.25平方毫米左右的细导线,长度控制在两米以内。
- 并联电容过大会改变信号建立时间。曾经试过在探头端并了0.1μF的电容,结果远程读数普遍偏高好几度,换成1nF后恢复正常。这一点原理上跟远端二极管在高频激励下的充放电不平衡有关,实际中只能靠实验确定。
- PCB自身发热对本地通道的影响。本地温度本来就是板温,如果板上有大功率发热元件贴着传感器,读到的温度自然比环境温度高。这是物理事实,不算故障,但系统设计时要明确这个读数的含义。
5. HVAC与嵌入式落地过程中的实操细节与避坑记录
5.1 HVAC场景:回风温度和盘管温度怎么布
在HVAC应用里,我把PJ85718DM的本地通道用作控制器内部的机箱温度监测,主要防止控制箱内部过热;远程通道则接了一只套管式探头,插在回风风道里测回风温度。这个布置的好处是:回风温度直接参与PID运算和启停控制,而机箱温度用来做设备保护的辅助判断,两者互不干扰。
很多暖通控制器的回路温度测量,以前用的是热敏电阻接到长线上,引线电阻在冬夏季温差大的时候会带来不小的误差。换成远程二极管探头后,这个问题基本消失,而且探头响应速度更快。因为二极管的体积比NTC热敏电阻小得多,热容小,温度变化时达到平衡的时间更短。
5.2 嵌入式场景:结温监测与风扇联动
另一块应用是把远程探头贴在功率MOSFET的散热片上,MCU根据读取的结温来调节风扇PWM占空比。传统做法是固定风扇转速或者根据环境温度粗略调速,噪声大、能耗高。用这个方案后,风扇转速和真实结温强相关,温度40℃时风扇几乎不转,温度70℃时缓慢爬升,超过85℃才全速运转。整个散热系统的噪声和功耗都明显改善。
这里有个细节:散热片表面最好做一个非常小的凹槽或者使用导热胶固定三极管,保证三极管尽量贴近热源,同时又不至于短路到带电的散热片。我用的办法是先把MMBT3904焊在一小块覆铜板上,再通过导热硅脂粘到散热片,中间用云母片绝缘。引脚引回D+/D-时尽量短,远离风扇的PWM线和电源线。
5.3 我踩过的几个坑和对应的处理办法
第一个坑是D+/D-接反导致远程温度报出异常值。那天现场温度显示直接从正常值跳到-127℃,我没想到是极性接反,先是怀疑驱动代码符号扩展写错了,反复查程序查了两个小时。后来用万用表二极管档测远程端子,才发现探头的D+和D-在接线端子处被工人对调了。这个问题在采用可插拔端子连接的系统中特别容易犯,建议在设计时把端子的1脚定义为D+,2脚定义为D-,并在丝印上标清楚。
第二个坑是I2C偶发超时。现象是系统运行几天后偶尔读不到温度,复位后一切正常。最终定位到上拉电阻用了1kΩ,而总线上挂着三四个器件,低电平灌电流偏大,把传感器的I2C输出级压得很厉害。换成4.7kΩ之后,连续跑了一个月再没出现过通信错误。
第三个坑是ALRT引脚悬空。开始设计时认为不用报警功能,就没接这个引脚。结果传感器正常工作时该引脚因为没有上拉电阻而呈现不确定电平,恰好又跟旁边一个GPIO产生串扰,导致MCU莫名其妙进中断。后来哪怕不用报警功能,也把ALRT通过10kΩ电阻上拉到3.3V,问题消失。
第四个坑关于SMBus和I2C的时序差异。传感器的SMBus接口有数据保持时间的要求,而STM32的I2C外设在某些配置下释放总线的时机偏快,读写密集时偶尔会出问题。解决的办法是把I2C时钟降到100kHz,并且开启I2C的模拟噪声滤波和数字噪声滤波,读操作之间加一个微秒级延时。
5.4 低功耗模式的联动策略
这套方案如果要跑电池或者需要低功耗运行,PJ85718DM和STM32L4A6RG的配合可以设计成完全事件驱动的方式。正常运行时MCU进入STOP2模式,所有不用的外设时钟关闭,I2C也关闭,只有ALRT引脚对应的EXTI保持使能。PJ85718DM保持连续转换状态,本地或远程温度一旦超出阈值,ALRT拉低,MCU被唤醒后重新初始化I2C,读取状态寄存器和温度值,完成控制动作后再进睡眠。实测下来,系统平均功耗可以压到非常低,休眠期间整机电流主要由传感器的静态电流和上拉电阻漏电流贡献。
这种事件驱动设计的关键是报警阈值的设置要合理,不能太灵敏频繁唤醒MCU,也不能太迟钝漏报问题。我通常在室温基础上加8℃作为告警阈值,回差设为4℃,这样正常工作时MCU基本处于睡着的状态,温度出问题才能及时醒。
最后分享一个我调试这套方案时最管用的细节:远程探头那两根引线在靠近传感器的位置并联一颗1nF的高频陶瓷电容,能明显改善温度读数的稳定性,而几乎不影响精度。当时现场有变频器干扰,远程读数每秒跳0.3℃,加上这个电容之后读数的跳动降到了0.0625℃以内。这个做法成本几乎为零,却解决了最头疼的现场干扰问题,强烈建议大家在设计阶段就预留这个电容的位置。