STM32与PJ85718DM实现本地远程温度监测方案解析 做 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 作为 SDAAF 复用编号以你 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 一组来自模拟项目的实测数据为了验证这套方案的实际水平我搭了一个模拟项目 XSTM32F302VC 通过 I2C 读取 PJ85718DM远程探头用成卷的 2N3906 二极管连接参考温度计精度为正负 0.3 摄氏度。结果如下测试条件参考温度本地读数远程读数本地误差远程误差常温环境探头引线 20cm22.3℃22.1℃22.5℃-0.2℃0.2℃恒温块 45℃探头贴合金属面45.2℃45.4℃45.1℃0.2℃-0.1℃恒温块 60℃探头用屏蔽双绞线 3m59.8℃60.0℃60.4℃0.2℃0.6℃恒温块 30℃探头用普通护套线 5m30.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 结信号走长线。批量产品要对探头二极管做一致性抽检。不同批次的晶体管在相同温度下读数可能相差零点几摄氏度这已经是无法通过软件定位的系统误差。最后再分享一个小技巧测空气温度时把测温二极管包一层薄热缩管或者放在导热硅脂里再贴在一块小铜片上读数会稳很多测管壁或散热器温度时就直接用导热胶把二极管贴在金属表面。不要把裸露的二极管悬在气流中间那样测到的既不是空气温度也不是管壁温度而是一个不伦不类的“耦合温度”整个控制逻辑都会被带偏。这一条是在现场吃过亏才记住的。