
做嵌入式温度采集和HVAC环境监测这一块我常被问到的问题是能不能用一个方案把设备自己周围的温度和远处某个关键器件的温度一起采回来还要稳定、低成本、不占太多PCB面积。早些年我用过DHT22、NTC、DS18B20这些常见器件各有优缺点但要同时满足“数字输出、双通道、远程测温”这几个条件选择一下子少了很多。最近在做一个设备监控项目时我选了PJ85718DM配STM32F745ZG这套组合把本地温度和远程温度同时监测并且跑通了嵌入式主板散热控制和HVAC风管温度采集两条应用线整轮开发下来效果超出预期。这篇就把选型思路、硬件连接、驱动实现、上位机以及调试验收中的关键细节完整拆一遍给正在做温度采集、散热控制、机房监控、暖通空调联动的朋友做个参考。1. 项目到底要解决什么问题1.1 “本地温度”和“远程温度”到底差在哪PJ85718DM这类芯片和普通传感器有个本质区别它内部自带一个测温通道同时又对外引出一对测量引脚可以接一个二极管测温元件。这样一颗芯片就能同时得到两个温度点。“本地温度”代表传感器芯片周围的环境温度放在嵌入式设备里就是PCB附近、机箱内部的热状况放在HVAC设备里就是控制板所在空间的温度。“远程温度”则代表远离主板的那个关键位置比如CPU表面、功率管外壳、风管内部、盘管表面。之所以需要这种双通道结构是因为很多设备里“环境温度”和“热点温度”并不是一回事。机箱内空气温度可能只有40℃功率管表面却已经85℃两者都要监测但测温点完全不在同一位置。我用普通单点传感器的时候往往要外接第二个探头再占一路接口还要在代码里维护两套标定参数线和接口都多一套。而PJ85718DM的远程通道和本地通道共享同一个ADC和校准电路两路温度读出来的一致性更好代码处理也简单得多。1.2 为什么不能靠两块单点传感器凑合可能有人觉得既然测两个点那用一颗单点传感器测本地再外接一颗DS18B20测远程不就行了实际项目里这么干过以后我发现几个痛点。第一是信号一致性。两颗独立传感器各自有各自的精度误差即使标称都是±1℃两路温差也可能达到1.5℃以上。而PJ85718DM的双通道本质上是在同一颗芯片里完成两路测量误差方向相同做温差判断、结温估算这类逻辑时更可靠。第二是布线复杂度。DS18B20虽然总线可以挂多个但每个探头都要单独引线线缆成本和施工成本都不低。PJ85718DM的远程通道只需要两根线拉到一个二极管上很多场景里这个二极管可以直接贴在目标器件表面比带探头的成品传感器紧凑得多。第三是低功耗和小型化。HVAC控制面板、嵌入式板卡都讲究小型化一颗SOT23封装的三极管就能当远程测温探头用整板面积和功耗都可以压得很低。1.3 这套系统的整体框架在往下讲细节之前先给整套系统画个轮廓方便后面理解。传感器层PJ85718DM负责本地温度采集远程探头负责远端关键点温度。主控层STM32F745ZG通过I2C/SMBus总线读取温度寄存器做滤波、阈值判断、报警处理。应用层嵌入式场景里输出PWM控制散热风扇HVAC场景里通过串口/RS485上送温度数据、参与防冻保护等联动逻辑。监控层温度数据实时上送PC用Python做曲线显示和数据记录。这个结构非常清晰后面每一章的选型和代码都围绕这条链路展开。2. 硬件选型为什么不是DHT22、NTC或者DS18B202.1 常见温度方案横向对比决定用PJ85718DM之前我把市面上常见的测温方案列了一张对比表方案测温原理典型精度通道数远程测温能力典型场景DHT22电容式湿敏热敏±0.5℃温度1无室内温湿度NTC电阻分压约±1%非线性1无需信号调理低成本板级测温DS18B20单总线数字±0.5℃1无短距离多点数字测温PT100铂电阻±0.1℃1无需变送器工业高精度测温PJ85718DM双通道Σ-Δ ADC常见标称±1℃以内2支持外接二极管板级热监控、HVAC从表里能看出来真正带硬件远程通道、又能数字输出的方案并不多。NTC如果要用远程通道得在远端接一个固定电阻、再走一段模拟信号回主板线阻、噪声、分压误差全搅在一起工程上非常难受。DS18B20虽然数字信号抗干扰强但它本质还是单点传感器想测两个位置就得挂两颗接口和地址管理反而变得复杂。2.2 PJ85718DM 的选型逻辑PJ85718DM让我最满意的是它把“信号调理”这件事在芯片内部做完了。远端二极管只需要接上去芯片自己提供偏置电流自己完成放大和ADC转换通过I2C寄存器直接读出温度值。这对嵌入式项目来说省掉了运放电路、滤波电路、分压校准这些杂活硬件设计时间能省两三天。另外一个理由是远程通道的响应速度。二极管测温本质上是测量PN结在不同电流下的电压差热时间常数非常小表面贴装的三极管探头对温度变化响应很快。我实测下来远程通道对热风枪吹过目标区域的变化几乎即时反映这用在CPU散热控制里非常关键——热点温度已经快速上升了风扇如果还按慢吞吞的环境温度来调速散热就滞后了。精度方面这类芯片在不同厂牌和批次里标称会有差异一般本地和远程的典型精度都在±1℃左右用于监控和联动控制完全够用。2.3 STM32F745ZG 在这里是什么角色只看温度采集其实一颗Cortex-M0内核的小单片机就够为什么选STM32F745ZG这种带Cortex-M7内核、216MHz主频的“大个子”原因在于这个项目不只是读温度。板子上还要跑多个任务温度采样、滤波算法、PWM风扇控制、风扇转速反馈监测、串口协议解析、LED和按键交互部分版本还要挂SD卡做历史记录甚至预留Modbus通信。Cortex-M7有2MB Flash和320KB SRAM外设资源多后续加功能不用换平台。还有一个很实际的点F745ZG带有硬件I2C和DMA温度数据可以不占CPU在后台搬运主核专心跑控制逻辑这在实时性要求高的场景里体验差异非常大。经费敏感的时候也可以降级到F103或者F4系列但功能迭代空间会被提前锁死。我的建议是如果项目明确有后续扩展主控可以按“未来半年需要跑的代码量”来选而不是按当前第一版的功能选。2.4 成本与开发效率的整体账单看BOM成本PJ85718DM比NTC、DS18B20都贵但把开发成本摊进去就不一样了。用NTC方案省的钱最后会花在运放调试、多点校准、非线性标定这些事上用PJ85718DM芯片上电读寄存器就能出温度校准几乎不用做。在一套有明确量产计划的设备里节省的调试工时远比器件差价大得多。3. 远程温度通道的原理与电路设计3.1 远端二极管测温的基本原理PJ85718DM的远程通道测的不是电压绝对值而是二极管正向压降在不同偏置电流下的差值。给二极管先后灌入两个不同电流测量两次Vbe差这个差值正比于绝对温度同时还能消掉二极管自身串联电阻和工艺偏差的影响。这就是为什么远端二极管测温可以做到较高的精度而不是简单地用一个固定电流测压降。实际接法上最常用的元件是NPN小信号三极管把基极和集电极短接成二极管形态。NPN管的基极-集电极短接点接芯片D引脚发射极接D-引脚。如果用的是PNP三极管接法方向反过来。我习惯选MMBT3904这类SOT23小封装管贴在目标器件表面或者插在小型探头里都很方便。3.2 D与D-引脚的滤波和保护远程通道的两个引脚对噪声比较敏感这是这类芯片最常见的问题点。D和D-之间要加一只100pF到1nF的陶瓷电容位置尽量靠近传感器引脚。电容太小滤波不够太大会增加充放电时间、影响转换结果常见取值是100pF到470pF具体以手册要求为准。如果远程探头距离比较远走线要用双绞线或者屏蔽线并且不要在D、D-附近跑开关电源的驱动信号。长距离时还可以在D线上串一只2.2k到10k的保护电阻既限制外部误接电压对芯片的伤害也能配合电容滤掉部分高频干扰。我踩过一个很典型的坑第一版PCB为了省事远程线走了一段普通排线和继电器控制线捆在一起结果远程温度读数上下乱跳幅度在±3℃以上。后来把那段排线改成双绞线并在D、D-之间加了100pF电容读数立刻稳定到±0.5℃以内。简单说远程通道的稳定性很大程度上是“布线设计”出来的不是芯片本身的问题。3.3 HVAC场景里的探头封装方式在HVAC设备里用PJ85718DM测风管温度不能像嵌入式板卡那样把三极管直接贴在芯片上。多数做法是把MMBT3904封进一个小型铜套或者铝套里灌入导热硅胶做成一个探头。这个探头插入风管通过两根屏蔽线回到主板。需要注意探头和线缆的整体导热和绝缘设计尤其是冷凝水环境下探头外壳要密封不能让水汽导致引脚间短路。远程测温探头最怕的就是受潮后漏电漏电会让温度读数偏高且不稳定。如果探头到主板的距离超过两三米我一般会重新考虑方案改用数字输出的单总线传感器更稳。PJ85718DM远程通道虽然抗噪能力不差但它本质是模拟量差值测量线太长以后天线效应和地电位差都很难控制。3.4 PCB布局的注意事项传感器芯片本身的位置也有讲究。PJ85718DM的本地通道测的是芯片周围环境温度如果把芯片放在功率管旁边本地温度就会一直被加热读出来的“环境温度”偏高。更合理的做法是把芯片放在主板中部偏通风的位置让本地通道更接近真实的机箱内空气温度。远端走线尽量内层走上下铺地平面避免悬空长线。芯片下面可以打一排过孔加强散热但注意不要用大面积铜皮把传感器整个包住否则它测出来的又不是空气温度了。4. 驱动开发寄存器配置与温度读取4.1 初始化流程与关键寄存器PJ85718DM通过I2C接口访问常见7位地址可以通过地址引脚选择不同地址具体地址值以生产批次数据手册为准。开发时先用I2C扫描工具确认实际地址这是所有后续工作的前提。初始化的完整流程大致是这样确认器件在线、读取状态寄存器清除残留告警、写入配置寄存器、设置温度报警阈值、开启中断引脚或者进入轮询采集。下面是这类芯片常见的寄存器分布实际使用时请按手册逐项核对寄存器常见地址主要含义状态寄存器0x02转换忙标志、远程开路/短路、温度超限配置寄存器0x03工作模式、通道使能、滤波、报警模式本地温度高字节0x05本地温度整数部分或高位本地温度低字节0x06本地温度小数部分远程温度高字节0x07远程温度整数部分或高位远程温度低字节0x08远程温度小数部分扩展精度寄存器0x10更高分辨率的附加数据位4.2 温度读取与换算代码读取温度的核心思路是连续读取两个字节然后按芯片定义的分辨率换算成浮点温度。不同批次的芯片在低位字节对齐方式上可能会有差异代码里要留一个可配置的换算方式不要写死。#define PJ85718DM_ADDR 0x4C #define REG_STATUS 0x02 #define REG_CONFIG 0x03 #define REG_LOCAL_TEMP_HI 0x05 #define REG_LOCAL_TEMP_LO 0x06 #define REG_REMOTE_TEMP_HI 0x07 #define REG_REMOTE_TEMP_LO 0x08 #define REG_REMOTE_EXT 0x10 static float convert_temp(uint8_t hi, uint8_t lo, uint8_t ext) { // 常见做法高字节作为整数部分 float temp (float)hi; // 低位字节最高位代表 0.5℃ 增量 if (lo 0x80) { temp 0.5f; } // 如果支持扩展精度寄存器再按位累加更小的分辨率 // 这里给出的仅为示例实际位对齐以数据手册为准 temp (float)((ext 6) 0x03) * 0.125f; return temp; }实际开发中我一般不做浮点运算而是把所有温度都乘10或乘100后转成整数来存既省内存又方便串口打印和上位机显示。比如内部统一用“温度值×100”的int16_t表示显示时再除以100。4.3 采集时序与状态轮询这类芯片上电后会自动进行周期转换本地和远程通道通常是轮流转换。读取温度寄存器之前最好先读状态寄存器确认转换已经完成否则读出来的可能是上一次的旧值。这里最笨也最稳的办法是固定延时延时长到超过一个完整转换周期比如50ms到100ms。在实时性要求更高的场景里我会用一个定时器中断每500ms触发一次采样进程里读状态寄存器的忙标志。如果忙这一轮就跳过等下一轮再采。这个策略能避免因为读取太频繁导致数据抖动也避免主循环被I2C阻塞。连续读两个字节时还有个细节如果两组字节的更新恰好发生在两次读取之间可能拼出一组不一致的数据。稳妥做法是连续读两遍比较一致再用不一致丢弃。在温度变化平缓的应用里这个情况很少但HVAC设备或者电源电流波动大的场合还是值得处理一下。4.4 报警阈值与中断模式PJ85718DM支持设置本地和远程的高温、低温报警阈值超过阈值后ALERT引脚会变化这个引脚可以直接接到STM32的外部中断输入端。这里要特别留意比较器模式和中断模式的区别。比较器模式是温度超限时ALERT引脚输出有效电平温度回落后自动恢复像一只“窗口比较器”适合直接驱动硬件电路。中断模式下超限产生锁存告警必须由主机读状态寄存器清除后引脚才会恢复。如果你在中断模式下忘了清除状态会出现一个非常隐蔽的故障温度已经降下来了但ALERT引脚一直拉着风扇一直全速转排查起来很费时间。4.5 初始化代码参考下面是一个简化的初始化函数重点是先确认状态、写入配置、然后再进入正常采集int pj85718dm_init(void) { uint8_t status; if (i2c_read_reg(PJ85718DM_ADDR, REG_STATUS, status) ! 0) { return -1; } // 清除残留告警 (void)i2c_read_reg(PJ85718DM_ADDR, REG_STATUS, status); // 配置寄存器开启本地和远程通道使能滤波进入正常测温模式 uint8_t config 0x00; // 具体bit含义按手册设置这里给一个默认无害值 i2c_write_reg(PJ85718DM_ADDR, REG_CONFIG, config); return 0; }I2C总线上电后我习惯先做一个总线扫描确认传感器应答地址然后再调用初始化函数。这样如果焊接有问题第一时间就能在日志里看出来不用反复怀疑代码。5. 应用层设计嵌入式风扇控制与HVAC联动5.1 嵌入式场景PWM滞回控制嵌入式散热控制最常见的不是PID而是滞回控制。PID调不好会震荡而且依赖传感器响应速度。滞回控制逻辑简单、效果直接温度升高到启动点风扇按设定占空比运转温度降到回差以下时再退出。这个回差一般设在3℃到5℃防止风扇在阈值附近反复启停。我实际项目里的策略是这样的温度低于50℃时风扇最低转速占空比30%。温度超过55℃占空比升到70%。温度超过65℃直接全速100%。温度回落到52℃以下才从70%降到30%。这个策略避免了一个常见问题风扇转速跟着温度做连续比例调节温度稍有波动风扇声音就忽大忽小很恼人。滞回控制虽然简单但人机体验好很多。PWM输出频率要避开可听噪声频段。20kHz到25kHz是常用区间风扇驱动电路也能轻松跟上。如果把频率设在1kHz附近电容和风扇线圈会发出明显的啸叫很容易被客户投诉。5.2 远程温度加入控制逻辑把远程温度纳入控制逻辑后事情变得更有效。环境温度低但CPU热点已经飙上来时风扇应该立刻加速。我的做法是取本地温度和远程温度的加权结果来驱动风扇状态机远程通道的权重更高。比如uint8_t temp_ctrl (uint8_t)(0.6f * remote_temp 0.4f * local_temp);这个权重的含义是热点温度对散热需求的贡献更大。如果两个温度相差很大也说明散热风道可能出了问题可以顺带在日志里打一条告警。5.3 HVAC场景Modbus上送与防冻保护HVAC设备里主控通常不是往风扇发PWM那么简单而是需要把温度数据上送给楼宇自控系统。我用STM32F745ZG的串口外接RS485收发器跑Modbus RTU协议把本地温度和远程温度映射到两个保持寄存器模拟量用“温度值×10”表示。这样PLC或者上位机组态软件直接读保持寄存器就行不需要做单位转换。防冻保护是暖通里头特别讲究的逻辑。当风管温度或者盘管温度低于设定值比如5℃时系统要联动关闭新风阀、提高热水阀开度防止盘管冻裂。这里我同时使用本地和远程两个温度来互相验证如果两个温差过大说明传感器或阀门可能出了问题这时候宁可做保护性动作也不继续维持原有输出。传感器断线检测在HVAC里尤其重要。PJ85718DM的状态寄存器里一般会有远程二极管开路/短路标志程序每次采集都查这个标志一旦发现远程探头异常立刻把对应温度数据置为无效并且向监控中心上报故障码。否则断线后读数会变成一个完全不合理的固定值监控人员反而以为设备真的到了那个温度。5.4 软件滤波与毛刺剔除温度数据偶尔会有毛刺尤其是远程通道受干扰时。我在主控里做了一级中值滤波加一级滑动平均。中值滤波是取最近5次采样的中间值能直接干掉单点突发干扰滑动平均再对结果做平滑让温度曲线不跳变。#define FILTER_SIZE 5 static int16_t history[FILTER_SIZE]; int16_t temp_filter(int16_t new_value) { int16_t tmp[FILTER_SIZE]; for (int i 0; i FILTER_SIZE - 1; i) { history[i] history[i 1]; tmp[i] history[i]; } history[FILTER_SIZE - 1] new_value; tmp[FILTER_SIZE - 1] new_value; // 排序取中值 for (int i 0; i FILTER_SIZE - 1; i) { for (int j i 1; j FILTER_SIZE; j) { if (tmp[i] tmp[j]) { int16_t t tmp[i]; tmp[i] tmp[j]; tmp[j] t; } } } return tmp[FILTER_SIZE / 2]; }这个简单滤波函数的实测效果很明显远程温度的短时波动从±1℃被压到±0.2℃以内。代价是响应会变慢一点但温度本来就是慢变量这个代价完全可以接受。6. 上位机显示与数据记录6.1 串口协议怎么设计STM32F745ZG把温度数据从串口送出来时不要直接裸传文本最好设计一个简单的帧协议方便上位机解析。我用的是这种格式0xAA 0x55 LEN CMD DATA0 DATA1 ... CRC头部用0xAA 0x55LEN表示后面数据的长度CMD用来区分数据帧、告警帧、心跳帧CRC做校验。比如温度数据帧里两个温度值都乘100转成int16_t按小端序放进去。uint8_t frame[8]; frame[0] 0xAA; frame[1] 0x55; frame[2] 0x05; // 数据长度 frame[3] 0x01; // CMD: 温度数据 frame[4] local_temp_raw 0xFF; // 本地温度低字节 frame[5] (local_temp_raw 8) 0xFF; frame[6] remote_temp_raw 0xFF; // 远程温度低字节 frame[7] (remote_temp_raw 8) 0xFF; // frame[8] crc8(frame, 8);上位机先找帧头再按LEN读完整帧最后校验CRC。这套协议虽然简单但后续加传感器、加命令都很方便。6.2 Python 实时曲线上位机我用Python快速搭了一个显示工具核心就是串口读取加matplotlib动态刷新。下面是简化版import serial import matplotlib.pyplot as plt ser serial.Serial(COM3, 115200, timeout1) local_list [] remote_list [] plt.ion() fig, ax plt.subplots() while True: data ser.read(9) if len(data) 9: continue if data[0] 0xAA and data[1] 0x55 and data[2] 5: local_raw data[4] | (data[5] 8) remote_raw data[6] | (data[7] 8) local_temp local_raw / 100.0 remote_temp remote_raw / 100.0 local_list.append(local_temp) remote_list.append(remote_temp) ax.clear() ax.plot(local_list, labelLocal) ax.plot(remote_list, labelRemote) ax.legend() plt.pause(0.1)这套工具调试风扇策略和HVAC联动逻辑时非常省力两条曲线一对比哪个温度先升、哪个温度后降一眼就能看出来。实测中发现远程通道响应速度确实明显快于本地通道这在做散热控制时是非常有价值的信息。6.3 数据记录与回放曲线只能看实时状态做长时间验证还是得记录数据。我一般让上位机同时往CSV文件里写数据列名是时间戳、本地温度、远程温度、状态标志。有了这种记录第二天可以把温度曲线回放出来检查晚上设备是否出现了异常波动也方便分析HVAC联动逻辑是否合理。如果设备要离线记录可以用STM32F745ZG的SDIO口接一张TF卡用文件系统写CSV文件。写入频率不用太高每10秒写一行足够一天的数据也就几千行对Flash寿命影响很小。关键事件比如温度越限、传感器断线则单独记录一条事件日志方便事后定位问题发生的时间点。7. 调试实录常见问题与排查技巧7.1 I2C扫描不到传感器先确认总线上有没有设备应答用I2C扫描器跑一遍。常见原因有地址引脚接法不对导致实际地址和代码不一致、上拉电阻没接或者阻值太大、传感器供电没到位。上拉电阻的选择很简单100kHz总线用4.7k400kHz总线用2.2k。阻值太大会导致上升沿过缓信号变形阻值太小会增大功耗芯片可能拉不动。这类I2C问题排查时先看波形永远是最快的。还有一点容易忽略部分型号的I2C接口兼容SMBus对时钟低电平时间有限制。如果用GPIO模拟I2C且速度太慢可能触发总线超时复位。最简单的规避办法是保证SCL频率不低于10kHz模拟I2C时不要延时太久。7.2 远程温度读数跳动的真实原因这是整个项目中间题最多的地方我把实际遇到过的原因按概率排个序一是滤波电容缺失。第一版硬件没加D、D-之间的电容远程读数在正常温度和异常高温之间狂跳。加上100pF电容后问题立刻解决大半。二是走线离干扰源太近。D、D-线靠近变压器、继电器、电机驱动线时远程通道会被耦合出大量尖峰。改成双绞线并远离干扰源后数据趋于平稳。三是地线环路。如果远程探头的公共地和主板地之间存在压差会导致读数有时高有时低。解决办法是单点接地探头外壳不要两端都接大地。软件层还可以做兜底连续读5次去掉最大最小再取平均或者用中值滤波。硬件问题不解决的情况下软件滤波只能压住表象不能根治所以调试时还是要优先查硬件。7.3 本地温度总比预期高本地温度偏高很少是芯片问题。第一看传感器周边有没有功率器件如果芯片离电源、MOS管太近本地通道读到的是被加热后的空气温度。第二看整板功耗设备运行一段时间后机箱内部温度本来就比室温高这是正常的。第三看芯片自身的自热功耗如果总线电压偏高或者持续高速采样累计发热也会有点影响。如果确认偏高确实存在可以在代码里做校准偏移。用标准温度计和传感器放在同一位置连续读一段时间取平均差值然后把这个差值补偿到本地温度读值上。7.4 远程探头断线和短路判断远程探头脱落时芯片读回来的温度数据通常会变成一个很离谱的固定值状态寄存器的远端开路标志也会置位。代码里不要只依赖“数值是否合理”来判断断线比如温度大于150℃或小于-50℃就置无效还要定期查状态寄存器。我在HVAC设备里遇到过一种情况远程探头轻微短路读数在正常温度和明显偏高之间来回跳状态寄存器偶尔报短路偶尔正常。这种软故障最难查最后是把探头拆下来量绝缘电阻发现线缆在端子附近磨损进水。所以接线端子一定要做防水处理线缆固定要留应力释放不能让接头长期受力。7.5 校准与环境测试PJ85718DM这类数字传感器在出厂时已经做过校准一般不需要用户再标定。但如果项目对温度测量精度要求比较高比如HVAC计量场景需要控制在±0.5℃以内可以自己做两点校准。最常用的校准点是冰水0℃和恒温水浴50℃。把整个探头浸入恒温介质中待读数稳定后记录偏差然后做线性补偿修正值 偏移量 增益系数 × 原始值这里“偏移量”和“增益系数”通过两个校准点的实际偏差计算出来最后在代码里对读值做变换。实际操作中校准设备本身的温度计精度要高于被测系统否则校准就没有意义了。整套项目做下来我最深的体会是PJ85718DM和STM32F745ZG这套组合的真正价值不在于某一个代码片段有多高级而在于它把“测两个温度点做联动控制上送监控数据”这条链路压缩得非常紧凑。硬件上只需要一个传感器芯片加一个极小探头软件上从底层I2C读到应用层风扇策略、上位机曲线只用了不到几百行代码。最让我印象深刻的还是远程通道那些坑几乎每一个都是布线设计造成的芯片本身反而没什么问题。如果你也打算在设备里做温度监测强烈建议第一版PCB就把远程通道的滤波电容和走线规划好后面会少很多折腾。