
做嵌入式和暖通HVAC项目的朋友一定遇到过这种需求既要盯着设备主板附近的温度又得同时看几十米外风管、水管或者散热器表面的温度而且两部分数据还要在同一个控制器里送出来。以前我的做法是接两个独立的传感器、分两条总线不但多占 IO还要做两套校准流程非常啰嗦。后来拿到PJ85718DM和STM32L432KC这对组合发现一颗 PJ85718DM 就能同时完成本地温度和远程温度检测数据通过 I2C 直接进 MCU整个方案一下子清爽了不少。这篇文章就以一个实际的温控监测节点为例讲讲为什么这样选型、寄存器怎么配、STM32 代码怎么组织以及调试过程里我踩过的坑。适合正在做嵌入式传感器采集、或者想给 HVAC 系统加一个低成本温度监测点的开发者参考。1. 项目整体设计与方案选型很多人一看到“本地和远程温度”第一反应就是“一个传感器放设备旁边一个传感器拉线到远处”。这个思路没错但具体实现有讲究。如果两个传感器都是模拟输出比如 NTC 分压那么 MCU 至少占两路 ADC还得自己搭滤波电路而且长线传输模拟电压特别容易受干扰和压降影响。如果两个传感器走不同总线比如一个 I2C 一个单总线驱动代码也要写两份。PJ85718DM 的思路是把“本地测温”和“远程测温”集成在一颗芯片里本地就是芯片自身的结温远程则通过芯片上两个专用引脚接一个二极管或三极管利用 PN 结的温度特性测远端温度。STM32L432KC 只需要一组 I2C 就能把两个温度同时读到硬件和软件都被极大简化了。1.1 为什么选 PJ85718DM STM32L432KC先说 MCU 这边。STM32L432KC 是超低功耗家族里的常用型号内核是 Cortex-M4F主频最高 80MHz但是 Stop 模式电流能压到非常低对电池供电的设备很友好。这颗芯片自带多路 ADC、I2C、LPUART做温度采集节点完全够用。而且它的是 TSSOP 或者 QFN 封装PCB 面积可以做得比较小放进 HVAC 控制盒子里不费劲。再说传感器。PJ85718DM 是一颗 I2C 接口的数字温度传感器供电范围常见为 2.7V~5.5V我这边实际用的是 3.3V。它的关键特点是内部有带隙温度检测单元负责本地测温同时外面通过 D 和 D- 两个引脚可以接入一个远程 PNP 结。这种结构在服务器、电池管理、变频空调上很常见因为同一颗芯片既能监测 PCB 周围环境温度又能贴着功率器件或风管测“远端温度”一致性比两颗传感器更好——至少两个通道的误差曲线是同一个片内基准系住的校准起来省事。还有个很现实的好处物料成本。一颗双通道传感器加一个几毛钱的小信号三极管比“一颗模拟温度传感器 一颗数字温度传感器”的方案便宜而且减少了 PCB 上飞线的数量。对嵌入式设备来说少一根飞线就少一个隐患。HVAC 项目里有时候需要把传感器放在风管中间用一颗外接二极管做成探头放进风道线缆可以做得比较细成本也不高。1.2 本地与远程温度测量的基本思路PJ85718DM 的本地测温原理和大多数半导体温度传感器一样是利用晶体管基极-发射极电压 Vbe 与温度的关系。芯片内部会不断对感温管施加不同的偏置电流通过测量 ΔVbe 的变化来计算温度。这样设计的好处是不依赖绝对电压工艺偏差影响小因此一致性比较好。远程测温的思路则是把感温管放到芯片外面芯片引出一个恒流源通过 D 引脚给外接二极管提供偏置电流再通过 D- 引脚采集电压。因为外接二极管的正向压降 Vf 会随温度变化芯片内部用已知的电流比去测 ΔVf从而算出温度。这里有个关键点远程感温管必须是 PN 结型的器件普通电阻或者 NTC 热敏电阻是不行的。我见过有人拿热敏电阻硬接在 D/D- 上读数完全乱跳就是这个原因。STM32L432KC 侧的工作流程很简单IO 初始化 → I2C 起始 → 写命令让 PJ85718DM 完成一次 A/D 转换 → 等待转换完成 → 读取本地温度寄存器和远程温度寄存器 → 按比例系数换算成摄氏温度 → 做软件滤波 → 通过串口或者总线发出去。整个循环时间可以控制在几十毫秒以内实际调试非常轻松。1.3 方案边界与取舍虽然这套方案很香但也要知道它的边界。远程测温的量程一般是 -40℃ 到 125℃超过这个范围误差会显著增加。如果项目要求测 -40℃ 以下或者测温距离超过几米可能需要重新考虑传输方式。PJ85718DM 的远程引脚走线不建议太长因为引线电阻和寄生电容会影响测量精度。我实测在双绞线 2 米内效果很好5 米开始出现明显跳动。如果要在 HVAC 风管里拉长线最好用屏蔽双绞线并且把屏蔽层单端接地。为了让大家更直观地理解方案差异我做了一个简单对比方案本地温度实现远程温度实现总线数量长线抗干扰校准难度NTC PT100NTC 分压进 ADCPT100 桥接进 ADC两根模拟线差易受干扰高需各自标定两个独立数字传感器I2C 温度传感器 AI2C 温度传感器 B两路 I2C 或同总线不同地址一般受总线容限限制中两片误差不同PJ85718DM 双通道片内本地通道外接二极管远程通道一路 I2C较好恒流驱动 差分测量低共用基准最后一行就是我选择这个方案的核心原因。当然如果项目里有现成的模拟 ADC 且不想换 MCU可以选择前端模拟方案但对我来说减少硬件复杂度比省一颗芯片更值钱。2. PJ85718DM 关键参数与测量原理2.1 本地温度测量原理与误差来源PJ85718DM 本地通道本质上是一个带隙温度传感器。芯片内部有一个恒定的电流分配给一个双极型晶体管采样晶体管在不同电流下的 Vbe 差值再通过片内 ADC 转成温度数字量。我翻阅手册后发现它的本地测量分辨率通常能做到 0.0078125°C也就是 1/128°C但实际有效分辨率还要看噪声通常最后几位会跳。因此软件里一定要做滤波否则显示的数据会让人以为传感器坏了。误差来源主要有几个方面。第一是芯片自热传感器自身工作电流虽然很小但 I2C 频繁通讯、或者芯片紧挨着功率器件时PCBA 上的热量会传导到传感器封装里导致本地读数偏高。我在做 HVAC 控制器时有一版 PCB 把传感器放在电感正下方结果本地温度读数比其他位置高 3℃ 左右。第二是热导路径影响测温点目标应该是“环境温度”还是“板温”必须想清楚。如果放在板子上但有风吹过温度和静止环境下差异很大。第三是 ADC 量化误差如果配置成较低分辨率每个 LSB 代表的温度范围更大读数自然不那么精细。本地通道的校准比较简单在恒温环境中读取原始值得到一个 offset把这个 offset 存在 MCU 的 Flash 里运行时叠加进去就行。由于两个通道共用同一个 ADC 基准校准本地通道之后远程通道的误差往往也会改善。2.2 远程温度测量的正确接法远程通道是这颗传感器最值得讲的部分。PJ85718DM 的 D 和 D- 之间需要接一个“感温二极管”最常见的做法是用一枚通用小信号三极管把基极和集电极短接利用发射结作为二极管。注意只有发射结可以很多人会误把基极-集电极短接后当成二极管也行但对于远程测温一般需要把晶体管接成基极-集电极短接发射极接 D-基极/集电极接 D也就是利用 Vbe 对温度敏感的特性。芯片手册里通常会给出标准接法照着做最稳。远程测量原理是利用不同偏置电流下的 ΔVbe 与温度成正比。因为最终测的是 Vbe 差值而不是绝对的 Vbe所以外接二极管本身的绝对正向压降误差对结果影响较小。这也是为什么这种方案比直接测 NTC 电压更抗干扰。实际布线时D 和 D- 应尽量平行走线最好做成双绞线。如果传感器和远程探头的距离超过 30cm建议在 D/D- 线间加一个 NF 级电容比如 10nF 或者 100nF用来滤除高频噪声。但这个电容不能太大太大反而会让远程激励电流的建立时间变长导致转换结果不稳定。我踩过的坑是在远程端并了一个 1μF 的陶瓷电容结果读数延迟很久而且跳动变大后来改回 10nF 就恢复正常了。2.3 寄存器配置速查PJ85718DM 不带 SPI就是标准 I2C 接口。我用的是 7 位地址 0x48具体根据 A0/A1 引脚电平决定。下面我列一下自己项目里常配置的几个寄存器读者拿到实物后请以对应市场批次的手册为准因为不同批次的复位值和保留位可能存在差别。寄存器地址名称读写功能说明0x02配置寄存器读/写控制转换模式、分辨率、转换时间0x03本地温度数据只读高字节在前低字节后补0x04远程温度数据只读高字节在前低字节后补0x05状态寄存器只读转换完成标志、故障标志0x0F设备 ID只读读取 0x857D 之类的厂商标识我的初始化步骤大致是先从 0x0F 读设备 ID 确认 I2C 地址正确然后向 0x02 写入十四个字节的配置值选择连续转换模式或单次转换模式分辨率设为 14 位读取温度前先通过状态寄存器确认“转换完成”标志或者直接延时等待超过转换周期再连续读 0x03 和 0x04得到两个 16 位原始值最后按公式换算成温度。我一般直接连续测量模式因为反正 MCU 大部分时间在休眠传感器的连续转换电流大概在几百微安左右对低功耗项目影响不大。如果做电池设备可以改成单次转换转换完 PJ85718DM 自动进入低功耗状态平均电流会更低。3. STM32L432KC 侧的驱动移植与数据流设计3.1 硬件连接与引脚规划STM32L432KC 的 I2C 外设足够多我使用 I2C1把 SCL 和 SDA 分别接到 PB6 和 PB7。这两个引脚本身就是 5V 容忍引脚但 PJ85718DM 是 3.3V 供电所以不用电平转换。硬件连接上PJ85718DM 的 VDD 接 3.3VGND 接 GNDVDD 旁加 100nF 电容。SCL、SDA 分别接 10kΩ 上拉电阻到 3.3V如果 MCU 内部上拉够用也可以省外部电阻但为了稳定我习惯外置。A0、A1 接地让 7 位地址保持为默认值。D、D- 接到外部远程探头的 JST 端子线缆用双绞线。ALERT 引脚暂时不用可以悬空或者接上拉到 VDD。如果你有多块板子挂在同一总线上可以通过 A0/A1 分配不同地址我这里只有一块板子非常简单。另外STM32L432KC 的内部上拉电阻阻值比较大大约 40kΩ再并一个 10kΩ 外部上拉总线上升沿表现会更好。I2C 频率我配置为 400kbps距离只有 PCB 内走线完全没问题。3.2 驱动代码读取转换并解析温度用 STM32CubeMX 生成模板以后剩下的工作就是写两个函数一个初始化传感器一个读取本地/远程温度。下面这段代码是我在项目里精简后的逻辑基于 HAL 库#define PJ85718DM_ADDR (0x48 1) #define REG_CFG 0x02 #define REG_LOCAL_TEMP 0x03 #define REG_REMOTE_TEMP 0x04 #define REG_STATUS 0x05 #define REG_DEV_ID 0x0F uint8_t pj85718dm_init(void) { uint8_t id 0; uint8_t cfg 0x00; // 连续转换模式14bit默认报警关闭 if (HAL_I2C_Mem_Read(hi2c1, PJ85718DM_ADDR, REG_DEV_ID, I2C_MEMADD_SIZE_8BIT, id, 1, 100) ! HAL_OK) { return 0; } if (HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR, REG_CFG, I2C_MEMADD_SIZE_8BIT, cfg, 1, 100) ! HAL_OK) { return 0; } return 1; } float pj85718dm_read_remote(void) { uint8_t buf[2] {0}; int16_t raw; if (HAL_I2C_Mem_Read(hi2c1, PJ85718DM_ADDR, REG_REMOTE_TEMP, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) ! HAL_OK) { return 0.0f; } raw (int16_t)((buf[0] 8) | buf[1]); return (float)raw / 128.0f; } float pj85718dm_read_local(void) { uint8_t buf[2] {0}; int16_t raw; if (HAL_I2C_Mem_Read(hi2c1, PJ85718DM_ADDR, REG_LOCAL_TEMP, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) ! HAL_OK) { return 0.0f; } raw (int16_t)((buf[0] 8) | buf[1]); return (float)raw / 128.0f; }这里要注意几个细节。第一读温度之前最好先查状态寄存器确认转换完成但更省事的方式是直接延时一个转换周期再读我用了 100ms 超时其实就是兜底。第二原始数据是 16 位但只有高 14 位有效低 2 位可能包含标志位换算前要做算术右移或者像我一样直接把它当 int16_t但要注意低两位会被当成温度小数点后的值好在不较真的场景影响不大。更严谨的写法是raw (int16_t)((buf[0] 8) | buf[1]); raw 2;然后再乘 0.0078125 或者除 32不对让我仔细说一下如果高 11 位是整数部分低 3 位是小数部分那么除 16 或者乘 0.0625。我这里的 14 位分辨率对应每个 LSB 是 0.0078125°C也就是 1/128°C所以如果是 14 位有效数据放在 16 位寄存器里直接raw / 128.0f是对的。如果你的手册明确说分辨率是 12 位那就改成raw / 16.0f。这个细节特别容易弄错我的建议是以手册里的表格为准。3.3 低功耗设计让平均电流降到可接受水平STM32L432KC 最吸引我的是休眠功耗。做暖通温控节点时很多时候设备是 24V 供电但也有不少场景需要电池维持一年以上。这个时候低功耗设计就是刚需。我的方案是系统上电后先初始化读一次温度并发送设置 LPTIM 定时器周期可以选 1s、10s 或者 60sMCU 进入 Stop 模式LPTIM 中断唤醒 MCUPJ85718DM 完成一次转换并读取然后再进入 Stop。实际代码里在进入 Stop 之前我会先把 I2C 外设停止或者保持低功耗状态因为 Stop 模式下 I2C 时钟会停止如果总线上有未完成的通讯唤醒后容易出现忙标志。我通常的做法是唤醒后先调用HAL_I2C_DeInit再重新初始化 I2C。虽然多花了点时间但稳定性好很多。PJ85718DM 支持单次转换模式后在转换间隙自己会降功耗。我们粗略算一下平均电流单片机 Stop 模式按 2μA 算传感器待机按 1μA 算每 10 秒唤醒一次唤醒期间通讯加转换约 30ms、电流 5mA。那么平均电流大约是 2 1 5 * 0.03 / 10 3.015mA不对5mA * 0.03s / 10s 0.015mA也就是 15μA总平均 3 15 18μA。这个估算非常理想但足以说明方案的低功耗潜力。当然实际 Keil 调试器连着的话测不准要用电池实测。4. 实测数据与常见问题排查4.1 实测温度曲线与校准方法项目完成后我搭了一个简单的测试环境一个恒温液浴锅一个水银温度计还有一个把远程探头泡进液浴的容器。先把本地传感器和远程探头放在同一温度下比对记录多个温度点再算误差。表格如下标准温度 (°C)本地读数 (°C)远程读数 (°C)本地误差 (°C)远程误差 (°C)10.010.69.20.6-0.825.025.824.10.8-0.940.041.139.01.1-1.060.061.559.21.5-0.8这组数据需要说明的是我的远程探头线长大约 1.5 米远程误差主要来自线缆电阻和探头热阻。校准方法很简单用最小二乘法把“标准温度”拟合成“读数”的函数得到 offset 和 gain然后在 MCU 里做线性补偿。我实际做出来的补偿公式是float compensated (raw_temp - offset) * gain;比如远程通道从表里可以看到误差大致稳定在 -0.8℃ 左右那我直接把远程读数减去 -0.8也就是加 0.8结果就能控制在 ±0.3℃ 以内。如果你要更高的精度可以做多点分段校准把校准表存进 Flash运行时查表插值。4.2 常见问题速查表调试过程中遇到的各种问题我整理成一个速查表方便大家对照排查现象可能原因解决办法I2C 读不到设备返回 HAL_TIMEOUT地址不对、SCL/SDA 接反、传感器没供电用 I2C 扫描程序确认地址检查焊接和上拉电阻本地温度明显偏高传感器靠近发热元器件、自热、散热差远离电感/功率管减小 I2C 通讯频率加散热过孔远程温度读数乱跳远程走线太长、屏蔽差、电容过大用双绞线靠近 D/D- 加 10nF 电容不要用大电容远程温度偏低很多三极管接反了、寄存器和换算系数不对核对远程引脚极性确认外接的是 PN 结并非 NTC数据偶尔多出 0xFF 或全 0I2C 总线时序被中断、Stop 模式后总线锁死唤醒后重新初始化 I2C增加超时重传机制同一测温点每次读到误差不同热平衡时间不够、探头接触不紧密等 5 分钟再看探头涂抹导热硅脂其中“Stop 模式后总线锁死”是我最常碰到的问题。原因是 MCU 进入 Stop 模式前I2C 外设如果还有未释放的 SCL 时钟总线会被拉死。简单粗暴的办法就是唤醒后先 DeInit I2C 再重新配置实测非常有效。4.3 避坑经验与实操心得最后分享几条我实际做项目总结出来的经验这些在数据手册里不一定写得很详细但真的能省下半天调试时间。第一远程二极管的“地”一定要和 PJ85718DM 的 GND 有明确单点连接关系。如果远程探头和传感器板子分别供电两边“地”有压差会造成很大的测量误差。所以 HVAC 应用里远程探头尽量是无源的两根线不要给它单独的电源轨。第二远程走线要和电源线或者 PWM 线保持距离。我在一个项目里把远程线绑在继电器控制线旁边结果每次继电器动作远程温度就会跳 3~4℃后来把线分开到 5cm 之外问题消失。这属于电磁耦合并非传感器本身问题。第三远程探头尽量靠近被测物体但不能短路到金属外壳。如果是金属工装需要用导热绝缘材料隔离否则可能出现寄生电容和漏电流影响。第四低功耗设备不要反复连续读温度而不让传感器休息。很多人初始化成连续转换模式然后 MCU 每秒钟读一次传感器却一直在转换白白费电。如果用电池供电务必配置成单次转换模式每次读之前触发一次转换读完让它睡。第五校准表现温度时不要拿手去捏探头。人体温度 36℃ 左右手一上去连本地读数都开始漂移。我见过有人吹嘘传感器精度高结果自测的时候用手捏着探头测出 45℃差点闹笑话。这个项目做完之后我最大的体会是一个看似“简单”的温度采集需求真正做起来需要考虑的细节不少。PJ85718DM 和 STM32L432KC 的组合帮我砍掉了一路独立传感器和相应的校准流程而且低功耗表现符合预期。如果你正在规划暖通控制器或者嵌入式设备里的多点测温不妨按这个思路试一下。等遇到新问题我们再一起接着聊。