PJ85718DM与STM32F042的远程温度监测系统设计实践

发布时间:2026/10/22 1:07:30
PJ85718DM与STM32F042的远程温度监测系统设计实践 去年下半年接了个设备配套的控制板项目需求一句话就能说完柜内要测环境温度柜外还要测两路远端温度一路是空调风管里的送风温度另一路是冷水盘管表面温度。整套系统要长期跑、要准、成本还不能高。最后我给的方案是用 PJ85718DM 这颗 SMBus 温度传感器搭配 STM32F042C6 这颗 Cortex-M0 控制芯片一颗负责把本地温度和远端二极管温度变成数字量一颗负责读数据、校准和执行控制逻辑。这套组合放在嵌入式和 HVAC 温度监测场景里都挺能打这篇文章把选型理由、硬件接法、软件换算、实测调校和踩坑记录完整整理出来给准备抄作业的朋友一个参考。1. 为什么是“一颗传感器 一颗 M0”先把分工理清楚1.1 本地温度和远程温度其实面对的是两个完全不同的问题很多刚开始做温度采集的人会觉得“本地”和“远程”只是距离差几厘米的事但工程上完全不是一回事。嵌入式的本地温度通常指 PCB 附近或机箱内的环境温度关心的是器件散热条件、整机热平衡安装位置要能代表“你想控制的那个区域”。而远程温度要么贴在功率 MOSFET、IGBT、FPGA 这种发热器件本体上要么像 HVAC 那样放在风管、盘管、冷凝器这些离控制板几米远的地方。这里的核心矛盾是本地测温要求传感器尽量靠近控制点但不能被热源干扰远程测温要求传感器物理上离开主控板信号又要原样传回来。MCU 内部的温度传感器测的是芯片 die 温度既代表不了环境温度也根本够不到远端这就是为什么需要外接一颗专用温度传感器。1.2 为什么不是 NTC 加 ADC也不是热电偶有人会问测温度嘛用 NTC 热敏电阻加 MCU 的 ADC 不行吗行但很累。NTC 的阻值-温度曲线是强非线性的你需要在固件里查表或者做多项式拟合引线长了以后线阻和噪声都会叠加进去校准起来非常痛苦。ADC 的参考电压、PCB 漏电流也都可能变成误差源。热电偶更麻烦它输出的是微伏级信号必须做冷端补偿还要考虑放大器的零点漂移在消费级设备里成本完全压不住。PJ85718DM 这种专用数字温度传感器内部自己就有恒流源、ADC、比较器和报警逻辑MCU 只需要通过 I2C 读寄存器。等于把测温中最容易出错的“模拟信号调理”交给专用芯片MCU 专心干活这是这类系统评估下来最省心的做法。1.3 为什么挑 STM32F042C6主控选 STM32F042C6 有几个现实原因首先是成本Cortex-M0 内核 48MHz32KB Flash、6KB RAM跑个 I2C 读取、滑动平均滤波、Modbus 或者简单的继电器控制完全够。其次是外设F042 自带的 I2C 硬件接口配 SMBus 器件很顺手不像某些芯片还得用 GPIO 软模拟 I2C。再者48 脚 LQFP 封装在中小型控制板上布线很舒服GPIO 数量也够用。不过这里要提醒一句F042 的 RAM 只有 6KB如果上位机通信协议很复杂或者想上 FreeRTOS就得掂量一下了。我的场景里固件逻辑不复杂6KB 绰绰有余。2. PJ85718DM 的测温原理与硬件接法本地一路、远程一路方式完全不同2.1 本地测温片上热传感器直接数字化PJ85718DM 的本地温度通道实际测的是芯片内部半导体结的温度而不是直接测空气。芯片内部有一个热敏元件通常是带隙基准的一部分配合内部的模数转换器把温度量化成数字。分辨率常见是 12 位也就是每步 0.0625°C这个精度对 HVAC 和嵌入式热管理都足够。需要特别注意本地温度是“die 温度”芯片装在 PCB 上它的读数会明显受到板子发热的影响。如果你想用本地通道代表“机箱内环境温度”PCB 布局上就要让传感器离那些大电流走线、LDO、MOSFET 远一点。我见过有人把传感器贴在 DC-DC 电感正下方本地读数凭空高了好几度然后到处怀疑芯片不准其实是被自己坑了。2.2 远程测温靠两档电流激励远端三极管的 ΔVBE远程测温是整个系统的灵魂。它的原理不复杂但值得说透。远端不需要再放一颗数字传感器只需要一个二极管接法的晶体管常见型号是 2N3904、2N3906或者贴片的 MMBT3904、MMBT3906。把晶体管的基极-集电极短接形成一个二极管传感器芯片通过 D 和 D- 两根线给它灌电流。晶体管发射结的正向压降 VBE 跟温度近似线性但 VBE 的绝对值受工艺和电流影响很大直接测单点 VBE 没法得到准确温度。所以芯片用的是“两档电流法”。先通一个小电流 I1量一次 VBE1再通一个大电流 I2比如 I1 的 4 倍量一次 VBE2。两者相减ΔVBE VBE2 - VBE1 (n×k×T/q) × ln(I2/I1)其中 n 是发射结理想因子k/q 是常数 86.17 µV/K。这个 ΔVBE 只跟温度 T 和电流比有关跟 VBE 的绝对值、工艺离散基本无关所以才能做到比较高精度。ln(4) 大约 1.386在 300K 附近 ΔVBE 大约 36mV 左右。这段解释了我为什么坚持用专用芯片而不是自己搭恒流源加 ADC两档电流的切换时序、ΔVBE 的差分测量、理想因子补偿自己做要写很多代码而且模拟误差很难控。接线方式记住一个通用习惯如果用的是 NPN 型三极管比如 2N3904/MMBT3904集电极和基极短接后接 D发射极接 D-如果用的是 PNP 型2N3906反过来发射极接 D基极和集电极短接接 D-。接反了读数会偏差很大或者干脆读出一个固定错误值。2.3 最小原理图与引脚连接我板子上这套连线供参考具体引脚定义以你手里的 datasheet 为准。PJ85718DM 引脚连接目标说明VDD3.3V并联 0.1µF 和 1µF 去耦电容GNDGND单点接地别跟强电共地SDASTM32F042C6 PB7开漏输出外接上拉电阻SCLSTM32F042C6 PB6开漏输出外接上拉电阻A0 / A1GND 或 VDD配置 I2C 地址我接 GNDALERTSTM32F042C6 PB5开漏报警输出要加上拉D远端晶体管集电极NPN走双绞线或差分对D-远端晶体管发射极NPN走双绞线或差分对上拉电阻取值上如果 I2C 跑 100kHz 用 4.7kΩ 比较稳妥跑 400kHz 建议换 2.2kΩ同时要看一下总线上挂没挂其他设备上拉太弱会让上升沿变慢通信就会偶发失败。远程 D/D- 的走线是重点。不要走普通长排线最好是双绞线或者 PCB 上的差分走线周围用地包围。HVAC 现场如果旁边有变频器、接触器这类强干扰源还得考虑加屏蔽层单端接地。3. STM32F042C6 侧电路、初始化和一次完整的读温度过程3.1 接线与电平匹配STM32F042C6 的 I2C 引脚在开漏模式下配合外部上拉使用跟 PJ85718DM 的 3.3V 逻辑电平可以直接对接。F042 的 PB6/PB7 复用为 I2C1先要把 GPIO 配置成开漏复用功能。注意别配成推挽输出I2C 协议要求 SDA 既能输出也能释放总线开漏是基本要求。地址这块我板子上 A0、A1 都接地7 位地址取的是 0x48。需要强调不同批次或者不同引脚配置地址会不一样务必以手册的地址表为准。用 HAL 库的时候Mem_Read 函数的地址参数要左移一位变成 8 位地址很多人在这里栽过跟头。3.2 I2C 外设初始化F0 系列的 I2C 没有老 F1 那种简单的时钟控制寄存器它用 TIMINGR 寄存器一次性描述上升沿、下降沿、保持时间等参数。这个值最好让 CubeMX 根据你实际的 HCLK 和 I2C 目标速率生成手工计算容易错。我的配置大致如下hi2c1.Instance I2C1; hi2c1.Init.Timing 0x00201D2B; // 48MHz 下约 400kHz以 CubeMX 生成为准 hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.OwnAddress 0; HAL_I2C_Init(hi2c1);GPIO 部分GPIO_InitStruct.Pin GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_OD; // 开漏复用 GPIO_InitStruct.Pull GPIO_NOPULL; // 上拉在外部 GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate GPIO_AF1_I2C1; HAL_GPIO_Init(GPIOB, GPIO_InitStruct);这里不推荐在代码里写死 Timing换个芯片主频就得重算用工具生成后贴进来是最省事的。我在实测中发现如果 I2C 总线电容偏大比如线长超过 30cm400kHz 容易抽风不如直接跑 100kHz温度数据又不是高速信号慢一点完全没影响。3.3 读温度函数与数据格式读温度的核心是写一个寄存器指针然后连续读两个字节。HAL 库的 Mem_Read 正好做了“写指针读数据”的完整流程#define PJ_ADDR 0x48 #define PJ_REG_LOCAL 0x00 // 本地温度寄存器以手册为准 #define PJ_REG_REMOTE 0x01 // 远程温度寄存器以手册为准 float pj_read_temp(uint8_t reg) { uint8_t buf[2] {0}; HAL_I2C_Mem_Read(hi2c1, PJ_ADDR 1, reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); int16_t raw (int16_t)((buf[0] 8) | buf[1]); return raw * 0.0625f; // 12 位右对齐0.0625°C/LSB }注意我这里是假设数据寄存器低 12 位有效、符号位扩展到高 4 位。如果芯片用左对齐格式就要先raw 4再乘系数。这两种格式在市面上都有人用写代码前务必拿手册确认。符号处理上先把两个字节拼成 int16_tC 语言自动做符号扩展负数温度就不会算错。4. 从原始寄存器到能用的温度换算、校准和滤波4.1 寄存器数据格式与换算示例拿 12 位分辨率、右对齐的格式举例换算关系非常直接温度 原始 int16 值 × 0.0625。寄存器原始值16bitint16 有符号值换算温度0x000000.000°C0x0010161.000°C0x01A041626.000°C0xFFF0-16-1.000°C如果读回 0x8000 或者 0x7FFF 这种极端值别急着当温度用很多传感器用它表示“远程二极管开路”或者“短路”的故障状态。处理办法是读状态寄存器确认而不是把这种值算成几百度的温度送到控制逻辑里。4.2 本地和远程的校准方法芯片出厂精度一般够用但如果你要的是绝对准确还是得校一次。我习惯的方法是用一个经过溯源的参考温度源比如精密水浴加标准铂电阻或者实验室级温箱把传感器和参考放在同一个热平衡环境里在 0°C、25°C、60°C 三个点记录偏差做两点或三点线性拟合然后把 offset 和增益系数存进 Flash。远程通道的校准比本地复杂一点因为误差来源多。最主要的是远端晶体管的理想因子 n 和实际芯片内部默认值不一致。硅信号管的 n 通常在 1.004 到 1.02 之间芯片一般按 1.008 左右计算。如果 n 不匹配读出的温度会有比例偏差温度越高偏差越明显。解决办法是查手册里的理想因子配置寄存器把你实际用的管子型号对应的 n 值写进去手册没有明确数据的话就用 25°C 和 60°C 两个实测点反推误差再做一个比例修正。另外远程通道的串联电阻也会带来误差。D/D- 走线长了铜线电阻、端子接触电阻都会算进去。我在一个 1.5 米双绞线的项目里端子氧化后接触电阻上升远程温度明显往高偏。手册里一般会给出串联电阻补偿寄存器没有的话就在软件里按实测偏移量做线性补偿。完整的修正表达式可以这样写float corrected_temp raw_temp * gain offset;gain 和 offset 在校准流程里算好存到一个结构体里每次上电从 Flash 读出来。这个做法简单、可维护后续换料或者改线长只要重校一次就行。4.3 滤波与报警阈值的工程处理拿到温度以后直接拿去控制在实际现场会出问题。HVAC 环境里风机启停、压缩机吸气排气都会让温度快速波动嵌入式板级场景里负载突变也会让读数剧烈跳变。我常用的滤波策略风机/压缩机控制用 16 点滑动平均采样周期 1Hz相当于 16 秒的平滑窗口避免继电器频繁动作。板级过温保护用一阶低通y 0.1 * (x - y)采样周期 100ms既保留了对快速过温的响应又滤掉了开关噪声。显示用温度可以用 5 点中值滤波专门对付现场偶发的尖峰干扰。报警这块PJ85718DM 这类芯片一般都有 ALERT 引脚和比较模式。配置阈值和迟滞之后温度越界 ALERT 自动拉低MCU 通过外部中断或者 1Hz 轮询去处理。迟滞一定要设不然温度在阈值附近抖动报警输出会不停翻转。软件层面再加一道保险连续两次读到越界才真正执行动作能避免偶发尖峰误触发。5. 两份实测记录嵌入式板级监测和 HVAC 空调箱调校5.1 案例一电机驱动板的功率器件温度监测某次给朋友的一个电机驱动板做热管理板子上的 MOSFET 发热比较可观客户要求监控散热片温度并在超温时降额。方案是PJ85718DM 放在 PCB 角落测“环境温度”一颗 MMBT3904 做成 SOT-23 贴片形式紧贴在 MOSFET 附近的覆铜热焊盘上通过板内差分走线连回 D/D-。实测数据大致是这样的红外点温枪作为参考工况本地读数远端读数红外参考校准后误差轻载 1A28.5°C42.1°C41.4°C0.7°C半载 3A31.0°C61.3°C60.5°C0.8°C满载 5A34.2°C85.7°C84.9°C0.8°C校准之前远端读数比红外参考普遍偏高 1.5°C 左右原因是 MMBT3904 的理想因子和芯片默认值有一点偏差。校准以后整个负载段的误差基本控制在 ±1°C 以内对这个应用足够了。这里有个体会远端温度回应速度很快负载从 1A 拉到 5A读数十几秒内就会明显上升所以板级过温保护的采样周期不能太长100ms 的低通滤波比较合适。5.2 案例二空调箱控制盒与风管送风温度另一个项目是 HVAC 空调箱的控制盒。控制盒装在回风区本地通道测控制盒内环境温度远程通道用一颗 2N3904 封装在防水探头里放到 1.8 米外的送风管中间测送风温度连线用的双绞屏蔽线。第一批样板调的时候远程温度读数在风机动的时候会夸张地跳来跳去有时上下差四五度。查下来原因有三层第一是 D/D- 走了普通排线跟风机反馈线并排走了半米第二是屏蔽层两端都接地了反而形成地环路第三是滤波太弱系统直接拿原始值做控制。后来改成双绞屏蔽线、屏蔽层只在控制板端单点接地软件加 16 点滑动平均读数就稳下来了。校准层面用经过校准的温湿度计探头插入同一个风管位置做对比发现远程通道整体偏低 0.8°C原因是风管风速大那个自制的防水探头热阻偏高响应慢且有一点偏差。这个不是芯片问题是探头结构和安装方式的问题我在固件里做了 offset 修正又给探头换了一个导热更好的外壳最终误差在 ±0.5°C 左右。这个案例给我的教训是HVAC 项目里传感器本身精度再高探头封装、风管安装位置、线缆敷设方式才是误差的主要来源。设计前期多花一点心思在探头结构上比后面用软件硬修要划算得多。6. 排障记录测温系统调试中最常被坑的几个环节6.1 通信失败先别怀疑芯片按这个顺序查I2C 通信不稳定我调试的时候从来都是按顺序排查而不是上来就怀疑芯片坏了先量 VDD 和 GND确认电源干净去耦电容是不是真的贴在电源引脚旁边。用示波器看 SCL/SDA 空闲电平正常应该被上拉拉到高电平如果是 0V 或者波形很缓先查上拉电阻和总线电容。确认地址没错特别是 7 位地址左移成 8 位地址时容易错。降低速率到 100kHz 试试很多“偶发失败”在低速下直接消失问题就在信号完整性和时序。我遇到过 A 同学的一块板子I2C 上挂了三个器件上拉电阻却用了 10kΩ总线电容偏大400kHz 下波形上升沿斜率根本不够通信一会儿好一会儿坏。换成 2.2kΩ 上拉、速率降到 100kHz 后问题消失。还有一个 I2C 老毛病从机把 SDA 拉死后总线死锁主机无法发起 start。处理办法是用两个 GPIO 手动模拟出 9 个 SCL 脉冲把卡死的从机状态机释放掉然后重新初始化总线。我在代码里加了这个恢复逻辑整机可靠性明显提升。6.2 远程读数整体偏高、随线长变化远程通道读数偏高并且随风线长度变化基本可以锁定为 D/D- 回路串联电阻问题。线越长、端子接触电阻越大ΔVBE 测量里面混入的 IR 误差就越大。我有一次在旧设备上排查发现用户为了延长探头线中间加了两个端子排氧化导致接触电阻涨到几十欧读数偏高了接近一度。解决方法是换镀金端子、压接合格并且把线长限制在手册推荐的范围内。如果线长实在压不下来就用芯片的串联电阻补偿寄存器同时在软件里做一个线性修正。另外如果远程通道没有接三极管就直接悬空很多芯片会返回一个固定的异常值别把这个当成“环境温度为零下”来触发控制逻辑一定先查接线再怀疑温度。6.3 读回 0x0000 或温度乱跳上电后读回一堆 0x0000最常见的原因是读寄存器指针没写对或者读的时候芯片还在转换中。温度传感器的转换不是瞬时的典型周期几十到几百毫秒你在转换没完成时去读拿到的是旧值或者无效值。解决方法是上电后先延时一个转换周期再开始连续读数。低功耗项目里还有一个坑芯片进了 shutdown 模式读出来一直是最后一次的值看起来像“温度卡住了”。唤醒之后必须等一个完整转换周期否则第一笔读数可能是残值。温度乱跳则分两类一类是真实的环境波动用滤波解决另一类是干扰噪声耦合进 I2C 或者 D/D-这在 HVAC 现场特别常见。判断方法是把传感器和主控用短粗线直接连起来如果读数稳定说明原来现场线缆的抗干扰设计不合格不是芯片问题。6.4 ALERT 引脚乱触发ALERT 乱触发九成是配置问题。阈值设置太贴近正常工作点迟滞设了 0或者选成了中断模式导致每次温度越界都触发报警。正确做法是设比较模式、给报警阈值加 2°C 左右的迟滞并且软件里做连续两次确认。还有一个低级错误ALERT 是开漏输出漏加上拉电阻引脚浮空导致电平乱跳。解决办法很简单10kΩ 上拉到 VDDMCU 侧配置成上拉输入也可以。整套系统调完以后我最大的体会是像 PJ85718DM 加 STM32F042C6 这种组合真正花时间的不是把 I2C 跑通而是“让温度读数在真实工作环境里长期稳定”。芯片本身该做的信号调理都做了剩下的误差源基本都在 PCB 布局、探头封装、线缆敷设和软件滤波这些系统层面。给后来者一个建议第一版打样就把 D/D- 做成可调的预留串阻测试焊盘地址引脚也做成跳线可选这样现场排障会从容很多后面量产才能少交学费。