做过暖通空调控制柜和电力电子控制器的人,应该都体会过“温度监测”这四个字背后的工作量。早几年我做一套嵌入式空气处理机组控制器,光回风温度和板温就占了四根模拟线:NTC 要分压、要对表、要每个批次做标定;热电偶虽然耐温,但毫伏级信号在电柜里一过就是一路地环路噪声。后来换方案,把本地和远程温度整合到了一颗芯片上——标题里的 PJ85718DM 就是这颗 I2C 接口的双通道数字温度传感器,主控用 PIC32MZ1024EFF144 这颗 200MHz 的 MIPS 内核 MCU。整套系统既测板卡自身温度,也测远端管道、功率器件或机组关键部位,一步解决。这篇文章把我在实际联机中整理的硬件接线、寄存器处理、滤波策略和踩坑记录完整写出来,给正在做嵌入式设备热保护、HVAC 机组监控或者类似项目的人做个参考。
1. 为什么要跟同一颗芯片较劲:本地温度与远端温度的需求同构
1.1 嵌入式和 HVAC 场景里的两个温度,往往对应两种保护
先说嵌入式场景。逆变器、充电桩、伺服驱动器这类设备,有两类温度你绕不开:一是 PCB 板卡本身的温度,二是功率器件或者说热源核心的温度。板温低一点高一点,可能只影响电路稳定性;但 IGBT、MOSFET 这种功率级的结温一旦超标,几秒钟内就可能炸管或者触发降额保护。以前很多板子把热敏电阻贴在散热器上,线拉到 MCU 的 ADC,每一条线都要单独处理,标定还随批次波动。
HVAC 场景其实也一样。机组内部的控制器板卡需要监控自身温度,因为电控箱在夏天可能被太阳晒到 60°C;另一方面,风管、水管的介质温度、压缩机机壳温度又是工艺控制的关键参数,通常装在离主控板几米甚至十几米远的地方。过去的做法是 PLC 加一堆模拟量模块,一个测点一路模拟量输入,硬件成本和调试成本都不小。
这两类项目放到一起看,需求结构完全同构:一个近点,一个远点;近点要求反应快、拉偏保护及时,远点要求精度稳、能扛环境干扰。既然这样,把两个测点交给同一颗芯片、同一份寄存器、同一个 I2C 地址,就比接两路 ADC 要清爽得多。PJ85718DM 这类器件正是这么设计的:本地通道读封装附近温度,远程通道通过一对 D+/D- 引脚外接二极管结,读的是远端那个结的温度。
1.2 远程二极管测温的原理:两路小电流算出 ΔVBE
远程为什么用一个二极管就能测温?本质上是利用了 PN 结正向压降和温度之间的关系。如果给一个二极管通恒定的正向电流,VBE 会随温度升高而下降,大约是 2mV/°C。可要真用“绝对 VBE”来测温度,麻烦在于每个二极管的 VBE 离散性很大,同一温度下不同批次能差几十毫伏,换算成温度就是好几度。
所以这类芯片不会傻傻地只测一个 VBE,而是在两个不同电流下测两次,取差值 ΔVBE = VBE1 - VBE2。根据半导体物理,ΔVBE 与温度近似成正比,同时把饱和电流、反向漏电流这些共性项抵消掉了。再用第三个电流点做修正,还能进一步把串联电阻、非理想因子的影响压下去。这就是为什么远程二极管通道能做到 ±1°C 甚至更好的精度,而不需要用户自己去标定二极管参数。
在接线上,你可以直接用一颗贴片三极管当作感温头:NPN 管的发射极接 D+,基极接 D-,集电极悬空;PNP 管同样也是发射极接 D+、基极接 D-。曾经有一版我把极性接反,读出来的温度直接往负方向飘了 30°C,这个我们放到最后一节排查部分细说。
1.3 为什么不继续用 NTC 或热电偶
可能有朋友要问:NTC 便宜量又足,热电偶耐温范围广,为什么非要换成二极管远程测温?
| 方案 | 接线成本 | 标定难度 | 信号抗干扰 | 适用距离 |
|---|---|---|---|---|
| NTC | 2 线/点,需分压电阻 | 每批次要做分压标定,非线性 | 电压信号,长线易受压降影响 | 板内或短距离 |
| 热电偶 | 2 线/点,需冷端补偿 | 冷端补偿模型复杂,线材配套要求高 | 毫伏级信号,极易受电机、开关电源干扰 | 现场仪表常用 |
| 本地+远程二极管传感器 | 最多 4 线(I2C+电源,远程 2 线) | 数字输出,无需分压表格,仅需两点偏移校正 | 差分方式测 ΔVBE,抗共模干扰较强 | 远程可拉数米至十几米 |
我自己用的感受是:换掉 NTC 之后,最明显的收益是调试时间缩短。以前现场温度不准,你根本说不清是探头问题、分压电阻问题还是接线松动问题;现在数字量直接从 I2C 读出来,温度异常时先怀疑电路板上的走线和接触,问题范围小了很多。
2. 硬件连接与总线设计:PJ85718DM 与 PIC32MZ1024EFF144 配合的细节
2.1 I2C 主机与供电,地址怎么定
PIC32MZ1024EFF144 是一颗资源很“富”的片子,I2C 外设有好几个,我这边用 I2C1 做主机,接 PJ85718DM 的 SDA/SCL。供电上传感器和 MCU 都走同一路 3.3V,传感器电源脚旁边放一颗 0.1uF 去耦电容,这是最稳妥的起步配置。SDA、SCL 两根线需要上拉电阻,典型值用 2.2kΩ 到 4.7kΩ 都行,取决于总线上挂了几颗设备;如果总线上就这一颗传感器,4.7kΩ 足够,总线长度不超过 20cm 时非常稳。
地址引脚是容易忽略的地方。这类 I2C 温度传感器一般都有 A0/A1 两个地址选择脚,接地和接电源有多种组合,最后形成好几个可选 I2C 地址。我当时把两个地址脚都拉低,7 位地址落在 0x4C;如果你把其中一个接到 VCC,地址就会变成 0x4D 之类。实践上建议做法是:上电后先写一个 I2C 总线扫描程序,把 0x00 到 0x7F 扫一遍,看看传感器实际响应哪个地址,再固定写进配置。不要凭记忆硬编地址,我亲眼见过别人因为地址位没拉对,读了一天全是 0xFF。
ALERT/THERM 这类报警输出引脚,在嵌入式板卡上值得接到 PIC32MZ 的某个 GPIO 中断上。它通常用开漏输出,要加上拉;温度超限时由传感器自己拉低,主控即使正在忙其他事情也能立刻知道,不用靠轮询。
2.2 D+/D- 的接法:三极管当“感温头”
远程通道的 D+ 和 D- 需要一个真正的二极管结。工程上最常见的做法是直接用一个小信号三极管的发射结,因为三极管来源广、一致性比乱七八糟的稳压管好,而且封装小,方便贴在测温点上。NPN 型如 MMBT3904,PNP 型如 MMBT3906,都能用。接法记住一句话:发射极进 D+,基极进 D-,集电极悬空。这样组成的二极管结方向,才能保证传感器在正偏状态下做电流切换。
在 HVAC 现场,我习惯把三极管焊在一小片覆铜板上,外面套不锈钢套管,再灌导热胶,做成一枚“管道温度探头”。这样探头的热时间常数可控,插到风管里也不怕凝露。嵌入式场景里,可以直接把三极管贴在 IGBT 模块表面,比如先涂导热硅脂垫一层绝缘片,再把管体压紧,读到的就是功率模块表面的实时温度,比贴在旁边的 NTC 反应快不少。
有一点要特别留意:D+/D- 这一对线走的是差分测温,但传感器芯片本身对线间电容比较敏感。如果为了滤波在 D+/D- 之间加了大电容,比如 10nF 以上,转换结果会出现明显滞后甚至偏差。我后续调试中用的做法是:容值控制在 1nF 以内,或者干脆只在对地各加一个 100pF 到 1nF 的小电容,而不在线间跨接大电容。
2.3 远程感温线的布线与保护
远程二极管测温的原理决定了它对串联电阻、线间噪声有一定容忍度,但这不意味着可以随便飞线。我实测过,线径太细、距离超过 20 米、又靠近变频器输出线时,读数跳动能达到 ±2°C 以上。最理想的布线形式是双绞线,让 D+ 和 D- 受到的电磁干扰尽量共模化,再靠传感器的差分测量抵消掉。
在电路板上,从 PIC32MZ1024EFF144 这侧看,D+/D- 的过孔和走线要远离开关电源的电感、MOSFET 栅极驱动走线,特别是不要和 PWM 信号线长距离平行。如果空间允许,在 D+/D- 进传感器引脚前加一级 RC 低通,例如串 100Ω,对地各并 1nF,既抑制噪声,也不会明显影响响应速度。现场远距离应用时,我会在探头端加一个小的 TVS 管,防止雷击感应或机组启停时产生的浪涌损坏传感器内部电路。
3. 固件实测:寄存器读写、温度换算与远端二极管标定
3.1 用 Harmony 配置 I2C 外设,先扫地址
固件这层我是基于 MPLAB Harmony v3 搭的。在图形配置界面里把 I2C1 设为 Master 模式,7 位地址,速率先跑 400kHz。如果你所在环境电磁干扰比较严重,也可以先降到 100kHz;反正温度数据量不大,100kHz 也绰绰有余。I2C 时钟本身就来自外设时钟分频,确认分频后的 SCL 频率不要超规格,这是最基础的一步。
配置完成后,第一步先做 I2C 地址扫描。这个方法在调试初期特别有用,能把总线问题、地址问题和接线问题快速分开。
void i2c_scan(void) { for (uint8_t addr = 0x08; addr < 0x78; addr++) { // 封装一个 I2C read 单字节并带 ACK 检测的函数 // 如果该地址有设备 ACK,打印出来 if (i2c_probe(addr)) { // 记下实际地址 printf("I2C found: 0x%02X\n", addr); } } }我实际扫出来的地址就是 0x4C,说明 A0/A1 都接地生效了。如果你扫不到任何地址,优先检查 SDA/SCL 是不是接反了、上拉电阻焊没焊、电源给没给上,而不是去怀疑寄存器配置。这一步排查顺序,能帮你在前半小时解决 80% 的联机问题。
3.2 读远程和本地温度:寄存器换算的完整代码
确认地址之后,就要读温度数据了。以我手头这颗 PJ85718DM 的寄存器版式为例,本地温度寄存器在 0x00,远程温度寄存器在 0x01,配置寄存器在 0x03。需要说明的是,不同批次、不同封装的同类器件寄存器位定义可能会有细节差异,移植代码时请务必以你自己拿到的数据手册为准。
#include <stdint.h> #define ADDR_TEMP 0x4C #define REG_LOCAL_TEMP 0x00 #define REG_REMOTE_TEMP 0x01 #define REG_CONFIG 0x03 // 读取两个字节的温度寄存器 static uint16_t read_temp_reg(uint8_t reg) { uint8_t buf[2]; uint8_t reg_addr = reg; // 这里调用你自己的 I2C 库函数: // 先发送器件地址+写位,发送寄存器地址, // 再发送器件地址+读位,连续读 2 字节。 i2c_write(ADDR_TEMP, ®_addr, 1); // 指定寄存器 i2c_read(ADDR_TEMP, buf, 2); // 连续读两字节 return (uint16_t)((buf[0] << 8) | buf[1]); } float read_remote_temp_celsius(void) { uint16_t raw = read_temp_reg(REG_REMOTE_TEMP); // 远程温度寄存器常见格式:高字节 + 低字节的高 3 位, // 低 3 位是状态/保留位,右移去掉。 raw >>= 3; // 如果是 11 位有符号数,位 10 是符号位,手动扩展成 16 位有符号 int16_t signed_raw = (int16_t)(raw & 0x07FF); if (signed_raw & 0x0400) { signed_raw |= (int16_t)0xF800; } // LSB = 0.125°C return (float)signed_raw * 0.125f; }本地温度读取流程完全一样,只是把寄存器地址换成 0x00。可以顺带把本地和远程温度放在同一帧结构体里,再连同时间戳一起交给上层逻辑处理,避免出现“本地是上一秒读的、远程是这一秒读的”这种不一致,在需要做温度差判断时会是个隐患。
3.3 标定流程:两点偏移校正与 β / 串联电阻补偿
远程二极管通道出厂时是按理想的二极管模型校准的,但实际用的三极管不可能跟理想模型完全一致。如果只要求 ±1~2°C 的监控精度,其实可以忽略差异;但如果你做的是精确控温,比如恒温槽、实验室环境舱,就需要做一次标定。
我的标定流程很简单:准备一个能控温的恒温槽,或者一块带测温孔的铝块,把高精度温度计和远程探头放在同一位置。分别在 25°C 和 45°C 两个温度点读一次芯片输出,算出两个偏移量。
float remote_apply_offset(int16_t raw_celsius) { // 25°C 处偏移 +0.375°C,45°C 处偏移 +0.875°C // 根据两个标定点做线性插值 static const float ref[2] = {25.0f, 45.0f}; static const float off[2] = {0.375f, 0.875f}; float slope = (off[1] - off[0]) / (ref[1] - ref[0]); return (float)raw_celsius + off[0] + slope * ((float)raw_celsius - ref[0]); }两点的偏移如果差不多,说明主要是二极管离散性造成的固定偏差,加一个常数就行。如果偏移量随温度明显变化,就要怀疑是不是三极管的发射结串联电阻偏大,或者 β 值偏低导致传感器内部电流比切换时出现了额外误差。这时优先换一颗来源可靠、一致性好一些的 MMBT3904 再试,通常比调程序里的补偿系数更省事。
提示:标定完成之后,把偏移量写进芯片自带的远程偏移寄存器,可以做到“硬件级修正”,即使主控程序重刷也不会丢。但很多这类寄存器的粒度是 0.125°C,写入后记得回读确认。
4. HVAC 与嵌入式两套数据滤波策略:同一颗传感器不同脾气
4.1 HVAC 追求稳定:低通滤波和慢速率变化
温度采样本身不难,难的是怎么把数据交给控制逻辑。暖通空调这边,风管里的温度变化本来就慢,压缩机启停周期至少是以分钟计。如果直接把每一次 I2C 原始读数交给 PID,哪怕只有 0.125°C 的量化噪声,都可能让执行器频繁动作。所以我在 HVAC 应用里用的策略是低通滤波 + 慢周期采样。
具体做法可以每 500ms 读一次数据,喂给一阶低通:
float lowpass_filter(float previous, float current, float alpha) { return alpha * current + (1.0f - alpha) * previous; }在风柜回风温度这种场景,alpha 取 0.05~0.10 就行,等效时间常数大约在 3~10 秒,既能反映温度趋势,又不会因为风机启停导致读数剧烈抖动。你可能会担心这样响应太慢,但 HVAC 系统的被控对象本来就是一个大惯性系统,几秒钟的滞后完全可接受。
这里有一个细节:滤波系数最好不要用一个常量写死,而是根据温差动态调整。比如当温度超过设定值 3°C 以上时,我会把 alpha 提高到 0.3,让报警信号快速响应;正常范围内又降回 0.05,保持稳定。这样既不会误报,也不会让真正的超温被滤波“吃掉”。
4.2 嵌入式保护要快:短窗口、异常跳变丢弃
嵌入式设备的热保护跟 HVAC 完全是两码事。IGBT 在过载瞬间温度爬升非常快,电源模块在短路时几毫秒内就可能超出安全温度,滤波如果太重,保护动作就会迟到。这一侧我用的策略是“短窗口滑动平均 + 跳变剔除”。
比如每 10ms 读一次,取最近 8 次数据做平均,等效滞后大约 40ms 左右,对过温保护来说足够。读数如果比上一拍突变了 8°C 以上,大概率是干扰或者接线松动,先丢弃这一拍,再用上一次的值顶上。这里不能盲目相信单个数据,因为远程二极管通道在受到强干扰时,偶尔会冒出一个离群值,直接把它当成真实温度去触发保护,是很危险的事。
static float temp_buf[8]; static uint8_t temp_idx; float embedded_temp_check(float current) { // 跳变剔除 if (fabsf(current - last_valid) > 8.0f) { return last_valid; } temp_buf[temp_idx++] = current; if (temp_idx >= 8) temp_idx = 0; float sum = 0.0f; for (int i = 0; i < 8; i++) sum += temp_buf[i]; float avg = sum / 8.0f; last_valid = current; return avg; }两种场景放一起看,思路完全不同:HVAC 怕误动作,所以把系统做“钝”;嵌入式怕保护不及时,所以把窗口做“锐”。同一个传感器、同一份原始数据,最后呈现在不同系统里的行为差别很大。
4.3 状态与告警处理:ALERT 引脚怎么接进固件
温度滤波只解决了数据好不好用的问题,真正要落地到保护逻辑,还需要把告警拉出来。PJ85718DM 这类器件一般都配了 ALERT 或者 THERM 引脚,主机可以给它写上限、下限,由传感器自己持续比较温度,超限就拉低引脚。
我在 PIC32MZ1024EFF144 这边的做法是:把 ALERT 接到一个能用边沿中断的 GPIO 引脚,下降沿触发中断。在中断里只做“置标志 + 记录发生时间”,不进行复杂滤波计算;主循环检测到标志后,再进一次正常的温度读取流程,确认是否真的超限。这个二次确认机制很关键,能避免因为一次 I2C 毛刺就误触发保护。
如果系统里有多路温度传感器,ALERT 引脚可以都接到同一个 GPIO 上做成“线与”。哪一路超限都会拉低总线,主控通过扫描不同的 I2C 地址去定位到底是哪个传感器报警,这在多温区控制的 HVAC 机组里非常实用。
5. 实测之后绕不开的几个坑:远端感测噪声、错误接线与地线压差
5.1 远程通道极性接反:读数瞬间变成负数
第一次调试时,我随手拿了一颗贴片 NPN 管,按“发射极接 D-、基极接 D+”的老印象接上,结果读出来的远程温度在 -20°C 左右,而实际室温是 25°C。检查了半天才发现是极性接反了。
正确接法是发射极接 D+,基极接 D-。如果你用的是 SOT-23 封装三极管,管脚定义很紧凑,建议每一批板子回来都先对照规格书确认丝印方向,不要凭上一批的经验直接焊。这种错误在实验室里只是数据不对,到了现场就很容易误判成传感器损坏。
5.2 高速开关噪声让原始值乱跳:从源头压,而不是堆滤波
还有一次我把 D+/D- 走线从一块开关电源的电感底下穿过,结果远程温度读数间歇性跳变,跳变量大概 0.5~1°C,用示波器看才发现线上叠加了一堆振铃。我一开始想在程序里加大滤波系数,确实能压住,但把真实的热响应也拖慢了。
后来改成双绞线走线、让两根线贴着走、离开电感区域,并在传感器端对地各加 1nF 电容,问题基本消失。这里我想说的经验是:滤波应该是最后一道保险,而不是第一道防线。能从布局布线解决的问题,就不要用软件滤波去扛;否则温度数据“看起来平滑”,实际物理位置的热变化却已经被掩盖了。
5.3 现场地线压差导致整体偏移:把远程线当成差分对看
在 HVAC 现场最容易忽略的是地电位差。一套机组里,变频器、压缩机、控制器各有各的接地点,两个接地点之间可能有 0.1~0.3V 的直流压差。如果 D+ 和 D- 在地线上形成环路,传感器测量的 ΔVBE 就会叠加一个共模误差,读数出现整体偏移。
解决思路是把 D+/D- 当成一对真正的差分信号来布:从探头到传感器尽量不经过其他设备的接地回路,屏蔽层单端接地,避免形成多点接地环路。如果实在绕不开,就在传感器端对地加共模滤波。实测下来,把线改成双绞并远离动力线后,原本 1°C 多的偏差能降回到 0.2°C 以内。
5.4 读出来全是 0x7FFF,先从总线时序找原因
最后说一个调试中最常见的“假故障”:I2C 读回来的值不是 0xFF 就是 0x7FFF。一开始我也怀疑是传感器坏了,换了一颗还是如此,最后定位到是 SCL 频率配得太高,加上飞线过长,导致从机应答信号没有被主机正确采样。
这类问题的排查顺序建议是:先降速到 100kHz,再拿示波器看 SCL/SDA 波形,确认 ACK 位是否干净。如果降速后读数正常,那就是高频振铃问题,别急着换芯片。温度传感器对时序的要求并不苛刻,但长线、跳线、手焊的杜邦线都会破坏信号完整性,稳定优先,再把速度提上去。
最后分享一点经验
整套系统调完,我最深的体会是:PJ85718DM 这类“本地+远程”双通道传感器,设计的真正价值不只是省一个 I2C 地址,而是把热管理的核心问题——近点保护与远点监测——统一成同一套数据访问方式。PIC32MZ1024EFF144 的算力对这种应用来说绰绰有余,真正决定项目成败的反而是布线和标定这些不起眼的细节。如果你手头的项目也要做嵌入式板温或者 HVAC 温度监控,我建议先画一份 D+/D- 的走线约束图,再写代码;等硬件稳定了,固件这边基本就是读寄存器、标准化处理和滤波三件事。下一步我会把这套方案再接一路 Modbus 转出去,让楼宇监控后台能直接读到每一个温区的实时数据,这也算把“本地与远程”的最后一公里打通了。