ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

STM32驱动双通道温度传感器PJ85718DM实现本地远程测温

STM32驱动双通道温度传感器PJ85718DM实现本地远程测温

做嵌入式和 HVAC 控制器的人,早晚都会遇到同一个需求:板子上的温度和远处测温点的温度都想覆盖,手头却可能只剩一路空闲 I2C。我最近一块试验板就是这个情况——板卡环境温度要盯着,远处风道里的温度也要盯着,最开始我想上两颗独立温度传感器,最后发现一颗 PJ85718DM 就能同时管住本地和远程两路温度,配合 STM32F071VB 的 I2C 外设,整个温度采集模块占的 PCB 面积小得可以忽略。这篇文章把方案背景、硬件接线、固件写法,以及我在 HVAC 强电磁环境下踩过的坑一次说清楚,适合正在做温控器、嵌入式板卡监控、环境监测这类项目的开发者参考。

1. 为什么"本地+远程"双通道测温比两个单点传感器更省心——背景与方案选型

1.1 嵌入式和 HVAC 应用里,到底要测哪些温度

先明确一个容易误解的点:这里说的"本地"和"远程",不是指手机 App 远程看温度,而是一颗传感器芯片内部有两个测温单元。本地通道测的是芯片封装自己的温度,通常用来当板卡环境温度或控制器机箱温度;远程通道测的是芯片外一个二极管探头的温度,探头可以放在芯片几十厘米甚至一米以外的地方。所以这个项目标题里的"本地与远程温度",指的是芯片内、外两路测温点。

嵌入式应用里,最常见的测温点有三个:板卡环境温度、功率器件附近温度、处理器结温。前两者用普通数字传感器或 NTC 都能凑合,但处理器结温基本只能用热敏二极管来测,而这正好是双通道传感器远程通道的强项。把远程通道接到处理器内部的 thermal diode 引线上,就能实时读到结温,不需要在芯片上额外贴探头。

HVAC 应用的测温点就更分散了:回风温度、送风温度、盘管温度、控制器自身温度。盘管或风管离控制板少说几十厘米,多则一两米。如果每个点都放独立传感器,成本、走线和维护都麻烦;如果只用一个本地传感器,那测到的只是板卡附近温度,和真正的盘管温度差得远。一颗带本地和远程双通道的传感器,正好一个通道管板上,一个通道管远处的物理测点。

1.2 常规方案对比:双芯片、NTC、双通道传感器

做方案选型时,大多数人第一反应是"一个测温点放一个传感器"。这样确实直接,但问题也明显:BOM 多一颗料、PCB 多一组走线、I2C 地址位不够分。用两片单通道数字传感器时,如果芯片地址可选位少,总线上再挂其它 I2C 器件就容易冲突。用 NTC 加 ADC 的方案也常见,但 NTC 的阻值-温度关系不是线性的,需要查表或多项式拟合,分压电阻的温度漂移还会影响精度,长期稳定性更差一些。

双通道传感器方案把两路测温收敛到一颗芯片上,本地通道和远程通道共用同一个寄存器组,读起来就是两个相邻寄存器的事。校准时也只要对一颗芯片做一次修正,温度一致性天然比两片独立芯片好。我在下表里整理了几种常见方案的差异,方便对照选型。

方案远程测点多远I2C资源占用额外硬件校准复杂度
单通道数字传感器 + NTC看NTC线长1个地址ADC通道、分压电阻、查表NTC查表+分压电阻漂移
两片单通道数字传感器看传感器探头线长2个地址两颗料、两组走线两片分别校准
双通道温度传感器半导体探头可达数十厘米以上1个地址几乎无一颗芯片一组offset

当然,双通道方案也不是没有前提:远程通道要求测点是一个 PN 结,而不是任意一个电阻温度探头。好在有些场景本身就具备这个条件,有些场景我们可以主动把探头做成二极管形式,后面章节会详细说。

1.3 STM32F071VB 的资源为什么够用

STM32F071VB 是一颗 Cortex-M0 内核、最高 48MHz 主频的 MCU,片上有 128KB Flash、16KB SRAM,I2C 外设有两个,还有 CAN、USART、SPI 这些常用接口。放在这个项目里,读温度只是它很小的一个任务,剩下的算力可以用来跑 PID、Modbus 协议、继电器时序控制,甚至做简单的记录存储。它的工作电压是 2.0~3.6V,在 HVAC 控制板上和 3.3V 仪表电源搭配很自然,LQFP100 封装也提供了足够的 IO 去点数码管、读按键、驱动继电器。简单说,选它不是因为"必须用它测温度",而是因为这类控制器本来就需要一颗 IO 丰富、通信接口齐全又不贵的 MCU,测温只是顺带把它用得更满。

2. PJ85718DM 的测温原理与硬件接线——双通道传感器的正确打开方式

2.1 本地测温单元:带隙基准与片内 ADC

PJ85718DM 本地通道的测温核心是一个带隙基准加一个模数转换器。温度变化会引起硅片上晶体管 Vbe 的电压变化,带隙基准把这个电压变化稳定地转换成可以量化的信号,ADC 再把它变成数字温度值写到寄存器里。这类芯片出厂前都会做温度校准,所以本地通道的典型精度可以做到 ±1℃ 到 ±2℃ 左右,对大多数环境监测和 HVAC 控制已经足够。平时使用中,本地通道不需要接任何外部元件,芯片上电后连续转换,温度值会周期性地刷新到数据寄存器里。

2.2 远程测温单元:二极管测温的两个关键点

远程通道测温的本质是测外部 PN 结的正向压降随温度的变化。PN 结正向压降会随温度升高近似线性下降,温度系数大约是 -2mV/℃,这是一个非常规律的关系。但直接用绝对压降解温度会踩坑:PN 结的绝对压降跟工艺离散度关系很大,同一型号的两个二极管,在相同温度下 Vbe 可能差几十毫伏,对应温度误差能达到好几摄氏度。更关键的是,探针引线本身有电阻,线阻上的压降会和二极管真实压降混在一起。

所以芯片内部不是简单地测一次压降,而是用两个不同电流先后激励同一个二极管,测两次压降,取差值来算温度。因为 ΔVbe 正比于 ln(I1/I2),串联电阻在两次测量里产生的压降是相同的,做差之后就消掉了。这就是为什么远程通道明明测的是二极管,却能给出比较可靠温度的原因。对于做嵌入式的小伙伴,这个原理同样解释了为什么 CPU 内部的 thermal diode 引线很细很长还能测温——芯片驱动的测量方法本身对串联电阻不敏感。

远程探头的典型形态有两种:一种是直接把目标芯片自带的 thermal diode 接到 D+、D- 两个引脚上;另一种是用一支小信号三极管,把基极和集电极短接,当作一个二极管探头使用。具体用哪种管子、哪个极性,要按 PJ85718DM 数据手册的参考电路来,不同芯片对二极管类型的要求不完全一样。

2.3 硬件接线清单与常见错误

下面是这次试验板上最常用的一组连接,照着搭一般都能跑通:

引脚接到哪里说明
VDD3.3V电源传感器与MCU共用电源域
GND电源地与MCU严格共地
SCLSTM32F071VB PB6I2C1时钟线
SDASTM32F071VB PB7I2C1数据线
A0/A1接GND按手册定义,组成I2C地址
D+远程二极管阳极探头正极,走线要短/平行
D-远程二极管阴极探头负极,与D+尽量等长

SCL 和 SDA 上需要各放一个上拉电阻,阻值通常选 4.7kΩ。如果总线上还挂了其它 I2C 设备,上拉阻值要按整条总线的总电容重新算,不能照抄。远程探头走线要尽量靠近地线或包地,避免从继电器、变压器、高频开关电源下面穿过。探头端如果有空间,可以在 D+ 和 D- 之间并联一个小电容滤除射频干扰,一般在 1nF 左右,具体上限以手册为准,电容太大反而会让芯片激励电流的翻转时间变长,读取结果异常。

这里有个特别容易犯的错:把远程探头接反。二极管接反之后,芯片读回来的远程温度要么是极其离谱的数值,要么会直接抛出开路/短路错误标志。所以第一块板子上电后,发现远程温度在 -100℃ 附近跳或者一直报错,先别急着怀疑固件,用万用表量一下 D+、D- 之间的二极管压降方向,多半能找到问题。

3. STM32F071VB 端 I2C 采集程序——从寄存器读取到稳定温度值

3.1 I2C 初始化与首次通信

用 HAL 库初始化 I2C1 最省事。打开工程,把 PB6/PB7 复用为 I2C_SCL/I2C_SDA,时钟设置成 100kHz 或 400kHz。我的建议是先用 100kHz 标准模式把链路验证通,再上快速模式;HVAC 这种温度数据本身刷新不快,100kHz 完全够用。初始化之后,第一件事是读一次配置寄存器或设备 ID 寄存器,确认芯片真的在线,避免后面读出来的全是 0xFF 还在找原因。

3.2 配置、读取与温度换算

温度传感器的寄存器布局通常很规律:本地温度高字节、低字节、远程温度高字节、低字节,外加一个状态寄存器和一个配置寄存器。配置寄存器里最常用的是分辨率和运行模式两项,我把分辨率设为 12 位、连续转换模式,这样每次读取读到的是最新转换结果。

读取温度时要注意高低字节的一致性。如果先读高字节,芯片内存的转换结果却在两个字节之间更新了一次,那拼出来的值就是错乱的。多数传感器会用"锁存"机制避免这个问题:只要你读了高位字节,低位字节就被冻结,直到你把它读走。所以我在代码里习惯用一次 burst read 连续读两个字节,而不是分开读两次。代码框架大概是下面这样:

#include "main.h" #define PJ85718_ADDR (0x48 << 1) /* 8位I2C地址,A0=A1=0时常见 */ #define REG_LOCAL_H 0x00 /* 本地温度高字节(示例偏移)*/ #define REG_LOCAL_L 0x01 #define REG_REMOTE_H 0x02 /* 远程温度高字节(示例偏移)*/ #define REG_REMOTE_L 0x03 #define REG_STATUS 0x04 #define REG_CONFIG 0x05 static int16_t read_high_low(uint8_t reg_hi) { uint8_t buf[2]; if (HAL_I2C_Mem_Read(&hi2c1, PJ85718_ADDR, reg_hi, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) != HAL_OK) { buf[0] = 0xFF; buf[1] = 0xFF; } return (int16_t)(((uint16_t)buf[0] << 8) | buf[1]); } float read_local_temp(void) { int16_t raw = read_high_low(REG_LOCAL_H); raw >>= 4; /* 12位数据右对齐时丢弃低4位,按手册调整 */ return raw * 0.0625f; /* 12位分辨率LSB=0.0625℃ */ } float read_remote_temp(void) { int16_t raw = read_high_low(REG_REMOTE_H); raw >>= 4; return raw * 0.0625f; }

代码里的寄存器偏移是我手头这类双通道传感器很常见的布局,PJ85718DM 的真实偏移一定要以你的数据手册为准。不少新手就栽在这一步:抄了别人代码里的偏移,结果读出来的温度要么跳变要么恒定,其实只是地址对不上。

3.3 温度换算里的符号位陷阱

负温度处理是最容易被忽略的细节。12 位分辨率下,温度寄存器里存的是有符号数:温度 0℃ 以上时二进制最高位为 0,0℃ 以下时最高位为 1。如果直接把两个字节拼成 uint16_t,-5℃ 会被算成 251℃。所以读回来一定要用 int16_t 来组合,还要根据寄存器对齐方式决定是否移位。代码里 raw >>= 4 是因为我把数据当作 12 位右对齐来处理的,如果你的芯片是左对齐格式,移位方向就不一样了。这里的判断方法很简单:数据手册里一定会给温度与寄存器值的对应关系,找一个已知温度点(比如 0℃ 的冰水)读一遍,看代码输出对不对就知道要不要调整对齐方式了。

3.4 软件滤波与状态检查

温度读取本身很快,几百毫秒读一次都算频繁。但原始值里偶尔会混入噪声毛刺,尤其是 HVAC 这种带继电器的环境。我只做两步:一阶低通加限幅。一阶低通就是 y = y + k * (x - y),k 取 0.1 到 0.2,采样周期 500ms 时相当于 2 秒左右的时间常数,既能把毛刺滤掉,又不会让温度响应显得迟钝。限幅则是说如果新读到的值和上次有效值相差超过 10℃,直接丢弃这次数据,连续丢弃超过 N 次才把当前值强制更新为最近读回的异常值,这样探头真正坏掉的时候至少能报警而不是永远卡在旧值。

每次读取后我还习惯性地看一眼状态寄存器,远程探头开路或者短路都会在状态位上反映出来。读取失败时也不要把 buf 里的 0xFF 当真,HAL_I2C_Mem_Read 返回非 HAL_OK 就要走错误处理分支。工程上我一般维护两个全局变量:current_temp 和 status_last_error,测点报警逻辑只认这两个变量,不会去碰原始寄存器值。

4. HVAC 盘管探头与嵌入式结温监测:两种场景的实测差异

4.1 嵌入式场景:远程通道盯结温,本地通道当机箱温度

嵌入式场景下,远程通道最迷人的用法是接处理器 thermal diode。我在试验板上模拟过一套嵌入式散热监控:主控芯片的 thermal diode 引脚直接接到 PJ85718DM 的 D+/D-,本地通道贴在板卡空闲区域。跑个压力测试,低负载时本地 27.6℃、远程 41.8℃;满载时本地 30.2℃、远程 77.5℃。远程通道反映的是硅片内部温度,可以提前感知到芯片快过热了,风扇提速可以早半拍,这个提前量对被动散热的设备很重要。相比之下,本地通道测的是机箱内环境温度,长年累月记录之后还能反过来判断散热风道是不是堵了。

4.2 HVAC 场景:把二极管探头做成风管里的测温点

HVAC 里常见的做法是拿一支小信号三极管 BC 短接,套进防水套管里做成探头,放在送风口或盘管位置。探头引出线一般用双绞屏蔽线,屏蔽层在控制器端单点接地,D+/D- 在探头端并联一个电容。这样做的效果我在一套空调箱控制器样机上验证过:本地通道测电控盒内温度约 33℃(里面元件发热明显),远程通道测送风管温度约 12.8℃,两个点相差 20℃ 以上,要是当初只放一个本地传感器,控制器永远以为环境 30 多度,防冻逻辑根本没法做。

HVAC 的另一个特点是温度变化比嵌入式慢得多。嵌入式结温可能几秒钟就跳好几度,风管温度可能十几分钟才动 1℃。所以 HVAC 这边的滤波时间常数可以放得更长,甚至直接用每秒读一次、一分钟平均值的方式,稳定性会更好,也不会给 PID 控制器带来噪声。

4.3 强电磁环境下的读值跳动压制流程

HVAC 控制板里最讨厌的干扰源是继电器和接触器的线圈。触点一吸合,几毫秒的电流尖峰能沿着电源线、地线甚至板上的走线耦合进传感器引脚。我这边远程通道的读数在继电器动作瞬间跳过 8~10℃,排查过程是按顺序做的:先在探头端并 1nF 电容,无效;再给 3.3V 电源增加 10uF 电解电容加磁珠,减轻了一些;最后把传感器换了个位置,让 D+/D- 走线远离继电器下方的高压区,同时把屏蔽层单端接在控制器地,读数才真正稳定下来。

这个案例给到大家的经验是:屏蔽线、差分布线、电源滤波、软件滤波四层防护,一层都不能省,但也不必一次全上。先找出干扰耦合路径,再对症下药,比盲目堆滤波电容高效得多。

下表是我对两个场景的参数差异汇总,方便以后直接套用:

维度嵌入式场景HVAC场景
远程探头形态处理器thermal diode引脚外接三极管封装探头
探头到芯片距离几厘米到十几厘米几十厘米到一米左右
主要干扰源功率开关、时钟、总线噪声继电器、接触器、压缩机型负载
推荐滤波时间常数0.2~1秒1~10秒
主要用途结温保护、风扇调速防冻保护、送风温度PID、除霜判断

5. 我踩过的坑与最终校准确认——远程二极管、I2C 与负温度

5.1 坑一:远程探头随便用个普通二极管,读数高十几度

第一版样机我想省钱,直接用手里现成的 1N4148 当远程探头,结果读回的温度比真实值高 12℃ 左右。原因是 1N4148 的漏电流和串联电阻特性跟芯片驱动的期望不匹配,远程探针模型需要一个漏电流小、BC 短接的小信号三极管,比如 2N3904 或 MMBT3904 这类。换上 BC 短接的 MMBT3904 之后,远程通道读数瞬间恢复正常。所以远程探头不是随便一颗二极管都能用的,第一次做这类方案的朋友最容易在这个细节上栽跟头。

5.2 坑二:I2C 上拉电阻 10k,400kHz 下面通信抽风

我习惯总线上挂多个 I2C 器件,上拉电阻随手用了 10kΩ。100kHz 标准模式下一切正常,改成 400kHz 快速模式后就开始偶尔读到 0xFF。示波器一看,SCL 的下降沿变得平缓,明显是总线电容和 10kΩ 上拉不匹配,上升沿时间超过了规范。把上拉换成 4.7kΩ 后问题消失。这个例子说明:I2C 上拉阻值不是随便选的,它会直接影响信号完整性,尤其总线走线较长、节点较多的时候,必要时要根据总线电容算一算。

5.3 坑三:负温度符号位没扩展,-5℃ 显示成 251℃

冷冻类应用里最容易碰到这个问题。12 位有符号温度数据,如果代码里用 uint16_t 拼字节,-5℃ 的补码会被当成 251℃。我的修正方式就是前面代码里写的:用 int16_t 拼装,必要时做符号扩展后再右移。调试技巧是拿一盆冰水混合物校零,把远程探头浸进去,看代码输出是不是 0℃ 附近;不是的话先怀疑符号位和对齐方式,别急着调 offset。

5.4 坑四:HVAC 继电器板上的周期性温度跳变

这个问题我前面提过,再展开说。现象是远程温度每 5 秒跳一下,跳变幅度 6℃ 左右,和继电器动作完全同步。排查链路是:先用示波器勾 D+/D- 两条线,发现继电器动作瞬间有振荡毛刺;然后在探头端并联 1nF 电容,毛刺明显变矮但没消除;再检查走线,发现 D+/D- 从继电器下方穿过,高压侧和低压侧距离太近;最后把走线移到远离继电器的板边,加上屏蔽层单点接地,读数稳定。整个过程说明:这种问题不是单一手段能根治的,布局才是一切的根源。

5.5 两点校准:没有恒温箱也能做

没有专业恒温箱时,我用冰水混合物和一台恒温烙铁做了两点校准。先把本地探头和远程探头都放进冰水混合物里,记录各自读数;再贴上恒温烙铁的一个已知温度点,记录第二组数据;最后在固件里做线性修正 y = a * x + b。a 用来补偿增益偏差,b 用来补偿偏置。修正系数烧录到 Flash 里,上电时读取。远程探头校准时要保证探头和校准点充分接触,不然修出来的系数比没修还差。至少两点才能校正斜率和截距,单点校正只能拉平偏置。

6. 个人体会和扩展玩法

做完整套温度监测之后,我的直接体会是:这种双通道传感器方案最舒服的场景是"点数少、距离远"。板上一个点,远程一个点,一颗芯片、一个 I2C 地址、一组 offset 校准,很干净。但如果测温点一次要超过四个,那还是老老实实上多通道 ADC 或者分布式总线传感器吧,双通道传感器一个一个堆反而贵。

后面我把这套读数逻辑拓展了一下,把本地温度和远程温度换算成 Modbus RTU 寄存器,通过 RS485 让上位机轮询。对 HVAC 的控制器来说,两个温度点正好够一个简单的 PID 回路用——比如盘管出水侧温度和送风温度。如果你手头正好也在做类似的嵌入式或 HVAC 测温项目,建议第一件事永远是翻手册确认寄存器偏移、分辨率对齐方式和远程探头极性,第二件事才是照着我上面的代码套路往下走。我踩过的这些坑,希望你能少走一遍。

RELATED READING

延伸阅读

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