做 HVAC 控制柜的时候,最烦的其实是温度点的分布:主控板要测板卡自己的环境温度,又得去管风管里面或者盘管上面的温度,以前我是放两支传感器,拉两对信号线,还得给它们分别做采样电路。后来改了一套方案,用 PJ85718DM 配合 STM32F302VC 做本地与远程温度监测,一颗传感器芯片外接一个普通小二极管,就能同时把两路温度收回来。这篇文章就把这套方案的完整逻辑拆开讲一遍,包括硬件怎么接、固件怎么写、误差怎么校准,以及在现场调试时我踩过的三个坑。
1. “一芯两用”的测温逻辑:本地和远程到底测的是什么
1.1 为什么一颗芯片能测两个温度点
很多第一次接触远程温度传感器的朋友会想不明白:芯片明明就贴在主板上,凭什么能测到远处探头的温度?秘密不在芯片本身,而在测温原理里使用的“PN 结”上。
这类温度传感器的核心是一个模数转换器和恒流源。芯片内部的本地温度传感器,本质上就是一个集成在芯片里的 PN 结。当一个 PN 结通过恒定电流时,它的正向压降 Vbe 与温度基本呈线性关系,温度越高,Vbe 越低,大约每升高 1 摄氏度,压降下降 2 毫伏左右。这个关系虽然好测,但单个 Vbe 的绝对值受工艺影响很大,所以高精度方案都使用“不同电流下的 ΔVbe”来测温。用两个不同大小电流分别激励同一个 PN 结,测两次 Vbe 的差值,这个差值与绝对温度成正比,而且与晶体管的工艺参数关系不大。
远程测温的原理完全一样,只是把那个 PN 结从芯片内部搬到了芯片外部。我在远端接一个普通小信号二极管,或者把三极管的基极和集电极短接后当作二极管使用,芯片通过两根引线 D+、D- 给远端 PN 结提供激励电流,再测量它的 ΔVbe,就可以算出远处那个结的温度。因此,一颗芯片的本地通道和远程通道,其实都在做同一件事:测 PN 结温度。
1.2 这套方案适合用在什么场景
从我实际做过的方向来看,这套设计在嵌入式和 HVAC 里都非常合适:
- HVAC 控制柜:主控板需要同时知道自己所处的控制柜温度,又要知道送风管、回风口、换热盘管附近的温度。把远程探头放在风管或盘管表面,主控板放在柜内,一颗芯片就能完成两处监控,省去一路传感器和一路信号调理电路。
- 嵌入式板级热监控:功率 MOS 管、DC-DC 电感、CPU/GPU 附近是最容易过热的地方,但探头位置往往离 MCU 有一段距离。远程测温二极管体积小、布线简单,可以直接贴在发热源附近,温度信息通过两根线传回 MCU。
- 多点温度预警:远程通道支持高温报警阈值,超过设定值后 ALERT 脚直接拉低,接到 MCU 的外部中断脚,不需要软件轮询也能实现硬实时保护。
在 HVAC 场景里,我建议远程探头的目标应用距离控制在 0.5 米以内;如果非要超过 1 米,后面的布线处理和噪声抑制就要特别注意,后面单独讲。
2. 硬件搭建:STM32F302VC 和 PJ85718DM 之间怎么连才稳
2.1 最小系统与 I2C 总线的接法
PJ85718DM 与 STM32F302VC 之间用的是 I2C/SMBus 数字接口,总线就两条:SCL 和 SDA。我这里用的是 I2C1,默认引脚是 PB6 作为 SCL、PB7 作为 SDA,AF 复用编号以你 CubeMX 生成为准。
硬件上最容易忽略的是上拉电阻。I2C 是开漏结构,总线空闲时必须靠外部上拉电阻把电平拉高。RC 常数直接影响通信速率:
| 总线速率 | 总线电容约 100pF 时的推荐上拉阻值 | 说明 |
|---|---|---|
| 100kHz 标准模式 | 4.7kΩ 或 3.3kΩ | 上升沿足够,噪声容忍度好 |
| 400kHz 快速模式 | 2.2kΩ 或 1.8kΩ | 需要更陡的上升沿,降低误采样概率 |
STM32 的 GPIO 内部上拉电阻通常是 30kΩ 到 50kΩ,远远不够,必须外部再放上拉电阻。我调试时遇到过直接省掉外部上拉、只开内部上拉的情况,结果就是设备偶发性通信失败,因为上升沿太缓,在总线电容较大时波形变成三角波,遇到噪声很容易误判电平。
最小系统连接清单:
PJ85718DM VDD -> 3.3V PJ85718DM GND -> GND PJ85718DM SCL -> PB6(经 4.7kΩ 上拉到 3.3V) PJ85718DM SDA -> PB7(经 4.7kΩ 上拉到 3.3V) PJ85718DM ALERT -> PA0(外部中断输入,开漏输出需上拉) PJ85718DM D+ -> 远端二极管正极 PJ85718DM D- -> 远端二极管负极ALERT 脚也是开漏结构,所以它到 PA0 之间同样要放一个上拉电阻。PA0 配置成外部中断,下降沿触发,因为报警时 ALERT 会被拉低。
2.2 远程探头端处理:二极管选择、滤波与长线权衡
远程测温的探头不复杂,但要选对器件。我不建议用真正意义上的“二极管”随机挑选,因为有些整流二极管的工艺和串联电阻会让读数偏差很大。更稳妥的做法是用小信号三极管,比如手头常见的 2N3906,把基极和集电极短接,用发射结做测温结。小信号三极管的 hFE 高、结特性一致性好,换不同批次时读数漂移小。
探头端的滤波有两个位置:一是 D+/D- 之间并联一个小电容,用来滤除差分噪声;另一个是 D+ 和 D- 各自串联一个小电阻,用来抑制长线反射。并联电容的容值建议在 1nF 到 10nF 之间,我常用的值是 2200pF。如果并联电容太大,ADC 采样时会因为激励电流建立时间不够而产生不稳定读数。
长线处理是常见矛盾:线越长,抗干扰能力越差;但串电阻又会影响测温精度。一个比较实用的折中方案是:短于 30cm 用普通双绞线,不加串联电阻;30cm 到 1m 用屏蔽双绞线,屏蔽层单端接地;超过 1m 就不要硬用远程二极管方案了,把 PJ85718DM 本身放到探头位置附近,用 I2C 线传回数据更划算。这个判断在 HVAC 现场特别重要,风管里有时候要走三五米,我更倾向于把传感器芯片放风管边上,MCU 用遮线或 RS-485 远传。
2.3 电源与去耦、报警脚接法
PJ85718DM 的电源建议和 STM32F302VC 的 3.3V 共用,电源引脚旁边放 100nF 陶瓷电容加 1uF 钽电容组合。很多人把 100nF 放得很远,结果高频噪声全进了电源,温度读数会周期性跳动。去耦电容必须靠近芯片电源引脚,这是温度稳定性的第一道防线。
ALERT 脚还有一个容易被忽略的细节:如果 MCU 和传感器芯片供电时序不一致,MCU 复位时 GPIO 状态不确定,可能通过上拉电阻把 ALERT 拉出异常状态。最稳的办法是让传感器芯片的 VDD 和 MCU 的 VDD 同步上电,或者在 ALERT 线上串联一个 100Ω 小电阻再进 MCU,限制故障态的电流。
3. 固件比想象中多三步:轮询、滤波、异常处理
3.1 初始化 I2C 时的几个关键配置
用 STM32CubeMX 生成工程时,I2C 的初始化参数不少,但有几个直接决定 PJ85718DM 能不能稳定工作:
- 时钟速度:先按 100kHz 跑通,不要一上来就 400kHz。HVAC 现场往往走线较长,400kHz 对上升沿要求高,一旦总线上有个别电容偏大,很容易出现偶发 ACK 失败。
- 无拉伸模式:NoStretchMode 建议保持 Disable。SMBus 设备允许时钟拉伸,如果强制关掉,部分芯片在内部处理寄存器时会丢失响应。
- 地址模式:7 位地址,PJ85718DM 的地址一般由 A0/A1 引脚决定,我把两个引脚都接地,设备地址记成 0x18,这只是我调试用的配置,实际要翻手册确认。
初始化代码:
hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1);GPIO 配置里,SCL/SDA 必须配置为开漏复用模式,同时使能内部上拉作为辅助。注意我在前面说过,内部上拉只能辅助,不能替代外部上拉电阻。
3.2 读取本地和远程温度寄存器
远程温度类芯片的寄存器布局各有差异,但读取流程非常相似:先写寄存器指针,再读两个字节数据。以我调试的这颗芯片为例,本地温度寄存器地址是 0x00,远程温度寄存器地址是 0x01,但代码里我统一封装了读函数,换型号时只需要改寄存器地址。
温度数据一般是 12 位二进制补码格式,高字节是整数位,低字节的高四位是小数位,每位代表 1/16 摄氏度。温度转换公式也就固定了:raw 值除以 16 就是摄氏度。
#define PJ_DEV_ADDR 0x18 // 按实际硬件接线配置 #define REG_LOCAL_TEMP 0x00 // 本地温度寄存器 #define REG_REMOTE_TEMP 0x01 // 远程温度寄存器 uint8_t pj_read_temp(uint8_t reg, float *temp_c) { uint8_t data[2] = {0}; uint8_t ret; ret = HAL_I2C_Mem_Read(&hi2c1, (PJ_DEV_ADDR << 1) | 0x01, reg, I2C_MEMADD_SIZE_8BIT, data, 2, 100); if (ret != HAL_OK) { return 0; } uint16_t raw = ((uint16_t)data[0] << 4) | ((uint16_t)data[1] >> 4); // 12 位补码需要符号扩展 if (raw & 0x0800) { raw |= 0xF000; } int16_t signed_raw = (int16_t)raw; *temp_c = signed_raw / 16.0f; return 1; }注意 HAL_I2C_Mem_Read 的设备地址参数需要左移一位,把 7 位地址转换成带读写位的 8 位地址形式,很多新手在这里翻车,直接填 0x18 会导致总线无响应。
3.3 数据滤波与报警阈值配置
直接读出来的温度数据,在安静环境下是稳定的,但 HVAC 现场有变频器、接触器、风机电机,远程探头线上会耦合噪声,读数低位会跳。我用的滤波是一阶 IIR,参数 alpha 取 0.2 到 0.3 比较合适。alpha 太大滤波效果差,太小报警响应又太慢。
float remote_temp_filt = 0.0f; float local_temp_filt = 0.0f; const float alpha = 0.25f; void update_temperature_filter(void) { float local_cur = 0.0f; float remote_cur = 0.0f; if (!pj_read_temp(REG_LOCAL_TEMP, &local_cur)) return; if (!pj_read_temp(REG_REMOTE_TEMP, &remote_cur)) return; local_temp_filt += alpha * (local_cur - local_temp_filt); remote_temp_filt += alpha * (remote_cur - remote_temp_filt); }报警阈值方面,建议在系统初始化时先把高温阈值写进寄存器,不要依赖芯片复位时的默认值。远程高温阈值寄存器一般在 0x05 附近,具体偏移见芯片手册。阈值 raw 值同样是 12 位格式,例如 60 摄氏度对应的 raw 是 60 * 16 = 960,也就是 0x03C0。
uint8_t limit_high = 0x3C; // 高 8 位,代表 60 摄氏度整数部分 HAL_I2C_Mem_Write(&hi2c1, (PJ_DEV_ADDR << 1) | 0x00, REG_REMOTE_HIGH, I2C_MEMADD_SIZE_8BIT, &limit_high, 1, 100);报警脚初始化成外部中断下降沿触发后,中断服务函数里读报警状态寄存器,判断到底是本地超温还是远程超温,然后做对应处理。注意读报警状态后要写“清除报警”命令,否则 ALERT 会一直保持低电平,误以为还有新报警。
4. 误差从哪来:串联电阻、非线性因素和长线的三重夹击
4.1 串联电阻怎样让远程温度偏高
远程测温最大的误差源,不是芯片自身的 ADC,而是 D+/D- 引线之间的串联电阻。当激励电流流过导线的串联电阻时,额外的压降会被 ADC 当成 PN 结压降的一部分算进去,结果就是温度读数高于实际值。
串联电阻的影响有多大?经验值大约是每 100Ω 串联电阻可能带来 1 到 3 摄氏度的正误差,具体系数取决于芯片内部使用的激励电流大小和测量算法。所以现场用劣质长导线接探头,读数偏高一两度是很常见的事。
要修正串联电阻误差,有两个方向:
- 选择内置三电流测量机制的芯片,它能通过多次测量抵消串联电阻压降,PJ85718DM 是否支持要看手册,但选型时可以优先考虑这种特性。
- 软件校准:先用短引线测出一个准确读数,再接入实际长线测一遍,两者差值作为固定 offset,写入芯片的偏移校准寄存器或软件做补偿。这个方法简单,但补偿值只在同一种线材和长度下有效。
4.2 远端晶体管的工艺参数影响
并不是任何晶体管接上去都能获得高精度。测温 PN 结有一个“理想系数”参数,理想系数越接近 1,ΔVbe 与温度的线性关系越好。常见的 2N3906 这类小信号晶体管理想系数很好,可以直接使用。而那些大功率整流管的结特性不干净,可能有明显的漏电流,低温段读数会飘。
还有晶体管的 β 值。如果远端三极管的 hFE 很低,基极电流就不能忽略,基极电流会分流一部分激励电流,导致测量到的 Vbe 偏低、温度读数偏高。芯片如果支持 β 补偿功能,可以尽量打开;如果没有这个功能,就不要用高功耗大功率管做探头,老老实实选小信号管。
我的建议是:正式项目里探头选型固定,批量贴片前先用几颗样品做一致性测试。换上不同批次的 2N3906 后发现偏差普遍在正负 0.2 摄氏度以内,这个量级可以接受。
4.3 现场校准方法:单点补偿和两点校准
对于大多数 HVAC 应用,单点偏移校准就够了。做法是弄一个恒温环境,比如一桶 45 摄氏度的恒温水浴,把探头和温度参考值放在一起,读取芯片到的温度,计算出偏差:
offset = reference_temperature - sensor_reading calibrated_temp = current_reading + offset如果项目要求全量程精度更高,比如嵌入式设备要求 0 到 100 摄氏度范围内误差小于正负 0.5 摄氏度,推荐做两点校准:
给定两个参考点:t1、t2 对应传感器读数:r1、r2 gain = (t2 - t1) / (r2 - r1) offset = t1 - gain * r1 true_temp = gain * reading + offset每次换探头型号、换线材长度,都要重做一次校准。我在一个模拟项目里就用这套公式,把远程通道在 0 到 80 摄氏度的最大误差从 1.2 摄氏度压到了 0.3 摄氏度以内。
5. 三次现场翻车排查:从诡异偏高到总线锁死
5.1 翻车一:远程温度偏偏高 3 到 5 摄氏度
第一次遇到这个现场时,设备在正常环境里运行,PID 控制逻辑没任何问题,但远程温度显示 28.5 摄氏度,手边水银温度计测出来只有 25 摄氏度。一开始我怀疑是探头坏了,换了探头还是偏高。
排查链路是这样的:
- 先用两根 10cm 短线把远程二极管直接焊到开发板上,读数回到了 25.1 摄氏度,问题范围瞬间缩小到探头引线。
- 观察现场走线,发现从控制柜到风管探头位用了大约 5 米普通多芯护套线,没有屏蔽也没有双绞。
- 示波器接到 D+/D- 两端,看到小幅度的开关噪声波形,现场附近就是变频器柜。
- 换用屏蔽双绞线,屏蔽层只在传感器芯片端单点接地,并在 D+/D- 之间并联 2200pF 电容。
- 复测,读数回到 25.4 摄氏度,与参考值偏差 0.4 摄氏度,问题解决。
这条排查看似简单,但关键在于“换短线恢复正常”这一步,它把问题从芯片/探头分离到了引线和布线上。要是没有这一步,我可能还会继续怀疑器件批次问题。
5.2 翻车二:I2C 总线被拉死,SCL 永远低电平
另一个项目里,系统连续跑了几分钟后,主控突然读不到 PJ85718DM 的数据,其他 I2C 设备也全部消失。示波器一看,SCL 卡在低电平,SDA 也是低。这种情况我遇到过几次,按以下顺序排查:
- 先区分是主控拉死还是从机拉死。把 MCU 的 I2C 外设禁用,释放 SCL/SDA,看总线电平。结果 MCU 释放后 SCL 仍然是低,说明是某个从机卡住总线。
- 断开 PJ85718DM 的 SCL/SDA,只保留其他设备,系统活动正常,说明不是其他设备问题。
- 检查供电关系,发现传感器芯片 VDD 和 MCU 的 VDD 不是同一路电源,复位时 MCU 先上电,I2C 外设已初始化,而 PJ85718DM 还在复位状态,输出引脚状态不确定,把总线拖死。
- 改成同源供电,另外在 SCL/SDA 线上各串 100Ω 小电阻,限流防损坏,同时增加 I2C 通信失败后的伪复位处理,故障消失。
有些远程测温芯片内部支持 SMBus 超时,可以避免总线一直占用,如果支持就把这个特性打开。如果芯片不支持,主控侧每次通信超时后要把总线时钟翻转几次,让从机恢复状态机。
5.3 翻车三:设备一上电,ALERT 脚就误触发
当时的现象是主控还没开始读温度,ALERT 脚已经拉低,导致系统进入停机保护。我第一反应是传感器温度真的超限了,结果一查,环境温度才 24 摄氏度。
逐步排查后发现,报警状态寄存器里的标志原本就是置位的,复位后芯片默认的高温阈值寄存器内容非常保守,加上上一次运行可能已经触发过报警,报警标志没有清除。解决方案很直接:
- 初始化代码里,先配置远程高温阈值和本地高温阈值,设置为符合现场的值。
- 再写“清除报警”命令,清一次状态位。
- 最后再读一次报警状态寄存器,确认已经清零。
如果报警配置寄存器是设置成“锁定”模式的,只要触发过一次,即使温度降回来也会一直保持报警。要特别注意初始化顺序:先写阈值,再清状态,最后再使能 ALERT 引脚的外部中断。否则很容易出现“上电即报警”的错觉。
6. 实测数据和六条工程建议
6.1 一组来自模拟项目的实测数据
为了验证这套方案的实际水平,我搭了一个模拟项目 X:STM32F302VC 通过 I2C 读取 PJ85718DM,远程探头用成卷的 2N3906 二极管连接,参考温度计精度为正负 0.3 摄氏度。结果如下:
| 测试条件 | 参考温度 | 本地读数 | 远程读数 | 本地误差 | 远程误差 |
|---|---|---|---|---|---|
| 常温环境,探头引线 20cm | 22.3℃ | 22.1℃ | 22.5℃ | -0.2℃ | +0.2℃ |
| 恒温块 45℃,探头贴合金属面 | 45.2℃ | 45.4℃ | 45.1℃ | +0.2℃ | -0.1℃ |
| 恒温块 60℃,探头用屏蔽双绞线 3m | 59.8℃ | 60.0℃ | 60.4℃ | +0.2℃ | +0.6℃ |
| 恒温块 30℃,探头用普通护套线 5m | 30.2℃ | 30.3℃ | 32.1℃ | +0.1℃ | +1.9℃ |
最后一行能明显看出,线材和屏蔽方式对远程测温误差的影响非常大,这不是芯片精度问题,而是系统设计问题。
6.2 在不同线缆长度下的读数漂移对比
我在同样 30 摄氏度恒温环境下,只改变 D+/D- 引线长度和类型,记录了远程读数:
| 引线情况 | 远程读数 | 相对基线偏差 |
|---|---|---|
| 10cm 短线直接焊接 | 30.1℃ | 基线 |
| 1m 双绞线 | 30.2℃ | +0.1℃ |
| 3m 屏蔽双绞线,屏蔽层单端接地 | 30.4℃ | +0.3℃ |
| 3m 屏蔽双绞线,屏蔽层两端接地 | 30.8℃ | +0.7℃ |
| 5m 普通护套线 | 32.0℃ | +1.9℃ |
屏蔽层两端接地那一行是我故意做的对照,在存在地电位差的环境里,两端接地的屏蔽层反而变成了地环路,噪声更大,读数偏差高于单端接地。所以选型时,“屏蔽层单点接地”不是一句空话,要写进安装规范。
6.3 针对嵌入式与 HVAC 落地的六条经验
把这几个项目做完,我总结了六条对后来者最有价值的经验:
- 远程探头引线尽量短。超过 1 米必须用屏蔽双绞线,屏蔽层只在传感器芯片端单点接地,千万别图省事两端都接地。
- I2C 上拉电阻不能靠内部上拉硬撑。100kHz 用 4.7kΩ,400kHz 用 2.2kΩ,量一下实际上升沿,别只看配置数值。
- 报警阈值、报警状态清理必须放在初始化最前面。不依赖芯片复位默认值,上电后先把阈值写进去再使能中断。
- 温度精度达不到要求时,先查探头和引线,再查芯片。我见过大量“芯片不准”的问题,最后都是探头布线不对。
- 在 HVAC 强干扰环境里,如果引线确实很长,优先把 PJ85718DM 放到探头位置附近,让 I2C 数字信号走长线,而不是让模拟 PN 结信号走长线。
- 批量产品要对探头二极管做一致性抽检。不同批次的晶体管在相同温度下读数可能相差零点几摄氏度,这已经是无法通过软件定位的系统误差。
最后再分享一个小技巧:测空气温度时,把测温二极管包一层薄热缩管或者放在导热硅脂里,再贴在一块小铜片上,读数会稳很多;测管壁或散热器温度时,就直接用导热胶把二极管贴在金属表面。不要把裸露的二极管悬在气流中间,那样测到的既不是空气温度,也不是管壁温度,而是一个不伦不类的“耦合温度”,整个控制逻辑都会被带偏。这一条是在现场吃过亏才记住的。