做嵌入式控制的这些年,我越来越觉得“温度”这个看似简单的物理量,恰恰是最容易翻车的环节。最近在做一套风柜温控样机,主控选了 PIC32MZ1024EFF144,温度采集部分用了 PJ85718DM 数字温度传感器,同时监测本地与远程两路温度——一路测控制器附近的环境温度,另一路通过线缆外接去测风管里的温度。这中间牵扯到传感器选型、长线 I2C 可靠性、固件滤波、现场校准等问题,值得把完整的思路和踩坑记录整理出来。
如果你正在做 HVAC 控制器、嵌入式设备的环境温度监测,或者手头正好有类似的“板载+外置”双路测温需求,这篇文章应该能帮你少走不少弯路。下面我就从需求拆解开始,按硬件、固件、实测、排查的顺序,把整个方案讲透。
1. 需求拆解:为什么要用 PJ85718DM 和 PIC32MZ1024EFF144
1.1 项目需求到底是什么
表面上看,这个项目就是“测两个温度”,但拆开来看有三个隐含要求:
第一,本地温度必须准确。控制器装在风柜控制箱内,箱内本身有变压器、继电器、主控芯片这些发热源,本地温度如果贴近热源,测出来的根本不是环境温度。所以本地温度传感器既要放在合适的位置,又得用足够精度的传感器,才能作为回风温度补偿的参考。
第二,远程温度要能扛住现场环境。风管内的温度传感器离主控可能有三五米甚至更远,走线路径会经过强电电缆桥架、变频器附近。在这种环境下用模拟 NTC 或者 PT100,信号衰减和电磁干扰问题会比较头疼。PJ85718DM 这种数字温度传感器直接把模拟量转成数字信号,在传感器端就完成转换,传回的是 I2C 数字电平,抗干扰能力比模拟信号强不少。
第三,MCU 要有余量。HVAC 控制器不只干测温这一件事,还要跑风机调速、阀门开度控制、模式切换、通信协议栈。PIC32MZ1024EFF144 这颗芯片主频高、Flash 和 RAM 都够大,做这些事不会显得捉襟见肘。更重要的一点是,它内部集成了多个 I2C 外设,方便我们在不占用软件 I2C 模拟的前提下,同时规划本地和远程两路温度总线。
1.2 两颗关键器件各自承担什么角色
PJ85718DM 在方案里的职责是“精确感知”。它是一款数字输出温度传感器,测温范围覆盖 HVAC 场景的 -40°C 到 +125°C 绰绰有余,精度在典型范围内做到 ±0.5°C 级别已经没有压力。相比之下,普通 NTC 热敏电阻虽然便宜,但要经过分压、ADC 采集、查表、非线性矫正这一整套流程,每一环节都会引入误差。PJ85718DM 内部自带 ADC 和数字校准,读出来的数据直接就是温度编码,从源头省掉了模拟链路的误差。
PIC32MZ1024EFF144 在方案里的职责是“统一处理”。它负责通过 I2C 周期性地读取本地和远程两路传感器数据,进行滤波、校准,然后把温度值用于控制逻辑,同时输出到 HMI 或者上报给上位机。这颗芯片的外设资源里有专门的 I2C 控制器,配合 DMA 甚至可以做到后台自动采集数据。不过在实际项目里,我建议先用最简单的中断+状态机方式把链路跑通,再考虑优化,否则一上来就上复杂架构,出了问题不好定位。
2. 系统架构与测量链路设计
2.1 本地温度与远程温度的定义
这个项目里,本地温度指的是安装在控制板上的 PJ85718DM 所测到的温度,它代表控制器所在环境的温度,也可以理解为回风路径上的参考温度。远程温度则通过连接器引出,用另一颗 PJ85718DM 放置在风管内部或空调处理机组的送风段,通过两芯或者四芯线缆与主板相连。
这里要特别强调一下,“本地”和“远程”并不是简单的“板载”和“外接”之分,而是代表两种完全不同的工程场景。本地传感器在 PCB 上,走线短,信号稳定,我们只需要关注布局热干扰。远程传感器则要面对线缆压降、总线电容、外部干扰、防水防尘等一系列问题。后面会发现,这两个场景对硬件设计和固件策略的要求完全不同。
2.2 双路温度传感器的 I2C 拓扑
PJ85718DM 这类数字温度传感器通常支持多个可配置的 I2C 地址,依靠地址引脚的电平组合来区分。我们可以把两颗传感器挂在同一条 I2C 总线上,也可以分开挂在两条总线上。我的建议是:如果主控 I2C 外设数量足够,尽量分开走。
原因很简单,远程线缆一旦出现短路、断线或者受干扰,整条总线都会被拖死。如果本地传感器和远程传感器共用一条总线,远程故障会导致本地温度也读不到,整个控制器就会失去本地温度保护功能。我在样机里把本地传感器挂在 I2C1,远程传感器挂在 I2C2,这样即使远程那边出了幺蛾子,本地温度依然能稳定读取,系统至少能靠本地温度进入安全保护逻辑。
如果主控只剩一个 I2C 外设,那就必须靠软件处理总线故障恢复,比如每次通信设置超时,发现总线挂死就主动发送 9 个时钟脉冲复位总线,再重新初始化。这个方案能用,但会牺牲一部分实时性,后续排查也更费劲。
3. 硬件设计:传感器电路和长线传输细节
3.1 传感器外围电路设计
PJ85718DM 的外围电路很简单,核心就是电源去耦和 I2C 上拉。供电我用的是 3.3V,在传感器的电源引脚旁边放一个 100nF 陶瓷电容,再并联一个 10uF 钽电容。如果你手头的板子空间紧张,至少也要放一个 100nF,千万别省。
I2C 上拉电阻的选择需要认真算一下。标准 I2C 规范里,上拉电阻的最小值由灌电流决定,最大值由总线上升时间和总线电容决定。以 3.3V 供电、VOL 取 0.4V、灌电流 3mA 来算,上拉电阻下限约等于(3.3 - 0.4)/0.003,差不多是 966Ω。如果总线电容在 100pF 到 200pF 之间,要求上升时间不超过 1us,那么上拉电阻上限在 4.7kΩ 到 6kΩ 左右。所以本地传感器我直接用 4.7kΩ,远程那边根据线缆长度调整,后面会细说。
还要注意,PJ85718DM 的地址引脚不要悬空,必须明确接到 VDD 或者 GND。悬空的状态很容易受噪声影响跳变,导致 I2C 地址不稳定,现场偶尔读不到设备就是这种看似不起眼的原因造成的。
3.2 长距离 I2C 的工程处理
远程温度传感器最大问题就是线缆带来的总线电容和信号反射。普通的 I2C 在 400kHz 模式下,总线电容要求不超过 400pF,但一根三五米的屏蔽线,芯线之间的电容很容易就超过这个值,所以必须从几个方面处理。
第一步,把通信速率降下来。远程传感器这条总线我直接降到 100kHz,本地总线可以保持 400kHz。PIC32MZ1024EFF144 的 I2C 波特率由 BRG 寄存器控制,公式是 F_SCL = F_PBCLK / (2 * (BRG + 1))。假设外设时钟 100MHz,100kHz 时 BRG 大概取 499,400kHz 时 BRG 大概是 124。初始化时算清楚,不要凭感觉写数。
第二步,重新计算上拉电阻。远程总线加上线缆电容后,总电容可能到 500pF 甚至更高。如果还用 4.7kΩ,上升沿会变得很缓,超过时序要求就会引发通信偶发失败。我实际测试下来,5 米线缆时用 2.2kΩ 上拉、100kHz 速率,波形已经比较干净。如果你的线缆更长或者环境更恶劣,甚至要并接两个 2.2kΩ 进一步降低阻抗。
第三步,屏蔽层单端接地。远程传感器线缆我用的屏蔽双绞线,屏蔽层只在主控端单点接地,另一端悬空。这样既能把外部电磁干扰导入大地,又避免形成地环路。如果两端都接地,反而可能因为电位差在屏蔽层上产生电流,耦合进信号线。
3.3 HVAC 场景的特殊防护
HVAC 现场有一个特点:环境脏、有凝露、有变频器干扰。风管里的传感器虽然封装里做了防护,但连接器、线缆进线口这些位置还是要注意。
我在远程传感器的连接器端加了 TVS 管,用来吸收插拔静电和雷击浪涌。TVS 管选双向的还是单向的要看电路,这里传感器供电和信号线都是 3.3V 逻辑,用 5V 规格的双向 TVS 跨接在信号线与地之间,就能起到钳位作用。电源入口再串一个小阻值电阻或者磁珠,能进一步抑制高频干扰。
另外建议在 PCB 上把远程传感器的电源和信号线走成一组,并且和主控板上的强电区域保持足够距离。样机第一次布局时,远程线的 JST 连接器离继电器太近,继电器动作时温度数据偶发跳变,后来把连接器挪到板边,又增加了一小块铺铜隔离区,问题才消失。这种东西在原理图上看不出来,全靠布局经验。
4. 固件实现:从读寄存器到稳定数据流
4.1 I2C 驱动与寄存器读取
PIC32MZ1024EFF144 的 I2C 外设初始化和单片机惯例差不多,开启外设时钟、设置 BRG、配置主模式、使能总线。初始化之后,读取 PJ85718DM 温度数据的过程是标准的“写寄存器地址,再读数据”流程。
PJ85718DM 的寄存器布局我按常规数字温度传感器的结构来描述:温度数据寄存器放在偏移 0x00,高字节在前,低字节在后;配置寄存器放在偏移 0x01,用来设置分辨率和转换模式。需要提醒的是,不同批次或者同系列的器件,寄存器偏移可能会有细微差异,做驱动时务必以手头 datasheet 为准,不要想当然。
读取的流程是:主控发 I2C 起始信号,发送器件地址加写位,然后发送寄存器指针 0x00,接着重新发送起始信号,发送器件地址加读位,连续读取两个字节,最后发停止信号。代码写出来大概是这样的骨架:
uint8_t temp_read_raw(uint8_t dev_addr, uint16_t *raw) { uint8_t buf[2] = {0}; // 发送寄存器地址 i2c_start(); if (i2c_write(dev_addr | 0x00) != 0) goto fail; // 器件地址+写位 if (i2c_write(0x00) != 0) goto fail; // 寄存器指针 // 重复起始,读两个字节 i2c_restart(); if (i2c_write(dev_addr | 0x01) != 0) goto fail; // 器件地址+读位 buf[0] = i2c_read_ack(); buf[1] = i2c_read_nack(); i2c_stop(); *raw = ((uint16_t)buf[0] << 8) | buf[1]; return 0; fail: i2c_stop(); return 1; }每次读取都要设置超时保护,我用的方法是读一个标志位加计数,如果超过 10ms 总线还没回应,就直接返回失败,让上层走错误处理流程,而不是死等。
4.2 温度数据转换与实际处理
从温度寄存器读回原始值之后,要转成实际温度。PJ85718DM 在 12 位分辨率模式下,每个最小位的温度增量是 0.0625°C。也就是说,读回一个 16 位数据,低 4 位在 12 位模式下属于无效位,先把原始值右移 4 位,再乘以 0.0625,得到的就是带符号的温度值。
如果按整数处理,可以避免浮点运算的负担。比如把温度放大 16 倍保存,也就是直接取右移前的整数值,等到显示或者参与控制时再除以 16。PIC32MZ1024EFF144 带 FPU,用浮点运算也能接受,但嵌入式开发里养成定点数思维没有坏处,尤其是后面还要做 PID 温控算法时,定点数一致性和可复现性都更好。
实际转换时,需要注意符号位处理。温度低于 0°C 时原始值是一个负数,C 语言里直接用带符号 16 位整数转换即可。我自己踩过坑:把原始值当成无符号数右移再强转,负温度直接变成很大的正数,导致冬天测试时控制器死活认为环境有 200°C。
4.3 软件滤波与校准逻辑
数字传感器读回来的数据虽然比模拟链路干净,但也不是绝对稳定。HVAC 现场风管里的气流变化、压缩机的振动、开关电源的纹波,都可能在原始数据上叠加抖动。我做了两级处理:
第一级是中位值滤波,取最近的 5 次原始读数,排序后取中间值。这个处理能干掉偶发的毛刺,比如继电器打火引起的短暂干扰。
第二级是一阶低通滤波,公式很简单,当前值等于上一次值加固化系数乘上新样本与上一次值的差。系数我取 0.2 左右,相当于时间常数一百多毫秒。这样的数据用于温度显示很平滑,用于控制也够稳定。如果响应速度要求高,可以把系数调大,但要注意可能出现温度抖动。
校准方面,虽然 PJ85718DM 出厂已经校准过,但传感器的安装位置误差、线缆阻抗、主控板上的热源影响,都会让最终读数偏离真实值。我在样机里用两个温度点做了线性校准,公式是校准温度等于斜率乘原始温度加偏移量。两个校准点分别取 5°C 和 60°C,用精密温度计作为参考源,校准完成后把斜率和偏移存到 PIC32MZ1024EFF144 的内部 Flash 专用区域,下次开机直接读取使用。
5. 实测记录与数据分析
5.1 测试方法与场景
样机做好之后,我搭了一个简单的测试环境:本地传感器放在控制板角落,远程传感器放入一个恒温箱内,箱温从 -10°C 到 70°C 分段设定,用参考温度计记录实际温度。然后连续跑了两天,每分钟存一条数据,看两路传感器的稳定性和偏差。
需要说明的是,这套测试数据是基于我手里这块样机的实测记录,不同板子、不同批次器件会有差异,但趋势和结论有参考价值。尤其是远程线缆长度的对照测试,结论非常明确。
5.2 远程线缆长度对照结果
我分别用 0.5 米、3 米、5 米和 10 米的屏蔽双绞线做了四组测试,I2C 速率固定 100kHz,上拉电阻 2.2kΩ,结果如下表所示。
| 线缆长度 | 通信状态 | 温度读数稳定性 | 波形表现 |
|---|---|---|---|
| 0.5 米 | 稳定 | 无跳变 | 沿口清晰 |
| 3 米 | 稳定 | 无跳变 | 沿口轻微变缓 |
| 5 米 | 稳定 | 偶发跳变后滤除 | 上升沿可见圆弧 |
| 10 米 | 不稳定 | 频繁丢数据 | 上升沿明显畸变 |
10 米那一组单独测试时,通信成功率已经降到 90% 左右,而且 SDA 的上升沿对示波器看已经明显跟不上时序要求。这说明 PJ85718DM 本身没问题,瓶颈在于长线 I2C 的物理层。实际项目里如果确实要超过 5 米,我建议换方案,比如在远端加一个小 MCU 做数据采集,再用 RS485 传回主控,而不是硬撑 I2C。
5.3 温度精度与误差分析
本地和远程两路传感器在 0°C 到 60°C 区间内,和参考温度计的最大偏差都在 0.6°C 以内,这符合 PJ85718DM 这类器件的典型精度水平。远程传感器在风管气流环境中读数比静态环境低 0.2°C 左右,属于正常现象,因为风吹过传感器会带走热量。
值得注意的反而是本地传感器。一开始我把本地传感器放在主控芯片附近,结果读数比环境温度高了 2°C 多。后来我把板子重新布局,把本地传感器挪到 PCB 边角、远离电源芯片和继电器,中间用铺铜隔离热传导,误差降到 0.3°C 以内。这说明传感器精度再高,也架不住板上热源捣乱,布局对温度采集的影响有时候比传感器本身还大。
6. 常见问题与排查实录
6.1 快速排查表
实际调试过程中遇到的一些典型问题,我整理成了一张速查表,方便你对照排查。
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 读回数据全是 0xFF 或 0x00 | 器件地址错误、传感器未供电、总线被拉死 | 用万用表量供电,用逻辑分析仪看地址是否匹配 |
| 远程读数偶发跳变 | 线缆过长、上拉电阻偏大、干扰耦合 | 降速到 100kHz,换小上拉,检查屏蔽接地 |
| 本地温度偏高 | 传感器靠近发热源,热传导路径不良 | PCB 布局调整,远离电源和主控,加敷铜隔离 |
| 一块板子正常另一块不正常 | I2C 地址引脚虚焊或悬空 | 加焊并实测地址引脚到 VDD/GND 的连通性 |
| 总线死锁,程序卡在读取 | 从机未释放 SDA,通信异常未复位 | 加超时判断,异常时发 9 个时钟恢复总线 |
6.2 几个典型问题的深度复盘
第一个让我印象深刻的坑是总线死锁。远程传感器线缆在某次现场接线时被工人反插了一下,导致通信异常,程序直接卡在 I2C 读取函数里,看门狗也来不及复位,整机像是死机。后来我在所有 I2C 操作外面套了超时函数,一旦超时就强制释放总线,并重新初始化 I2C 外设。这里有个关键技巧:释放总线时要主动翻转 SCL 9 个周期,让卡住的从机完成内部复位,才能彻底恢复通信。
第二个问题是远程读数的毛刺。刚开始用 4.7kΩ 上拉跑 400kHz,5 米线缆下温度数据随机跳变,用示波器看波形上升沿又圆又慢。把速率降到 100kHz、上拉电阻改成 2.2kΩ 之后,毛刺频率明显下降,再配合软件中值滤波,数据曲线彻底稳定。这也让我明白了一个道理:软件滤波不是万能的,物理层先过关才是根本。
第三个是本地传感器的热干扰问题。这个在前面已经提过,但值得再强调一次:本地测温并不是把传感器放在板子上就行,必须结合整板功耗和发热分布来决定位置。我见过有人用同一颗传感器,只是从芯片附近移到板边,温度误差就从 2°C 降到 0.3°C。布局改一版,比写再多补偿代码都有效。
6.3 固件层面的稳定性补强
除了上面硬件上的措施,固件我也做了几点补强:第一,两路传感器独立读取,读取失败时各自保留上一次有效值,并累计错误计数,错误计数超过阈值才报警。这样远程传感器临时断线,本地温度控制还能继续工作。
第二,温度数据参与控制前,要经过“合理范围”判断,比如 -40°C 到 +125°C 之外的数据直接丢弃,不做控制依据。这是防止故障数据触发错误动作的一道底线防线。
第三,周期读取和主循环解耦。我用了一个 200ms 周期的定时器去触发 I2C 读任务,主循环只从消息队列里拿最新温度值。这样做的好处是,即使某次 I2C 通信因为干扰耗时较长,也不会阻塞主循环里的风机控制、阀门控制等实时任务。PIC32MZ1024EFF144 的主频跑这套逻辑非常轻松,CPU 占用率几乎可以忽略。
最后再分享一个经验:对 PJ85718DM 这类数字温度传感器,硬件上要把它当成一个“脆弱的外设”来设计,电源、上拉、ESD 保护一个都不能少;软件上要把它当成一个“会出错的源”来编程,超时、重试、滤波、范围校验都要有。两者配合,才能在 HVAC 这种干扰多、工况复杂的场景里拿到干净可信的温度数据。我在这套样机上踩过的坑,无非就是长线 I2C 物理层、板级热布局、软件容错这三件事,希望这篇记录能让你在设计阶段就避开它们。