ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于PJ85718DM与STM32F745ZG的本地远程温度监测方案

基于PJ85718DM与STM32F745ZG的本地远程温度监测方案

1. 为什么要在同一套系统里同时测“本地”和“远程”温度

1.1 本地温度与远程温度根本不是一回事

很多人第一次接触温度采集时,以为只要是测温度,随便找个热敏电阻、温度传感器芯片往板子上一放就行。真做起嵌入式和 HVAC 项目之后才会明白,本地温度和远程温度是两个完全不同的概念,混在一起用早晚要出问题。

所谓本地温度,指的是传感器芯片封装内部或器件本身所在位置的温度。它在 PCB 板上工作,周围有 MCU、电源芯片、功率器件,这些发热源会直接升高中局部环境的温度。而远程温度,指的是通过外部探头、引线连接,去测量远端那个物理位置的真实温度,比如风管送风温度、回风温度、室内温度。这两个温度之间可能差好几度,甚至十几度。MCU 旁边那 5 度的温升,可能让原本正常的控制逻辑误判房间过热,把冷阀全开,结果现场却冷得不行。

所以我在做“基于 PJ85718DM 与 STM32F745ZG 的本地/远程温度监测方案”时,第一件事就是把这些温度来源分开看待。这套系统面向的是嵌入式和 HVAC 两个场景,嵌入式端需要关心板子本身的健康状态,避免 MCU 局部过热、散热设计失效;HVAC 端需要关心被控空调区域的实际温度,把它作为风机盘管、新风机、冷热水阀门的控制依据。同一个系统里,用一颗带本地和远程测量能力的传感器芯片,配合一颗有足够算力的 STM32F745ZG 做数据处理和策略控制,比堆一堆分立器件靠谱得多。

1.2 PJ85718DM 在系统里到底承担什么角色

PJ85718DM 是一颗数字温度传感器芯片,常见封装形式下支持片上本地温度测量,同时可以外接远程温度探头。简单说,这颗芯片就是一个“双通道测温桥”,本地点负责机板温度,远程点负责被测环境温度,两个读数最终通过数字接口送进 MCU。

我没有把它的型号当作唯一选项,但在这个项目里,选它的核心原因是双通道集成度高。如果用两路热敏电阻方案,得占用两路 ADC,还要自己搭分压网络、做非线性校正、校准串联电阻误差,整套东西下来不仅占用 PCB 面积,调试成本也高。而用这类带数字接口的多通道传感器,远程探头接上去之后,芯片内部自动完成激励、采样、ADC 转换和对数运算,MCU 只需要通过 I2C 之类的接口把换算好的数据读回来就行,省下的精力可以全部放到控制算法上。

实际接线时,我通常把本地通道当作“板温”来看,把它作为固件保护和散热告警的输入;把远程通道接到 HVAC 现场测点。远程探头和芯片之间的引线可以做得比较长,这一点对 HVAC 安装特别友好。因为控制器往往被装在配电箱、控制柜、吊顶检修口这些地方,真正要控温的测点在房间中央或者风道深处,二者之间经常隔了好几米。

1.3 为什么用 STM32F745ZG 而不是普通入门 MCU

STM32F745ZG 这颗料在这套方案里不是简单跑一下 I2C 读传感器就完了。它是 Cortex-M7 内核,主频最高能到 216MHz,带浮点单元,片上资源也很充足。HVAC 控制器往往要同时干几件事:轮询多路温度、做平均温度滤波、跑 PID 控制算法、驱动三速风机、控制电动阀、刷新显示屏、和上位机走 Modbus 或 CAN 通信。如果手头只有一颗几十 MHz 的入门单片机,把这些任务塞进一个裸机循环里,容易把实时性拖垮。

F745ZG 的另一个优势是外设多。它不止一路 I2C,我可以把传感器单独挂一路 I2C 总线,把 EEPROM、LCD 驱动、触摸按键挂到另外的总线上,避免传感器时序被其他设备的响应速度干扰。144 引脚的封装也给了比较充分的 GPIO,方便以后扩展更多数字输入输出。处理温度数据时,直接使用浮点运算做滤波和校准,不用像在低端 MCU 上那样逐字节模拟浮点,代码写起来简单,运行速度也快。

当然,如果项目成本压力非常大,换一颗低成本的 M0/M3 单片机也是可行的,你的工作量会多一些,但并非做不了。这篇方案的核心是把“多通道温度采集 + 复杂控制逻辑”这条完整链路理清楚,所以我会以 F745ZG 为主线来展开。

2. 硬件搭建:从传感器到 MCU 的完整链路

2.1 PJ85718DM 的引脚与接入方式

先说明一下,不同批次、不同封装的 PJ85718DM 引脚定义和寄存器地址可能有差异,我下面描述的方案是基于我手头这颗常见贴片封装的接法。拿到实际的芯片之后,第一步永远是查对应批次数据手册,核对引脚功能,不要盲抄网上的代码和接线图。

以典型接法为例,传感器部分至少要引出以下信号:

  • 电源引脚 VDD 和 GND,通常接 3.3V。
  • I2C 数据线 SDA 和时钟线 SCL。
  • 报警输出引脚 ALERT,可以接到 MCU 的 GPIO 外部中断输入,用于温度越限时快速响应。
  • 远程探头接入端,通常是一组差分输入,接外部温度探头。

PJ85718DM 和 MCU 之间的连接很简单,因为都是标准 I2C 电平,只要保证电平一致,不需要额外转换。STM32F745ZG 的 GPIO 可以配置成开漏输出并外加上拉电阻,和传感器的 I2C 接口直接连一起就可以工作。远程探头接入端就比较关键了,要注意极性,接反之后读到的温度往往是不正常的,或者直接出现传感器故障标志。

整套硬件链路的信号方向是:远程探头产生的微弱温度信号进入 PJ85718DM,经过内部 ADC 和算法后转成数字温度读数;本地通道同时完成板温采样;MCU 通过 I2C 周期读取两个温度寄存器和状态寄存器,然后进入应用层处理。

2.2 电源、去耦和上拉电阻的选择

温度传感器属于模拟测量器件,虽然它已经把 ADC 做在芯片内部,但电源噪声仍然会影响最后的读数稳定性和精度。尤其当芯片直接放在一块有继电器、有电机驱动的 HVAC 控制板上时,电源的干扰会明显传导到传感器测量链路上。

我在这套方案里采用的做法是:传感器电源独立使用 3.3V,并且在这颗芯片附近放最少一个 100nF 陶瓷电容,紧贴电源引脚放置;在此基础上再放一个 10uF 的钽电容或高容值 MLCC,用于吸收稍低频的电源波动。MCU 的电源也需要认真处理,STM32F745ZG 的 VDDA 和 VDD 不要混用,要给模拟电源引脚单独加磁珠和电容,保证 ADC 参考电压干净。

I2C 总线的上拉电阻也需要算一下。总线速度选择 400kHz 时,上拉电阻常见取值是 2.2k 到 4.7k。如果传感器离 MCU 只有几厘米,用 4.7k 没问题;如果传感器通过排线拉到几十厘米以外,可以把上拉电阻降到 2.2k,改善总线上升沿。不过上拉太小会让总线负载变大,降低噪声容限。最稳妥的做法是先看一眼 I2C 总线上的总电容,再根据通讯波形决定电阻值。手头没有示波器的时候,优先选 4.7k 起步,观察设备能否稳定读写。

2.3 远程探头选型与线缆长度约束

HVAC 场景里,远程探头是这套系统中离现场环境最近的部分,选型不能太随意。常用的远程温度探头有两种:一种是传感器厂家配套的专用探头,内部已经做了匹配电路,直接插上就能用;另一种是自制热敏电阻探头或者二极管类温度探头,需要根据芯片的数据手册确认驱动电路参数。

优先推荐用配套探头。原因很简单,传感器的远程通道内部有一套为特定探头类型设计的激励和校准逻辑,用不匹配的探头会导致读数非线性、偏大偏小,甚至完全读不出来。我见过有人拿一个普通的 NTC 热敏电阻去接远程通道,结果温度读到 -30 度,后来换回配套探头或者按数据手册匹配偏置电阻才能正常工作。

关于线缆长度,远程测量的核心矛盾在于线阻和干扰。线缆越长,线阻越大,外部电磁噪声也越容易耦合进来。把远程探头线做成双绞线,可以大大抑制共模干扰;如果现场环境有变频器和继电器,再用屏蔽双绞线,屏蔽层单端接地。线缆长度尽量不要超过数据手册给定的上限,如果实在要拉很长的线,我倾向于在远程通道输入处加小容量共模滤波电容,但要注意电容过大会拉长内部采样稳定时间,可能导致传感器无法在设定的采样周期内完成转换,造成读数异常。

2.4 STM32F745ZG 侧的硬件初始化

STM32F745ZG 一侧的硬件准备比较常规,但有一个细节常被忽略:I2C 引脚必须选择有 FC 功能且支持 3.3V 电平的引脚,并把 GPIO 配置为开漏输出。如果配成推挽输出,外部上拉电阻和内部驱动会打架,总线波形会变难看,甚至通讯出错。

我建议把传感器所使用的两个 I2C 引脚单独列出来,不要把其他 GPIO 复用到相邻位置。虽然 I2C 本身只需要两根线,但在 HVAC 控制板上,电机驱动、继电器线圈、开关电源这些强干扰源都会往导线上耦合噪声。给 I2C 走线留出独立的布线空间,保持和其他高电压大电流走线有足够间距,比后期在软件上拼命滤波管用得多。

还可以把 ALERT 引脚接到 F745ZG 的一个外部中断 GPIO 上。正常情况下固件周期轮询温度即可,但报警场景下,用中断来唤醒和处理会更及时。比如远程探头断线时,传感器会在 ALERT 引脚上产生低电平信号,MCU 立刻进入故障处理流程,比等待下一次轮询要快不少。

3. 固件实现:读取、换算、滤波

3.1 初始化 I2C 总线与传感器配置

固件开发的第一步不是读温度,而是把 I2C 外设初始化和传感器配置做好。STM32 的 HAL 库把 I2C 配置封装得比较完善,我一般使用以下初始化逻辑:

I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 400kHz hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; // 主机地址,可配置 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1); }

初始化完成后,再配置传感器。不同芯片的寄存器格式不同,我这里只给出典型流程:先读取设备 ID 寄存器确认通讯正常,然后配置转换分辨率、报警阈值和远程通道采样模式。分辨率越高,每次转换需要的时间越长,所以要根据 HVAC 系统的实际控制周期去平衡。房间温度变化速率通常很慢,1 秒左右读一次就够了,没必要追求极限速度。

3.2 读取本地和远程温度寄存器的代码思路

配置完成之后,温度的读取逻辑非常规整:MCU 先写寄存器地址,再连续读取两个字节的数据,最后把两个字节拼成 16 位原始值。

使用 STM32 的 HAL 库时,读寄存器可以这样写:

uint8_t raw_temp[2]; int16_t raw_temp_val; // 假设本地温度寄存器地址为 0x00 HAL_I2C_Mem_Read(&hi2c1, PJ85718DM_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, raw_temp, 2, 100); raw_temp_val = (int16_t)((raw_temp[0] << 8) | raw_temp[1]);

这里的 PJ85718DM_ADDRESS 需要根据实际硬件上的地址引脚设置来定义,常见默认值是类似 0x48 这类地址,具体要查阅数据手册。远程温度寄存器地址一般在本地温度寄存器之后,读取方式完全相同。

读到的是原始码值,不是可以直接用的摄氏温度。换算方式通常是把整型数据右移规定的位,再乘以 LSB 对应的温度增量。比如 12 位分辨率下,每个 LSB 代表 0.0625 摄氏度,那么真实温度就是(raw_temp_val >> 4) * 0.0625。要注意有符号数的移位,在 C 语言里最好使用int16_t类型并且逻辑右移后再乘,避免负数温度被错误转换。

3.3 温度数据的平滑处理与变化率控制

直接读取的温度值会有几项噪声来源:远程探头引线接收的电磁干扰、传感器内部 ADC 的量化噪声,以及 HVAC 设备启停瞬间造成的电源波动。如果直接把原始读数丢给控制逻辑,阀门和风机就会频繁抖动,室温稳定不下来,控制输出也会产生磨损。

我习惯在读取之后加一层一阶低通滤波,代码实现如下:

#define FILTER_ALPHA 0.2f static float local_temp_filtered = 0.0f; static float remote_temp_filtered = 0.0f; void update_temperature_filter(float local_raw, float remote_raw) { local_temp_filtered += FILTER_ALPHA * (local_raw - local_temp_filtered); remote_temp_filtered += FILTER_ALPHA * (remote_raw - remote_temp_filtered); }

这个FILTER_ALPHA可以根据采样周期调节。采样越快,alpha 可以取得越小,让滤波更平滑。如果希望温度显示响应更快,alpha 可以调大。我通常在试机时先看原始数据和滤波数据的曲线,再确定最终值,而不是拍脑袋定一个数字。

除了低通滤波,我还会计算温度变化率。做法是把最近两次滤波值的差值除以采样间隔,得到每秒变化多少度。变化率数据很有用,HVAC 控制里可以用它来判断房间是正在快速升温还是已经趋于稳定,从而提前调整输出,而不是等到温度已经越过阈值才响应。

3.4 用 ALERT 引脚和中断提升越限响应速度

轮询方式的响应时间等于采样周期,在某些保护场景下可能不够快。毕竟,如果远程温度因为探头故障或者现场异常突然飙升,最好立刻停下来做保护处理。

我这里把 PJ85718DM 的 ALERT 引脚连接到了 STM32 的外部中断。当温度超过预设上限或低于下限时,传感器硬件会自动置位报警输出,MCU 的外部中断伺候函数立即被执行。里面只做一个最小处理:设置一个报警标志,然后让主循环尽快进入故障处理逻辑。不要在中断里做复杂计算,避免阻塞其他高优先级任务。

这个设计让系统同时具备两种响应能力:常规定量控制靠周期轮询和滤波,极端异常靠硬件中断快速介入。对于 HVAC 控制器来说,这种“软硬结合”的响应机制是防止现场设备损坏的重要保障。

4. 面向 HVAC 场景的功能增强与现场适配

4.1 传感器故障检测与冗余策略

HVAC 设备长期运行,远程探头断线、短路、接触不良都是会发生的问题。固件不能假设传感器永远在线,必须把故障检测当成一项常规功能。

传感器状态寄存器里一般会有远程开路、远程短路、本地传感器故障等标志。我这里推荐一个最简单的故障判断规则:

读到的温度状态可能的故障原因固件处理策略
远程温度超出量程上限远程探头开路或引线断标记远程故障,进入防冷防热保护
远程温度低于量程下限远程探头短路或接反标记远程故障,切换到备用测点
本地温度异常跳变芯片内部故障整体重启传感器或使用看门狗复位
状态寄存器显示传感器错误芯片内部转换异常重新初始化传感器,仍失败则告警

如果系统里有第二个测点或者第二颗传感器,可以做简单冗余。主传感器连续三次读取异常后,自动切换备用通道,同时产生报警供维护人员处理。不要让系统因为一个探头坏了就完全停摆,也不要瞒着故障继续盲目控制。这种“带病运行 + 明确告警”的方式更符合 HVAC 设备的工程实际。

4.2 现场安装造成的测量偏差如何校准

传感器即使在实验室里很准确,装上设备后也可能偏离真实环境温度。原因很常见:控制器贴在墙面,墙体温度比空气低;传感器离回风口很近,空气流速高导致散热强;又或者控制箱内部发热严重,让整个控制器外壳都偏热。

工程校准不适合只做硬件层面的简单调零,我用软件方式落地:在接线端子附近预留标准温度计插孔,然后在两个温度点做两点校准,算出增益和偏移量,写进固件的配置参数。

typedef struct { float gain; float offset; } CalibrationParam; float apply_calibration(float raw_temp, CalibrationParam param) { return param.gain * raw_temp + param.offset; }

两点校准的做法是:在设备安装现场,让室内处于一个稳定的较低温度状态,记录传感器读数 T1 和标准温度计读数 S1;再人为把房间升到另一个稳定温度,记录 T2 和 S2。然后计算:

gain = (S2 - S1) / (T2 - T1); offset = S1 - gain * T1;

这套修正逻辑调试起来很直观,比在数据手册里填偏移寄存器更灵活,而且可以按不同安装位置单独设置参数。校准参数要保存在外部 EEPORM 或 Flash 参数区,不能写成死代码,否则现场设备每台都要重新编译固件,维护成本太高。

4.3 温控策略如何结合本地和远程温度

有了本地和远程温度,HVAC 控制就比单纯用一个传感器丰富很多。最基础的逻辑是用远程温度做环境温度控制,本地温度做设备保护。举个例子:远程回风温度达到设定值就降低风机档位,本地温度过高就触发散热策略或者降低输出功率,防止控制板长期处于过热状态。

稍微复杂一点的做法是用本地温度补偿远程测量的动态误差。某些现场,远程探头处于空气流速很低的位置,温度响应慢半拍,而控制器外壳的本地温度变化却能更快反映房间升温趋势。我做过一版策略:用本地温度变化率作为前馈信号,当本地温度已经快速上升而远程温度还没跟上时,提前减少制冷输出,避免过冲。效果很直观,但需要根据现场调试系数,不能盲目套用。

控制算法的核心是把两个温度看作可用的状态量,而不是彼此替代的关系。只要物理含义明确,对应策略清晰,即使代码不复杂,系统表现也能比单点温控扎实很多。这也是这套方案的最终价值所在。

5. 调试过程中踩过的几个比较深的坑

5.1 I2C 总线锁死与强制恢复

第一次调试传感器时,经常出现的现象是:程序跑了几分钟后,I2C 总线彻底卡住,所有读写都超时。用示波器一抓,发现 SDA 线一直低电平,SCL 时钟还在跳,这就是 I2C 总线被从机拉死的典型表现。

原因多半是传感器在主控复位期间收到了不完整的传输,从机内部的位状态机卡死在中间状态。解决方式有两个:第一个是在初始化传感器之前,先手动把 SCL 线翻转 9 个时钟周期,把从机状态机强制释放;第二个是在硬件上给传感器加独立的电源控制引脚,I2C 卡死时直接断电重启传感器。

软件释放代码可以参考:

void recover_i2c_bus(void) { GPIO_InitTypeDef gpio; // 手动把 SCL 九次翻转,释放从机 for (int i = 0; i < 9; i++) { HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET); delay_us(5); } }

这个坑在批量设备现场更容易发生,因为现场继电器动作产生的电源瞬变比实验室频繁得多。加了这个恢复逻辑后,即使偶尔出现总线异常,系统也能自行恢复,不用派人去断电重启。

5.2 PCB 布局让本地温度读数整体偏高

有一版打样回来,把 PJ85718DM 放在 MCU 正下方,离一个低噪声的 DC-DC 电感太近。结果发现本地温度读数比室温高 12 度,而且无论怎么校准都压不下去。这个不是芯片的问题,是板级热耦合问题:电感、MCU 和传感器在同一个热区域里,热量通过 PCB 铜皮直接传到了传感器底部。

解决办法有几个方向。最直接的是把传感器挪到远离热源的板边位置,最好是主板的角落,并且下方不布置大电流走线。很多芯片的底部焊盘也承担散热功能,如果不想让板温影响测量,可以考虑在传感器下方的 PCB 铜皮上做开槽处理,切断热传导路径,或者在 PCB 设计时单独挖掉一块铜区域。

这类问题还有个排查技巧:用红外热成像仪看整板温度分布,可以直接找到热源和热量扩散路径。没有热成像仪的话,可以把传感器芯片悬空,用引线连到板上,对比悬空读数和贴板读数的差异,同样能判断是不是受到了板级热影响。

5.3 远程探头引线造成的毛刺读数

在某个测试场地,远程探头从控制器走线到风管,途经 2 米双绞线。设备一启动,温度读数就会出现周期性跳动,波动幅度接近 1.5 度。这个现场里有电机启停和继电器通断,电磁干扰相当严重。

我先加了共模滤波电容,改善不明显;又换成屏蔽双绞线,把屏蔽层单端接地之后,毛刺才明显减少。所以如果现场存在强干扰,不要把希望全寄托在软件滤波上,硬件屏蔽是根本。

软件层面也补了一道中值滤波:连续取 5 个采样点,去掉最大最小值,对中间三个取平均。虽然增加了一点采样开销,但远程温度读数变得很干净。实际项目里我通常把“硬件屏蔽 + 软件滤波”一起用,两者缺一不可。

5.4 固件可观测性不足导致的定位困难

还有一个我比较后悔的坑,是早期版本没有把调试信息完整打出来。传感器读到的原始码、滤波值、状态寄存器、报警标志全都闷在固件里,设备在现场出问题时,只能靠猜。

后来我把 UART 调试口做成一个简单的命令行接口,输入temp命令就能打印所有相关信息,输入cal就能临时修改校准参数。这个工具帮了大忙,现场工程师不用拆外壳,直接连串口线就能拿到内部数据。如果项目里已经有 RTOS 或者主控有网络接口,还可以把这些数据通过日志系统上传。调试接口看起来不起眼,但对工程化设备来说,是实打实的生产力工具。

6. 后续还可以往哪个方向扩展

这套本地/远程温度监测框架稳定之后,扩展的方向非常清晰。

第一,把多台控制器的温度数据汇聚起来。STM32F745ZG 自带丰富通信外设,可以通过 Modbus RTU 或者 CAN 把各台控制器的本地温度和远程温度发给集中监管平台。这样可以很直观地在总控室看到各区域温度分布和故障点位。

第二,引入无线遥测。部分 HVAC 项目现场不适合重新布线,可以用低功耗无线模块把远程温度探头的数据传回控制器,只需要在固件里增加一路无线接收数据的输入通道,与有线通道做统一处理。

第三,在算法层做得更智能。当前代码主要做的是滤波、变化率计算和简单 PID。后续可以基于远程温度和本地温度的组合,结合湿度、风速等参数,做一些舒适度模型,实现更精细的节能控制。STM32F745ZG 的算力完全够跑这些算法,不需要更换主控。

最后分享一个特别实用的小习惯:把所有温度换算和校准逻辑单独封装成一个模块,不跟具体传感器型号的寄存器操作混在一起。这样以后哪怕换成别的温度传感器,只要改底层寄存器层,上层控制、显示、告警代码基本不用动。对长期维护的项目来说,这种分层思路能省下大量时间。

如果你正要搭一套类似的双温度采集系统,建议先把本地温度和远程温度的物理边界想清楚,再动手画板子写代码。硬件确定了,软件框架理顺了,剩下的就是耐心调校和现场验证了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进