做 HVAC 控制器这几年,我有一个很深的感觉:很多项目一开始画原理图时,大家都盯着板上那颗温度传感器,觉得“本地测温”接好线、读个寄存器就完事了。结果设备发到现场,远程点位要么通信丢包,要么在风道里测出来的温度比实际高好几度,最后慌慌张张回来改板。
今天想分享的这套组合——PJ85718DM 与 PIC18F97J94,就是我在一个嵌入式和 HVAC 温控项目里折腾下来的完整思路。它不算什么高端方案,但足够稳、足够便宜,而且能同时解决“本地测温”和“远程测温”两种需求。PJ85718DM 负责把温度变成数字量,PIC18F97J94 负责跑逻辑、通信和报警,整个架构非常清晰。如果你也在做空调箱、恒温恒湿机组、冷链箱体这类东西,这篇文章应该能帮你少走一些弯路。
1. 项目整体思路与硬件选型
1.1 这个项目到底要解决什么问题
先描述一下场景。我这边当时要做的是一套用于厂房空调箱的嵌入式温控模块,要求能采集至少两个温度点:一个装在控制板附近,用来监测机柜或回风温度,算“本地测温”;另一个传感器要装到风道深处或者远端冷却水盘管上,距离主板可能有十几米到几十米,算“远程测温”。实际上现场还提了第三个需求——远程温度要能实时传给上位机或者触摸屏,方便运维人员在大门口就知道整个空调系统的状态。
听起来功能不复杂,但真正落地的时候就会发现几个矛盾点:
- 如果本地和远程都用热敏电阻 NTC,每一路都要单独做调理电路、校准和线性化,精度还受电源电压波动影响。
- 如果用 PT100 铂电阻,精度倒是高,但调理电路更复杂,远程布线还要做三线制或四线制,成本一下子就上去了。
- 远程传输如果走模拟信号,长线缆上的压降和干扰会直接叠加到温度数据里,这在 HVAC 现场几乎是没法避免的。
所以最后我选择了 PJ85718DM 作为温度传感器。这颗芯片本质上是把测温元件、ADC、数字接口集成在一起的方案,直接输出数字量,省掉了一堆模拟调理电路。PIC18F97J94 则作为主控 MCU,负责读取传感器数据、完成滤波和越限判断,并通过串口/RS-485 把远程温度上报给上位机。
整个系统的核心架构可以概括成一句话:本地用数字传感器直连 MCU,远程用数字传感器加长距离总线通信。这样既保证了测温精度,又解决了远距离传输的干扰问题。
1.2 为什么选 PIC18F97J94 这颗 MCU
可能有人会问,做温度采集为什么不直接用 STM32 或者别的 Cortex-M 内核芯片?我的答案很简单:在这个项目里,8 位机完全够用,而 PIC18F97J94 的外设丰富程度和抗干扰能力在同价位里非常有优势。
先看外设。PIC18F97J94 属于大引脚封装的高端 8 位 MCU,片上有多个 UART、I2C、SPI 和 ADC 模块。我们这个项目需要一路 I2C 去读本地温度传感器,还需要至少一路 UART 转成 RS-485 去轮询远程节点,另外还要留几路 GPIO 去控制继电器输出。这颗 MCU 的引脚资源非常充裕,不需要像小封装单片机那样为了复用引脚而绞尽脑汁。
再看电气特性。HVAC 控制器往往工作在 24V AC 或者 24V DC 的工业环境里,电源纹波大、继电器动作频繁,电磁环境并不友好。PIC18F97J94 的 IO 口支持 5V 电平,抗干扰能力比 3.3V 系统要皮实一些。而且它的内部振荡器校准精度足够跑 UART 通信,虽然我最后还是外接了晶振,但至少多了一个备用方案。
最后是成本。8 位 MCU 在嵌入式温控这类对算力要求不高的场景里,成本优势非常明显。温度采集和通信协议栈本身就不需要跑 RTOS,用裸机状态机就足够了。
1.3 PJ85718DM 这颗传感器的特点
PJ85718DM 我拿到的是 I2C 接口的版本,后缀 DM 在选型时一定要确认清楚,同系列可能存在不同接口规格。它的典型特性包括:
- 测温范围覆盖 -55℃ 到 +125℃,HVAC 场景完全够用。
- 默认 12 位分辨率,对应温度分辨率是 0.0625℃。
- 典型精度能做到 ±0.5℃ 左右,这比普通 NTC 方案要稳得多。
- 芯片自带 I2C 接口,地址引脚可以设置,一条 I2C 总线上至少能挂好几个。
我实际测试下来,PJ85718DM 的线性度很好,长时间运行漂移也不明显。最关键的是,因为它是数字输出,不需要像 NTC 那样在固件里做查表或 Steinhart-Hart 方程计算,直接把寄存器值换算成温度就行。
2. 硬件设计与接线要点
2.1 本地测温:I2C 直连 PIC 的设计
本地温度传感器就放在主控板上,离 MCU 很近,所以我直接把它挂在 PIC18F97J94 的 I2C 总线上。接线非常简单:
- VCC 接 3.3V 或 5V,具体看传感器数据手册。
- GND 接系统地。
- SDA 和 SCL 分别接 MCU 的对应引脚。
- SDA 和 SCL 各接一个 4.7kΩ 或 10kΩ 的上拉电阻到电源。
- 在 VCC 和 GND 之间放一个 0.1μF 去耦电容,尽量靠近传感器引脚。
这里有一个很多人容易忽略的点:I2C 上拉电阻的值不是随便选的。上拉电阻太小,总线灌电流太大;上拉电阻太大,信号上升沿太慢,高速通信时会出错。4.7kΩ 在 100kHz 标准模式下一般没问题,但如果你为了省时间把 I2C 速率调到 400kHz,建议按公式估算一下总线上拉能力,必要时改成 2.2kΩ。对于几厘米的板内走线,4.7kΩ 通常是最稳妥的起点。
我在原理图上还给 SDA 和 SCL 加了串联电阻,比如 33Ω 到 100Ω。这个电阻能抑制振铃,尤其是传感器离 MCU 比较远或者走线经过了排针的时候,效果很明显。别小看这两个小电阻,它能帮你挡掉很多现场干扰问题。
2.2 远程测温:RS-485 总线方案
远程温度传感器和主板之间的距离不再是“厘米级”,而是“米级”甚至“十米级”。这时候如果仍然用 I2C 直接拉长线,总线电容会超标,通信不稳定是必然的。所以我在远程温度采集通道上走了 RS-485 总线。
具体的拓扑结构是这样:主板上的 PIC18F97J94 通过 UART 连接一个 RS-485 收发器,然后从端子引出两根双绞线到远处的传感器节点。传感器节点那边可以放一个小采集板,板上也有一颗 PJ85718DM,加上一颗 RS-485 收发器和一颗带 UART 的单片机做协议转换。
可能你会问,为什么远程节点也要放单片机?因为 PJ85718DM 是 I2C 接口,RS-485 是串行差分接口,两者不能直接互连。用一个便宜的 8 位单片机做桥接,把 I2C 温度读出来,再打包成 Modbus RTU 或自定义帧,通过 RS-485 发回主机。这样做的好处是,远程节点的硬件逻辑可以做得非常简单,就是一个“温度变送器 + 总线从机”。
RS-485 接线有几个硬性要求:
- 必须使用屏蔽双绞线,屏蔽层单端接地,不要两端都接地,否则会形成地环路。
- 总线两端要接 120Ω 终端电阻。如果只有一个远程节点,就在最远端那端接;如果以后还要扩展多个节点,记得在总线两端都接。
- 远程节点的电源最好和主板隔离,或者至少共地要可靠。HVAC 现场经常有强弱电混走的情况,地线压差会导致通信异常,严重的会烧收发器。
我在实际项目里给远程传感器板做了 DC-DC 隔离电源,同时也把 RS-485 收发器做了隔离。虽然成本增加了十几块钱,但换来了现场的省心。要知道,HVAC 设备维修一次可能就要停机几小时,那损失可比十几块钱大得多。
2.3 电源、防护与 PCB 布局细节
HVAC 设备里的电源环境是非常恶劣的,继电器通断瞬间会产生几十伏甚至上百伏的尖峰。所以我给主控板和远程板的电源入口都加了以下防护:
- 自恢复保险丝,防止过流烧板。
- TVS 管,钳位浪涌电压。
- 共模电感加电容滤波,抑制高频干扰。
- 每个 MCU 的 VDD 引脚旁边都放 0.1μF 和 10μF 电容,组成去耦组合。
这里我特别强调一下 PCB 布局。PJ85718DM 作为温度传感器,它对自身发热特别敏感。如果它旁边就是 LDO 或者功率电阻,测出来的温度会明显偏高。我之前吃过亏,把传感器放在一个发热量很大的稳压器旁边,结果温度读数凭空高了 2℃。所以在布局阶段就要注意:
- 传感器尽量靠近测温目标,比如进风口或采样点。
- 传感器和发热器件之间留出足够距离,或者用铺铜开槽做热隔离。
- 如果要测的是空气温度,传感器上方不要覆盖大面积的完整铜皮,否则铜皮本身会成为一个散热器,影响响应速度。
3. 固件实现:从 I2C 驱动到温度计算
3.1 I2C 读取驱动的基本框架
固件部分,我先说读取 PJ85718DM 温度的核心代码。I2C 读温度寄存器其实就两个步骤:先写指针到温度寄存器地址,再连续读两个字节。下面是我在项目里用的简化版驱动,基于 PIC 的 MSSP 模块操作。
#define TEMP_SENSOR_ADDR 0x48 // 根据地址引脚设置,可能是 0x48~0x4F uint8_t temp_raw_high = 0; uint8_t temp_raw_low = 0; void i2c1_write_byte(uint8_t addr, uint8_t reg) { // 略:标准 I2C 起始、发送设备地址+写位、发送寄存器地址、结束 } void i2c1_read_temp_raw(uint8_t addr) { i2c1_write_byte(addr, 0x00); // 温度寄存器地址 // 略:标准 I2C 起始、发送设备地址+读位、读高字节、读低字节、结束 }如果你用的是硬件 I2C 外设,读操作时要注意 ACK 时序。最后一个字节要发送 NACK,然后产生停止条件。很多新手在这个地方会卡一下,导致读出来的数据始终是 0xFF 或者最后一组数据重复。用逻辑分析仪一抓波形就能看出来,应答位不对。
如果你的 MCU 没有硬件 I2C,也可以用 GPIO 模拟 I2C。但我要提醒一句:模拟 I2C 在系统调度复杂、中断频繁的项目里很容易产生时序抖动,所以能上硬件外设就尽量用硬件外设。PIC18F97J94 自带 MSSP 模块,不用白不用。
3.2 温度换算:从寄存器到真实温度
PJ85718DM 的 12 位温度数据以二进制补码形式存储。换算公式不复杂:
温度值 = 原始值 * 0.0625℃举个例子:原始值是 0x01A0,也就是十进制的 416,那么温度就是 416 × 0.0625 = 26.0℃。如果原始值是负值,比如 0xFF38,先取补码得到 -200,再乘以 0.0625 就是 -12.5℃。
在嵌入式代码里,我不建议直接用浮点数。PIC18F97J94 虽然是 8 位 MCU 里的高性能型号,但浮点运算还是会占用大量时间和 ROM。更推荐的做法是使用定点数:
int16_t temp_raw; int16_t temp_x100; // 温度乘以 100,用来保留两位小数 temp_x100 = temp_raw * 625 / 10; // 如果 temp_raw 是 416,则 temp_x100 = 416 * 62.5 = 26000,表示 260.00℃ 除以 100这样,温度值都以整数形式传输和显示,上位机只需要自己在小数点上做处理。精度方面,625/10 这种写法会比直接乘 625 再除 100 更减少中间溢出风险,不过还是要看编译器对整型的处理,必要时用 long 类型。
3.3 滤波算法与越限报警处理
HVAC 现场的温度信号本身就不是一个纯净的恒定量,风机启停、门开关都会让温度产生快速波动。如果直接用原始值去做控制判断,继电器会频繁吸合,缩短寿命不说,控制效果也会很差。所以我在温度进入控制逻辑之前加了两层处理。
第一层是滑动平均滤波。我用了 8 个采样点做环形缓冲区,每次读取新值后计算平均值。对于 1Hz 左右的采样频率,8 次平均大概对应 8 秒的平滑时间,用来做空调控制是比较合适的。如果采样频率更高,比如 10Hz,那么窗口可以适当加长。
第二层是一阶低通滤波,也叫惯性滤波。它的数字实现非常简单:
filtered = filtered + (new_value - filtered) / N;N 越大,滤波越平缓,但响应也越慢。我一般取 N=16,用来处理短时毛刺,同时保持一定的动态响应能力。
越限报警这块有一个小技巧:一定要加滞回比较。比如设定高温报警值是 40℃,那么报警解除温度应该设在 38℃ 或 37℃,而不是也设在 40℃。否则温度在 40℃ 附近波动时,报警会反复触发,现场运维人员会疯掉的。
#define ALARM_HIGH 4000 // 40.00℃ #define ALARM_OFF 3800 // 38.00℃,低于这个值才解除报警 if (temp_x100 >= ALARM_HIGH && !alarm_state) { alarm_state = 1; output_alarm_on(); } if (alarm_state && temp_x100 <= ALARM_OFF) { alarm_state = 0; output_alarm_off(); }这个“先触发后解除”的写法,是所有现场告警逻辑里最基本的经验。别图省事只设一个阈值,后面调试的时候你会后悔的。
4. HVAC 场景的远程温度采集策略
4.1 远程测点怎么布置才准
远程温度传感器在 HVAC 系统里最常见的位置有三个:送风管、回风管和盘管后。不同位置对传感器的安装要求是不同的。
装风管里时,传感器探头要伸到气流中心,不能只贴在管壁外测。管壁温度受环境温度影响很大,测出来的值根本不是风温。我自己习惯在风管侧面开孔,把探头插入管径的三分之一到二分之一深度,然后用密封胶固定。注意探头周围不要被保温棉包裹得太严,否则热响应会变得很慢。
装回风口时,传感器要避开直吹的冷风或者热风射流,尽量装在混合区域。回风温度代表整个空间的平均温度,如果装在空调送风口正下方,它测到的是送风温度,控制逻辑就会误判。
装盘管温度时,传感器要贴在盘管表面,但要做好导热和隔热。导热硅脂不能省,而且探头外部需要用保温材料包起来,避免被环境温度干扰。这种情况下测的是“盘管表面温度”,不是空气温度,控制逻辑里要另外做补偿。
4.2 主机轮询逻辑与协议帧设计
多个远程温度节点挂在同一根 RS-485 总线上时,主机需要按地址轮询。我这里的做法是自定义了一个极简帧格式,兼容 Modbus RTU 的功能码 03,方便以后接触摸屏或上位机。
一个典型的请求帧是这样:
- 地址:1 字节,表示远程节点地址。
- 功能码:1 字节,固定为 0x03。
- 起始地址:2 字节,这里是温度寄存器地址。
- 读取数量:2 字节,固定为 1。
- CRC:2 字节,CRC16/MODBUS。
远程节点接收到请求后,返回的数据帧包括地址、功能码、字节数、温度数据高字节、温度数据低字节、CRC。主机解析时要先校验 CRC,再判断设备地址,最后才取温度值。顺序不能乱,因为总线上有干扰时,数据大概率是错的,但 CRC 能帮你把这些错误帧筛掉。
轮询间隔我设的是 1 秒。这个频率对 HVAC 控制足够,也避免总线过于繁忙。如果总线上的节点数量多到 30 个以上,1 秒轮询就会变得紧张,那时候可以考虑把轮询间隔缩短到 500ms,或者使用事件触发上报。不过对大多数项目来说,轮询是更稳健的方式。
4.3 断线识别与系统自恢复
远程传感器环境复杂,出现断线是大概率事件。所以固件里必须做好断线识别。我的实现思路是:
- 连续 3 次轮询没有收到远程节点的响应,就认为该节点离线。
- 离线后,温度数据保持上一次的有效值,并在状态寄存器里置一个离线标志。
- 如果连续离线超过一定时间,比如 10 个周期,就上报故障,并把该节点的参与值从 PID 控制逻辑中摘除。
- 一旦节点恢复响应,自动清除离线标志,重新参与控制。
这个逻辑看起来简单,但真正容易踩坑的是没有加看门狗。远程通信如果偶尔卡死在某个 UART 接收状态里,没有看门狗的 MCU 可能会一直停在原地,导致整个系统“假死”。所以我在主循环里喂狗,同时给看门狗设了一个较长的超时时间,比如 2 秒。这样不管通信层出了什么问题,MCU 都能在 2 秒内复位,重启后又主动去恢复远程连接。
这里我再补充一句,设备的“自恢复能力”在 HVAC 这种 7×24 小时运行的场景里非常重要。宁可系统重启一次,也不能让它带着错误数据一直运行,否则现场的温控就会失控。
5. 常见问题与排查技巧实录
5.1 温度读数整体偏高,越调越不准
这个问题我遇到过不止一次。现象是读数比水银温度计或红外枪高 1~2℃,而且很稳定。排查到最后,基本都指向同一个原因:传感器自热。
PJ85718DM 的工作电流虽然不大,但如果不注意散热环境,芯片内部的温升是实打实的。特别是当传感器被封装在一个很小的塑料壳里,而且周围没有空气流动时,自热会导致测量结果系统性偏高。
解决办法有几个:
- 降低采样频率,减少传感器连续工作的时间,比如每次测完可以进入低功耗模式。
- 检查传感器周围有没有热源,包括 MCU 本身、电源芯片、大电流走线。
- 尽量让传感器探头部分悬空,增加空气接触面积。
我之前在一台设备上把采样间隔从 250ms 改到 1s,温度读数下降了 0.3℃ 左右。所以如果你发现读数“平白无故”偏高,先别怀疑传感器坏了,先算算自热。
5.2 RS-485 远程通信时好时坏
通信时好时坏是 RS-485 最常见的故障,解决问题前必须先判断是硬件问题还是软件问题。
我的排查顺序是:
- 先用示波器或逻辑分析仪看总线上的波形,确认有正常的差分信号。
- 在远程端和主机端同时测量 A、B 线对地电压,正常空闲状态应该是 A 高 B 低。
- 检查是否有共地。如果两个设备的 GND 不连,而电源又来自不同开关电源,地电位差很容易超过收发器的共模范围。
- 检查终端电阻。距离超过几十米但没有终端电阻,总线末端信号反射会非常严重。
遇到一台现场设备,我折腾了很久,最后发现是两个节点之间的 GND 电压差有 3V 多。加了隔离收发器之后,问题立刻消失。所以做 RS-485 设计,隔离不是可选项,而是建议直接默认加上。
5.3 I2C 读数偶尔出现 0xFFFF 之类的异常值
I2C 通信偶尔读到异常值,原因可能是总线毛刺导致数据位被错读。处理思路有两个方向:硬件上加强滤波和软件上做数据校验。
硬件方面,除了前面说的串联电阻和上拉电阻,还可以在传感器附近加一个 100pF 左右的滤波电容到 GND,吸收高频噪声。软件方面,读取温度后,我加了简单的合理性判断:如果温度值超过传感器量程或者变化量超过每周期 5℃,就认为本次读取无效,用上一次的值代替。
还有一种情况是 I2C 读操作缺少 NACK。很多 MCU 的硬件 I2C 外设在最后一个数据字节读取时,需要主机主动发出 NACK 信号。如果你用的是通用 I2C 库,确认库函数是否已经处理了这一步。
5.4 系统反复复位,温度时不时跳变
反复复位最可能触发的是看门狗超时,但真正的触发原因在哪里,需要仔细查。我之前遇到过一次,是主程序在等待 RS-485 发送完成时,因为 UART 中断优先级配置不当,导致主循环长时间没执行喂狗语句,所以看门狗被饿死了。
解决方法是把喂狗放到定时中断里,或者在高优先级任务完成后及时喂狗。还有一个额外经验:不要在 UART 发送中断里做耗时的算法处理,否则系统时间片会被拖垮。大数据处理应该放到主循环,中断只做数据搬运。
另外,温度跳变还可能来自电源瞬间跌落。继电器吸合瞬间,如果电容储能不足,MCU 供电电压会瞬间低于复位阈值,于是整个系统重启。这种问题在原理图阶段就要重视,给 MCU 单独加一个足够容量的储能电容,比如 100μF 以上,可以有效防止继电器动作引起的掉电复位。
5.5 实用排查速查表
| 现象 | 可能原因 | 优先排查手段 |
|---|---|---|
| 温度读数偏高且稳定 | 传感器自热、靠近发热源 | 降低采样频率、检查布局 |
| 读数随机跳变 | I2C 干扰、电压跌落 | 加滤波电容、检查电源 |
| 远程通信时好时坏 | 缺终端电阻、地电位差大 | 量 A/B 线电压、加隔离 |
| 偶尔读到 0xFFFF | 总线毛刺、ACK 时序错误 | 加串联电阻、检查代码时序 |
| 系统反复复位 | 看门狗溢出、电源跌落 | 检查喂狗位置、加大储能电容 |
这个表是我项目调试时候的速查清单,每次现场问题都能从这里找到方向,节省了不少时间。
最后一点实在话
如果你正在跟别人合作做一个嵌入式或者 HVAC 相关的温度采集项目,我建议别把注意力全部放在某个传感器或者某颗 MCU 上,先花时间想清楚“温度测出来之后要给谁用、怎么用”。在 HVAC 里,温度只是控制的输入之一,它还要和湿度、压力、阀门开度一起参与决策。PJ85718DM 和 PIC18F97J94 这套组合,价值在于把“可靠获知温度”这件事变得低成本且稳定,但真正让系统跑得顺的,还是周边电源设计、通信协议和故障处理。
我在实际做项目中最受益的一个习惯,就是每一版硬件回来之后,先花半天时间手动测试传感器极限情况,比如短接 SDA、拔掉远程总线、把供电拉到临界值,看系统会不会崩、会不会死锁。把这些异常路径都摸透,远比把代码写得很花哨更重要。希望这些经验对你也有用。