最近在做一套 HVAC 电控柜的温度监测改造,核心方案是 STM32F100ZE 主控 + PJ85718DM 温度传感器,专门解决“本地和远程温度监测”这件事。整个项目做下来,我发现这套组合在嵌入式和暖通空调场景里都挺能打:板子面积小、接线少、测温精度够,还省掉了原来那套老式的模拟变送器。这篇文章我把选型思路、电路设计、固件坑位和实测数据全部写出来,给同样在搞嵌入式温控或者 HVAC 控制板的同学一个参考。
项目背景不复杂:我们有一套风柜控制系统,现场需要同时监测柜内电子元器件附近的温度,以及风道、回风口这些不可能把主板放过去的远端点位。之前用的是 PT100 变送器加模拟量采集,一套下来成本高、接线多,而且零点校准很麻烦。换成 PJ85718DM 之后,一颗芯片就同时把本地通道和远端通道解决了,配合 STM32F100ZE 的 I2C 接口读取数值,从硬件到固件都干净了很多。
下面我从“为什么这个方案成立”讲起,一路说到原理图、寄存器配置、实际联调遇到的坑,最后给出生产落地建议。内容偏实战,有动手计划的朋友可以直接照着做。
1. 为什么 HVAC 和嵌入式系统需要本地、远程双通道测温
1.1 一个真实改造项目里的测温痛点
我们这套柜子以前是这么做的:柜内一个 PT100 探头测环境温度,风道里再放一个 PT100 探头测送风温度,两个探头分别接到两个模拟变送器,变送器再输出 4-20mA 到 PLC。这套结构本身没毛病,但问题也明显——变送器占据柜内大量空间,每路都要单独校准零点,接线端子一多维护就头疼,而且长期运行后偏差得靠人工定期复核。
后来嵌入式主板介入,想在柜内直接用一个单片机来处理温度采集和控制逻辑。当时摆在面前的选择就是:要么继续用模拟方案,要么换数字传感器。我抽样对比了几颗数字温度芯片,最后把 PJ85718DM 放到板子上试跑。它的好处是本地通道外,还留了一个专门的远端测温接口,正好对应我们“柜内一个点,风道一个点”的需求。
实际操作中,HVAC 项目里最麻烦的不是“测不准”,而是“测点分散”。风机盘管、新风机组、电控柜,每个柜子至少三四个测温点,全部用独立模拟传感器拉线回来,成本和故障率都往上涨。数字芯片把“传感器信号数字化”的动作直接在探头端完成了,线缆只需要跑 I2C 或者类似的数字总线,抗干扰能力和布线便利性完全是两个层级。
1.2 本地温度和远程温度的物理含义
很多人第一次看到“本地/远程温度”这个概念会懵:都在一块板子上,怎么还分远近?
这里的“本地温度”,指的是 PJ85718DM 芯片自身所在位置的温度,也就是 PCB 板卡附近的空气环境温度。它内部集成了测温元件,固件直接读芯片内部寄存器就能拿到数值,不需要外接任何探头。适合监测控制柜内部环境、单片机附近是否过热,或者作为设备本身工作温度的保护值。
“远程温度”则指的是通过芯片专门预留的远端测温引脚,外接一颗双极性晶体管(比如常见的 2N3904 接成二极管形式),把这只管子放到需要测温的远端位置。芯片内部有一个恒流源,会给远端二极管提供偏置电流,通过测量二极管正向压降随温度的变化关系,换算成远端温度值。
我们现场就是把这颗 2N3904 用小螺丝固定在送风管道外壁上,用两条普通双绞线传回主板,距离大概 1.5 米。实测下来稳定性和精度都在预期内。这种结构特别适合 HVAC 里那种“采样点在风道、盘管、冷冻水管,而控制板在电柜”的经典布局。
1.3 双通道方案和多个单点传感器的区别
那有人会问:既然要测两个点,为什么不直接放两颗独立的数字温度传感器?
理论上完全可行,但实际工程里有几个差异。首先,如果全是独立传感器,每颗都要有一个唯一的 I2C 地址,地址不够用的时候就得加地址切换电路,复杂度和故障点自然上去了。而 PJ85718DM 这种本地+远程的组合,一个 I2C 地址解决两个测点,省掉一颗芯片的成本。
其次,远端二极管测温有一个好处:作为感温元件的三极管非常便宜,丢了坏了换一颗的成本极低,不用担心“传感器本体被风道里的灰尘和水汽搞坏”。而且三极管体积小,相比封装好的探头更容易塞进各种狭小位置。
我并不是说把所有场景都换成这种双通道方案,如果测点之间毫无物理关联、距离也远,独立传感器依然是更合理的选择。但 HVAC 机柜里最常见的形态就是“柜内一个点+管道一个点”,这种时候一颗双通道芯片的性价比优势就很明显了。
2. 选型逻辑:STM32F100ZE 和 PJ85718DM 为什么是合适的一对
2.1 STM32F100ZE 在项目里的角色
STM32F100ZE 这颗芯片,表面上看性能参数并不算亮眼,Cortex-M3 内核,主频最高 24MHz,但它在这一类“慢速工业控制”场景里非常合适。温度采集本身就是一个毫秒级甚至秒级刷新率的任务,完全用不到高速计算能力,真正需要的是稳定、抗干扰、接口丰富、库存量足。
我们选它还有一个重要原因:引脚数量。ZE 后缀是 144 脚封装,GPIO 资源非常充裕。除了接 PJ85718DM 的 I2C,还要接 OLED、RS485 收发器、继电器驱动、拨码开关、状态灯、报警输出,算下来接口需求接近 40 个引脚。如果用一个小封装单片机,资源不够就得加扩展芯片,反而增加成本。
另外 F100 系列标配多个 USART、SPI、I2C,Modbus RTU、OLED 显示、传感器采集可以各自独立。我记得选型的时候也考虑过低功耗方案,但这套设备常年接 24V 电源供电,功耗并不是核心矛盾,与其在低功耗模式上折腾,不如用一颗皮实耐用的标准芯片。
2.2 PJ85718DM 的功能框架和 I2C 接口
PJ85718DM 是我在某国产物料清单上挖到的片子,网上公开资料不多,数据手册还是扫描版,但这并不影响使用。它的内部结构一句话就能概括:片上集成一个本地温度传感器,外部接一个二极管测温通道,两者经过模数转换后,通过 I2C 接口供主控读取。
芯片地址由 AD0 引脚的接法决定,接低电平时地址是 0x48(7 位地址),接高电平是 0x49。所以我们板子上留了焊盘跳线,做一主多从扩展时可以用。工作电压支持 2.7V 到 5.5V,正好兼容我们板上的 3.3V 系统,输出电压逻辑直接给 STM32F100ZE 读,不需要电平转换。
精度方面,手册给的数据是本地通道在 0 到 70 摄氏度范围内典型误差 ±1 摄氏度,远端通道在 -20 到 100 摄氏度范围内典型误差 ±2 摄氏度。对 HVAC 的监测需求来说完全够用,风道温度本身有 1 摄氏度的波动很正常,硬要追求 0.1 摄氏度的精度反而没有意义。
配置寄存器和温度寄存器走的是标准 I2C 流程,读地址、写配置、连续读两个字节就能得到数据。后面我会给一份寄存器速查表和驱动代码。
2.3 对照热敏电阻 + ADC 的模拟方案
我在做选型对比的时候,把“热敏电阻 + 单片机内置 ADC”这条路也仔细推演了一遍。热敏电阻方案成本确实低,一个 NTC 才几毛钱,单片机自带 ADC 也不用额外买芯片。但问题出在工程上。
热敏电阻的阻值-温度曲线高度非线性,必须在软件里做查表或多项式拟合,每一项校准都要单独标定。而且模拟信号通过长线传输时,线缆电阻、接触电阻、干扰耦合都会直接叠加到测量结果上,误差来源很难排查。ADC 参考电压如果不够稳定,还会引入整体漂移。
PJ85718DM 方案则是把感温、信号调理、模数转换、数字接口全部集成在一颗芯片里,远端三极管虽然也是模拟链路,但芯片内部做了相对成熟的激励和补偿逻辑,干扰敏感度比直接测电阻低很多。系统集成度高了,校准和排查问题的成本都下来了。
当然模拟方案也不是一无是处,如果每个测点的距离只有几厘米,温度范围窄,精度要求低,那 NTC 依然是性价比王。但如果你是做 HVAC 控制柜,需要考虑长线、干扰、维护,数字温度传感器这条路更省心。
3. 原理图设计:双通道测温电路与走线考量
3.1 电源和去耦设计
PJ85718DM 的电源引脚虽然简单,但也不能随便一接就完事。我们板上的 3.3V 由一颗低压差稳压器产生,芯片的 VDD 引脚附近放了一颗 0.1uF 陶瓷电容做高频去耦,再在稍远处放了一颗 10uF 钽电容稳住低频波动。不要小看这两颗电容,HVAC 柜子里继电器频繁吸合,电源毛刺经常会让温度寄存器读到异常值。
ALERT 输出引脚默认是开漏结构,必须在外部接一颗上拉电阻。我们选了 10kΩ,接到 3.3V。这个引脚直接连到 STM32F100ZE 的一个 GPIO 并配置为外部中断输入,当温度超过设定阈值时,芯片会把 ALERT 拉低,主控立刻响应,而不是一遍遍地轮询温度寄存器。
如果系统里还有其他 I2C 设备共用一组上拉,注意计算总线的等效电阻,不要因为多颗上拉并联导致低电平电压超规格。我们板子上 I2C 总线上并联了 PJ85718DM 和 OLED,每颗各自带一组上拉就会偏弱,所以最终只在总线侧统一焊了两颗 2.2kΩ 电阻。
3.2 I2C 上拉电阻取值计算
I2C 上拉电阻不是拍脑袋定的,它和总线电容、通信速率、电平阈值都有关系。我简单推一遍,方便大家以后改参数。
最小上拉电阻由输出低电平电流决定,公式是:R_min = (VDD - VOL_max) / IOL_max。VDD 取 3.3V,标准低电平阈值 VOL_max 是 0.4V,IOL_max 按 3mA 算,得到 (3.3-0.4)/0.003 ≈ 967Ω。实际取值一般会比这个大,给留出余量。
最大上拉电阻则由总线上升时间和总线电容决定:R_max = t_rise / (0.8473 × C_bus)。标准模式 100kHz 要求上升时间小于 1μs,如果总线电容按 200pF 估算,R_max ≈ 1e-6 / (0.8473 × 200e-12) ≈ 5.9kΩ。
所以我们取 2.2kΩ 到 4.7kΩ 都合理。实际选择了 2.2kΩ,主要考虑柜内线束可能额外增加一些寄生电容,电阻小一点上升沿更陡峭,抗干扰更好。通信速率我们固定在 100kHz,没有用 400kHz 快速模式,因为温度采集根本不需要高带宽,低速状态下的时序余量更大,出错概率更低。
提示:如果 I2C 线上需要热插拔,建议在设备侧串一颗几十欧姆的限流电阻,防止带电插拔时的打火损坏。我们量产板没做热插拔需求,就省掉了。
3.3 远端二极管的接线和布局
远端测温的接线是整个硬件设计里最需要讲究的地方。芯片会从 D+ 引脚输出激励电流,流过外部三极管,再通过 D- 引脚返回。这个回路如果布线不当,很容易引入噪声或者产生较大的寄生电阻。
我们选的远端感温元件是 2N3904 三极管,把集电极和基极短接,由集电极、发射极两端引出两根线,等效成一个二极管。接线端子采用两线制,建议用双绞线或者屏蔽双绞线,屏蔽层单端接地。双绞线能有效抑制共模干扰,屏蔽层则对付空间辐射。
需要注意的一点是:远端引脚附近不要直接并联大电容,有些初学者为了滤波会在板端加 1uF 电容,结果导致芯片的激励电流建立时间变长,测量结果严重偏慢甚至跳动。我实测下来,在远端三极管附近放置一个 1000pF 的小电容可以帮助滤除高频噪声,但放在主板端效果不好。
另外,远端导线的串联电阻会引入测量误差。普通铜线每米电阻不过几十毫欧,但如果用了细而长的线,或者经过连接器多次转接,累计电阻就可能到几欧姆。部分芯片会在配置寄存器里提供“串联电阻抵消”功能,可以补偿一定范围内的线阻,我们在固件里把这个功能打开了,后面会讲。
从板级布局上说,D+ 和 D- 的走线要远离继电器线圈、变压器和强电回路,尽量等长靠近走。我们第一批样板因为在远端线束旁边扎了一根 220V 供电线,结果读数值周期性跳变,重新理线之后问题才消失。
3.4 保护器件和抗干扰设计
HVAC 柜子里空间不大,但干扰源不少:变频器、接触器、继电器动作时都会在电源和信号线上感应尖峰。远端探头毕竟要走线到风道,线束在柜外,我们给它加了一颗低电容 TVS 管跨接在 D+、D- 之间,用来泄放静电和浪涌。
同时,ALERT 和 SCL、SDA 信号也都预留了串联电阻的位置。如果量产测试发现信号质量差,可以在主控端串 33Ω 电阻,配合电容构成简单滤波。不过这些元件我是按预留位画的,最终测试合格后没有全部焊接。
还要提醒一件事:I2C 的设备地址设置脚 AD0,一定不要悬空。芯片内部有时会上拉到 VDD,有时会下拉到 GND,悬空状态不确定会导致地址随机,系统初始化时找不到设备。我们板子上用 10kΩ 电阻拉到 GND,默认地址 0x48,需要扩展时把电阻改到 VDD 即可。
4. I2C 时序、寄存器配置与温度换算细节
4.1 软件模拟 I2C 而不是硬件外设
这节是我最想分享的经验。STM32F100ZE 自带的硬件 I2C 外设功能上没问题,但在实际项目中,我遇到过好几次“总线忙寄存器置位后清不掉”的情况。现象就是初始化代码执行到一半发现 BSY 标志为 1,导致通信永远卡住。反复开关外设时钟、重新初始化都无效,最后只能对 GPIO 做多次脉冲复位才能恢复。
后来我干脆在温度采集模块里用 GPIO 模拟 I2C,代码量不大,时序完全可控,出问题也好查。对于 100kHz 这种低速场景,模拟 I2C 的稳定性和灵活性远比硬件外设高。温度采集又不是要跑几百 kHz 高频通信,GPIO 模拟完全够用。
模拟 I2C 的核心就是控制 SCL 和 SDA 两根线的电平时序。发起始条件:SCL 保持高电平期间,SDA 从高拉低;发停止条件:SCL 高电平期间,SDA 从低拉高。发送一个字节就是按位让 SDA 在 SCL 低电平期间改变状态,在 SCL 高电平期间保持稳定。接收数据类似,主控在 SCL 高电平期间采样 SDA 电平。
完整实现代码不复杂,但里面有几个细节要注意:起始条件之后要等一小段时间再操作 SCL,避免电平竞争;每次数据传输完成后要检测从机的 ACK;在一个 32MHz 左右的系统上,每比特间加 1-2 微秒延时即可。我们用的库函数是基于寄存器操作的,跑起来非常稳定。
4.2 配置寄存器和温度读取流程
PJ85718DM 的寄存器按字节编址,温度寄存器是 16 位宽,分成高字节和低字节两个地址连续存放。以下是调试板的寄存器速查表,不同批次丝印可能略有差异,拿到实物先对照手册。
| 寄存器 | 地址 | 读写 | 说明 |
|---|---|---|---|
| 本地温度高字节 | 0x00 | 只读 | 12 位温度的高 8 位,含符号位 |
| 本地温度低字节 | 0x01 | 只读 | 低 4 位有效,高 4 位固定为 0 |
| 远端温度高字节 | 0x02 | 只读 | 12 位温度的高 8 位 |
| 远端温度低字节 | 0x03 | 只读 | 低 4 位有效 |
| 配置寄存器 | 0x04 | 读写 | 分辨率、报警模式、补偿功能开关 |
| 状态寄存器 | 0x05 | 只读 | 本地/远端报警标志 |
| 本地报警上限 | 0x06 | 读写 | 本地通道阈值 |
| 远端报警上限 | 0x07 | 读写 | 远端通道阈值 |
我习惯的初始化顺序是:先把配置寄存器设为 12 位分辨率,启用串联电阻抵消和远端 beta 补偿;然后写入本地和远端的报警上限;最后读一次状态寄存器,清掉上电可能出现的无效报警标志。整个过程大约需要几毫秒,之后就可以周期读取温度。
读温度时我连续读 4 个字节,一次把本地高、本地低、远端高、远端低都读回来,避免多次进出 I2C 通信浪费时间。核心代码大致如下:
void pj_read_all_temps(int16_t *local, int16_t *remote) { uint8_t buf[4]; i2c_start(); i2c_send_byte(0x48 << 1 | 0); // 写地址 i2c_send_byte(0x00); // 从地址 0x00 开始读 i2c_start(); i2c_send_byte(0x48 << 1 | 1); // 读地址 for (int i = 0; i < 4; i++) { buf[i] = i2c_recv_byte(i < 3 ? 0 : 1); // 前 3 字节回 ACK,最后回 NACK } i2c_stop(); *local = (int16_t)(((uint16_t)buf[0] << 8) | buf[1]); *remote = (int16_t)(((uint16_t)buf[2] << 8) | buf[3]); }需要注意,buf[0] 和 buf[1] 拼出来的 16 位值,符号位在高字节的最高位,所以要先把它转成 int16_t 再乘分辨率系数,不能直接用无符号整数做,否则负数温度会算成大正数。
4.3 温度换算与滤波算法
12 位分辨率下,PJ85718DM 的每个最低有效位 LSB 代表 0.0625 摄氏度。换算公式非常简单:
float local_temp_c = (float)((int16_t)((raw_local >> 4) << 4)) * 0.0625f; float remote_temp_c = (float)((int16_t)((raw_remote >> 4) << 4)) * 0.0625f;为什么要把低 4 位清掉再乘?因为 12 位温度数据是放在 16 位寄存器的高 12 位,低 4 位无效。也可以直接写成低 4 位再算成小数:温度值 = 高字节 × 16 + 低字节高位 4 位,再乘以 0.0625。核心是根据寄存器排布来控制不对齐。
温度读回来之后不能直接用,尤其是远端通道,因为现场干扰和线缆条件复杂。我用了一层滑动平均滤波,缓存最近 8 次读数,取平均值输出。这样既可以抑制随机噪声,又不会对缓慢的温度变化造成明显滞后。HVAC 系统的时间常数通常是分钟级,8 次轮询(每次间隔 500ms)也就滞后几秒,完全可接受。
另外还加了一个异常值剔除:如果当前读数与上一次读数的差超过 10 摄氏度,直接丢弃,不进入滑动窗口。这个策略很粗暴,但能有效防止 I2C 偶发误码导致的瞬间跳变。实际联调时有一次远端温度从 25 度莫名其妙跳到 -18 度,就是靠这个规则拦住的。
4.4 远端通道的 beta 补偿与串联电阻抵消
远端二极管测温的原理不复杂,但有两个误差源必须处理:一是三极管/二极管的理想因子(beta 值)在不同型号之间略有差异,二是线缆串联电阻会叠加到测量结果上。
PJ85718DM 的配置寄存器里提供了补偿选项。第一个叫 beta 补偿,用来匹配不同型号的远端三极管。默认配置是按某型号二极管校准的,换成 2N3904 之后,我在初始化和实测后做了一次对比,发现不启用补偿时读数会整体偏高大概 2 摄氏度左右,这显然不能接受。配置寄存器里把补偿功能打开之后,误差明显收窄。
第二个是串联电阻抵消。线缆电阻像是给二极管串了一个电阻,会让芯片测到的正向压降偏大,温度读数随之偏高。启用抵消功能后,芯片内部会做自动校准,扣除信号链路上额外的电阻压降。这一步在项目里效果很明显,1.5 米线缆加上两端连接器,抵消前后读数变化了接近 0.8 摄氏度。
这两个补偿位的具体数值设置,不同批次芯片的寄存器位定义有一定差异,我建议翻一下芯片规格书里的“Remote Diode”章节,确认哪个 bit 是开启补偿,哪个 bit 是设置 beta 系数范围,不要想当然地一套代码通吃所有批次。
5. 实测中的三个大坑:总线忙、读数跳变、温度偏高
5.1 硬件 I2C 总线忙,复位不彻底
第一批样板焊接完,烧录程序后发现系统卡在初始化阶段。用调试器逐行查,发现所有 I2C 操作都卡在一个等待函数里,原因是 I2C 外设的 BSY 总线忙标志始终为 1。理论上如果总线上没有其他主机占用,这个标志应该在总线空闲时自动清零。
排查过程很有意思:一开始怀疑是上拉电阻没焊好,用示波器量 SCL、SDA 波形,两根线都已经被从机拉低到一个异常电平。然后怀疑 PJ85718DM 坏了,拿万用表量它的 VDD、GND,电压正常。最后想到一个可能:初始化流程里主控先访问了 I2C 设备,但此时从机的电源时序还没完全建立,从机直接拉死总线,导致 BSY 标志状态错乱。
解决方法是给 I2C 外设做一次彻底的软件复位:关闭外设时钟,把 SCL 和 SDA 对应的 GPIO 配置为普通推挽输出,然后手动翻转 9 个周期的时钟脉冲,把总线上的从机状态机复位,最后重新启动 I2C 外设。这套流程在错误恢复里屡试不爽。不过因为软件复位逻辑绕来绕去,我在第二版固件里干脆把整个模块换成了 GPIO 模拟 I2C,故障直接消失。
5.2 远端温度读数周期性跳变
第二版样机装进真实机柜后,远端风道温度每几十秒就会出现一次剧烈跳变,变化范围从正常值跳到 -20 度附近。抓示波器看波形,发现远端 D+ 和 D- 上有明显的毛刺,幅度达到几百毫伏。
一开始怀疑是机柜变频器干扰,但把探头挪走之后问题依旧。后来分步排查发现,干扰源头不是变频器,而是我们自己板子旁边的继电器——继电器每次动作瞬间,线圈产生的反电动势通过空间耦合到了远端线束上。我们当时没有设计 TVS 管位置,也没有用屏蔽线,所以干扰直接灌进了模拟测量链路。
处理措施有三条:第一,在远端接口处焊上低电容 TVS 管;第二,把远端线束换成屏蔽双绞线,屏蔽层在主板端接地;第三,固件里把 I2C 轮询间隔固定在一个不会和继电器动作重合的相位上,虽然治标不治本,但给了系统观察窗口。做完之后,温度跳变现象基本消除。
后来我又在调试中发现,读取远端温度时偶尔会出现 NACK 或者读出全 0xFF 的情况,这和模拟 I2C 的时序有一定关系。解决办法是做一个三取二的表决:连续读三次,如果三次里有两个值接近,就用这个值;如果三次都各不相同,就继续重读。配合前面说的异常值剔除,这套逻辑在量产板上表现很好。
5.3 远端读数整体偏高,校准才发现根源
还有一件事让我印象深刻:第一版固件烧进去之后,用冰水混合物做零点校准,本地通道读数很准,远端通道却整体偏高 2 摄氏度左右。乍一看以为是芯片个体差异,后来把配置寄存器的 beta 补偿参数逐位调整,问题才找到根源。
问题出在远端三极管的型号上。2N3904 的 beta 特性和早期校准用的某个国产二极管不一样,芯片的默认补偿值是按前者调的。我在初始化代码里最初没有打开 beta 补偿功能,等于用默认参数硬算温度,自然不准。打开补偿并设置正确分档后,整体偏差降到了 0.3 摄氏度以内。
这件事给我的教训是:数字温度传感器并不能省略校准环节,只是把校准工作从“标定变送器”变成了“确认配置参数”。如果换了远端三极管型号,必须先做几次对比测试,把补偿分档、串联电阻抵消打开或关掉都试一遍,找到最适合当前器件组合的配置。
6. 从监测到联动:本地显示、远程上送与保护逻辑
6.1 本地显示:OLED 与地址冲突的坑
温度读到之后,第一步自然是显示出来。我们在柜门内侧装了一块 I2C 接口的 OLED 屏,显示本地温度、远端温度、设备运行状态和一个简单的报警信息。OLED 的 I2C 地址固定,很容易和 PJ85718DM 冲突,不过我们这颗芯片有 AD0 引脚可以改地址,所以板卡上用跳线把 PJ85718DM 设为 0x49,OLED 保持默认地址,两者互不干扰。
显示逻辑没有做成每 500ms 刷新一次,那样太闪烁,看着闹心。我们把刷新周期定在 2 秒左右,只在温度变化超过 0.3 摄氏度时才立刻刷新,否则保持上一次画面。这样既省了主控的 I2C 开销,也避免 OLED 高频刷新加速老化。
还有一个小细节:OLED 和温度传感器共用 I2C 总线,但 OLED 的初始化偶尔会因为总线繁忙而失败。我在上电流程里把延时延长到 300ms,并且在 OLED 初始化失败时自动重试三次,而不是整板死等。这个做法让我们在生产测试时减少了不少误报。
6.2 远程上送:Modbus RTU 与 RS485
遥控温度数据如果只在本地 OLED 上显示,那这套系统就废了一半。我们的机柜有统一的上位机监控,所以我让 STM32F100ZE 通过 USART 外接一颗 RS485 收发芯片,以 Modbus RTU 协议把温度数据上送。
Modbus 从机地址用板卡上的拨码开关设置,温度映射到保持寄存器:40001 放本地温度,40002 放远端温度,40003 放状态标志。温度值按整数十倍上传,比如 23.4 度上传为 234,这样上位机不用处理浮点数,省去一堆格式转换问题。
RS485 是半双工总线,发送数据时要把方向引脚拉高,发送完成后先延时一小段再拉低。很多人栽在这个方向上:发送完最后一个字节后必须等总线完全释放,否则紧接着收到的响应帧会被自己的队尾打断。我们在 USART 发送完成的回调里加了一个 4 字节时间的延时,实测很稳定。
6.3 联动保护逻辑:过温、低温与温差控制
既然能实时拿到温度和报警信号,接下来就可以做控制联动。我们实现的第一个逻辑是风道过温保护:远端温度超过 70 摄氏度时,直接从 ALERT 中断进入紧急停车子程序,切断加热器输出;普通报警则由主循环查询状态寄存器后处理,让 ALERT 保留给最紧急的情况。
第二个逻辑是防冻保护:冬季如果回风温度低于 5 摄氏度,自动打开防冻电加热,等温度回到 10 摄氏度以上再关闭。这里必须设置回差,否则温度在设定点附近抖动会导致继电器频繁通断。我们把回差设在 2 摄氏度,实测继电器动作频率大大降低。
第三个逻辑比较实用:比较本地温度和远端温度的差值,如果差值过大,说明风道热交换出了问题,比如风机停了或者滤网堵塞。平时温差稳定在 5 摄氏度以内,如果连续 10 分钟超过 8 摄氏度,上位机就推一条维护提醒。这个功能客户非常喜欢,等于花一颗传感器的钱做了一个简易诊断。
7. 生产落地经验:测试点、校准和备料建议
7.1 画板时预留测试点
画 PCB 的时候,很多人只顾着功能连通,忽略了生产测试的便利性。我们在 I2C 的 SCL、SDA、远程 D+、D-、ALERT 五个信号上都预留了 1.27mm 间距的测试焊盘。产线测试时,用探针直接夹住这些焊盘,跑一段自检脚本,就能快速判断主板是否正常。
如果没预留测试点,后期出了问题只能剥开线缆量,不仅费时,还容易损坏板子。我建议所有信号线里越“娇气”的越要留测试点:模拟测温线、I2C 线、报警中断线。电源和地反而不用刻意引,电容脚上就能量到。
量产测试固件里写了一段自动巡检逻辑:上电后读一次本地和远端温度,如果读数超出合理范围就点亮红色故障灯;如果芯片通信失败,则通过串口打印错误码。这样产线工人不需要会看代码,只要看灯就能判断板卡好坏。
7.2 校准流程和系数管理
温度芯片的校准不像模拟采集那样需要逐板微调,但也需要验证。我们的做法是抽测:每批次抽三块板,在冰水混合物里验证 0 度附近,在 50 度恒温水浴里验证高温段。如果抽测板偏差都在 ±1 摄氏度以内,整批不再单独校准;如果超差,说明物料批次可能有问题,整批退回排查。
即使芯片一致性很好,我仍然在固件里加入了“软件校准系数”的接口:从外部 EEPROM 读取两个浮点数,一路是比例系数 k,一路是偏移量 b,实际温度 = 原始温度 × k + b。这样万一某块板出现固定偏差,只需写入一组系数就能修正,不需要拆板换芯片。
校准系数和硬件版本号、固件版本号放在同一个信息块里,读取时做 CRC 校验,避免 Flash 里数据损坏导致设备用错参数。这在批量设备维护时特别省心,现场通过上位机就能远程改校准系数,不用打板子。
7.3 远端三极管的备料一致性
远端三极管虽然便宜,但不同厂家、不同批次的产品在 beta 特性和封装热阻上有差异。如果项目后期更换了供应商,或者买到不同批次的 2N3904,远端温度读数很可能整体漂移。
最好的办法是在设计阶段就锁定供应链:选一两家长期稳定供货的厂商型号,写进 BOM 备注;更换器件前先做至少 20 只样品的温度一致性测试。如果必须换型,则需要回到 4.4 节说的 beta 补偿配置,重新做一轮校准。
封装也很重要,直插式 TO-92 和贴片 SOT-23 的热响应速度不一样。贴在同一个风道位置,直插式反应慢一些,但接触面积大、安装牢固;贴片式响应快,但散热路径容易受焊盘和走线影响。我们用直插式,因为现场维护人员更习惯这种规格,换起来也方便。
7.4 如果重新选型,我会怎么做
到这里项目已经稳定跑了一个夏天,回头复盘时我也会想:如果从头再来,方案会不会有变化?
大概率还是选双通道温度芯片加 STM32F100ZE,因为这套方案的性价比和稳定性足够。唯一会调整的是远端线缆长度设计:如果哪天遇到超过 3 米的远端距离,我不会继续依赖 I2C 上的远端二极管,而是改用一个小型子板方案,子板放一颗独立的数字温度传感器,通过 RS485 或者无线方式把数据回传。这样做虽然物料成本高一些,但长距离传输的可靠性更有保障。
PJ85718DM 的适用范围,我的判断就是“柜内 + 短距离管道点”这一档,对应 HVAC 机柜里绝大多数测点部署场景。只要距离控制在两三米以内,走线绕开强干扰源,配合软件滤波,它能很轻松地交付一个令人满意的温度数据源。