做 HVAC 控制器调试的那段日子,我最大的体会就是“温度不是一个点,而是一张网”。一个标准的风冷热泵机组,少则三五个测温点,多则十几个:压缩机排气温度、回气温度、冷凝器出口水温、蒸发器进出水温、机房环境温度……全都得进控制器。如果只是把传感器堆到一块板子上,那倒简单,难的是分布式布置——有的探头就在主控旁边,几十厘米线缆;有的装在风道深处或者多联机外机侧,一走就是二三十米。
我这次要聊的项目,就是用 PJ85718DM 和 PIC18F55K42 这套组合,把“本地温度”和“远程温度”统一收进同一个轮询框架里。PJ85718DM 是一颗带 I2C 接口的数字温度传感器,PIC18F55K42 则是某半导体厂商 K42 系列里的一颗 8 位主控,带 12 位 ADC 和硬件可配置逻辑,用来做多点温度采集非常顺手。这套方案在嵌入式和 HVAC 领域都有代表性,适合正在搭集中式环境监控板、冷链设备控制器或者机组控制器的朋友参考。
先交代一句:这篇文章里的步骤和参数,大部分来自我实际做过的类似工程,小部分属于同类数字温度传感器的通用做法。具体到 PJ85718DM 的寄存器地址和精度等级,还是要以你手上那版数据手册为准。
1. 先把需求讲清楚:为什么“本地温度”和“远程温度”必须分开设计
1.1 HVAC 温控系统的测温分布特点
HVAC 系统的测温点,按物理位置可以分为两类。一类是“近机侧”的点位:主板周围的环境温度、电源模块附近的温升、控制器壳体内壁的温度,这些点离 MCU 很近,线缆长度通常不超过 30 厘米,信号质量几乎不用操心。另一类是“远置”的点位:空调箱的送风温度、回风温度、冷冻水管进出口温度、冷库的库温,这些传感器往往装在金属风管、水管保温层里,或者干脆挂在房间的墙壁上,离主控板少则几米,多则几十米。
这两种点位如果简单粗暴地全部用同一套接线方式,一定会出问题。本地点位可以随便用 I2C 直连,只要规范布线,数据怎么读都稳定;远程点位就不一样了,I2C 总线对线缆电容非常敏感,线一长就容易出现时序失真、数据错位,而且 HVAC 现场从来不是安静的电磁环境——风机启动、变频器动作、压缩机吸合,电源线里的毛刺和空间辐射随时可以干扰一根裸露的总线。
所以这个项目的起点不是“选什么芯片”,而是先确认一点:本地点位和远程点位在电气设计上必须做差异化处理。我把这一点想清楚之后,后面的电路和代码才没有反复返工。
1.2 本地与远程在电气设计上的本质区别
先说本地。本地传感器和 MCU 靠近,I2C 走线只有十几厘米,这时候最需要注意的是传感器的供电干净不干净、地址线拉没拉到位、上拉电阻阻值是否匹配,以及传感器附近有没有大功率发热器件造成自热。这些都属于“常规排布问题”,不需要额外的总线驱动器件。
远程则完全是另一套逻辑。I2C 标准模式下,总线电容一般建议控制在 400pF 以内,实际工程里我习惯按 150pF 留余量。普通屏蔽线的分布电容大约每米 50~100pF,算一下就知道,超过三五米之后,信号边沿就开始变“馒头”了。如果还按标准 4.7kΩ 上拉电阻工作,传输的“1”会被线缆电容拉得很慢,主设备很容易误判电平。
所以远程测温我必须做两件事之一:要么加总线缓冲器把 I2C 分段驱动,把每段线缆电容控制在规范以内;要么降低速率,把时钟压到 30kHz 甚至更低,并且精调上拉电阻。第一个方案可以跑得更远更稳,第二个方案简单但治标不治本。
1.3 项目整体架构一句话总结
整个项目一句话可以概括为:PIC18F55K42 作为 I2C 主设备,板上短距离直接挂 PJ85718DM 测本地温度,板外通过带缓冲的 I2C 长线挂若干颗 PJ85718DM 测远程温度,MCU 轮询完所有点位后,把温度经过滤波、限幅、标定偏移处理后,用于显示、通信和过温保护逻辑。
这个架构最大的好处是统一。所有传感器型号一样,寄存器操作一样,固件里一套读取函数覆盖全部点位。不用像某些方案那样,本地用数字传感器、远程用 433M 无线或者 4-20mA 变送器,维护时恨不得带三套工具。后面我会按这个架构把硬件、软件、调试逐段展开。
2. 器件选型的底层逻辑:PJ85718DM 与 PIC18F55K42 为什么配
2.1 PJ85718DM:一颗 I2C 数字温度传感器的典型姿态
PJ85718DM 这类数字温度传感器,外形通常就是一颗 SOT-23-5 或者 SOT-23-6 的小封装,引脚就四五个:电源、地、SDA、SCL,外加一两个地址选择引脚。它的内部核心是半导体 PN 结测温加一个模数转换器,温度变化会引起 PN 结正向压降线性变化,ADC 把压降转换成数字量,再通过 I2C 接口输出。
以我经手过的同类数字传感器为参考,典型参数大致是这样:测温范围 -55℃ 到 +125℃,精度 ±0.5℃(在 -20℃ 到 +70℃ 区间可以做到更小),分辨率 0.0625℃ 也就是 12 位,平均工作电流在微安到几百微安级别,供电 2.7V 到 5.5V,I2C 速率支持标准模式 100k 和快速模式 400k。有些型号还带一个过热输出引脚,温度超过设定阈值后可以直接拉低报警,不需要主控参与。
对 HVAC 项目来说,0.0625℃ 的分辨率完全够用,甚至有点浪费。风机盘管、水系统的温度控制在 0.1℃ 级别就已经很精细了,真正影响控制效果的从来不是分辨率,而是传感器的安装方式和标定一致性。不过分辨率高有个附带好处:软件滤波之后,显示值可以很平滑,不会因为最低位跳动让用户觉得温度在乱晃。
2.2 PIC18F55K42:K42 系列里适合测温器的边角料
PIC18F55K42 这颗 8 位 MCU,第一眼看上去规格不算华丽,但在这个项目里非常对路。K42 系列最大的特点是片上的 12 位 ADC 带计算核心,硬件可以自动做多次采样平均、阈值比较甚至数字滤波,CPU 只需要在转换完成后读结果。温度传感器本身就是数字的,这条特性暂时用不到 ADC 上,但 MCU 的另一个能力——可配置逻辑单元(CLC)——在过温保护电路里很值钱。
具体到这个项目,我看中 PIC18F55K42 的几个点:第一是它有多路 I2C/SPI 外设,作为总线主设备挂多个从设备很从容;第二是它工作在 2.7V 到 5V 范围内,直接兼容 PJ85718DM 的宽供电特性,不用做电平转换;第三是它的 I/O 带电流驱动能力尚可,驱动板载 LED 指示或者通过三极管驱动继电器都够用;第四是它的中断系统响应快,I2C 事件中断可以配合状态机,不让主循环卡在总线等待上。
还有一个隐藏的优势:K42 系列在业内比较成熟,编译器和调试器支持完善,代码库资料多,工程上手成本低。做项目最难的不是芯片不够强,而是资料太少、坑要自己踩一圈才摸清,选一颗生态成熟的 MCU 能省下不少时间。
2.3 这套选型解决了哪些实际问题
把这两颗芯片放到一起,解决的实际问题有三个。
第一个是“单总线协议生态的局限”。很多人做远程测温第一反应是单总线传感器,因为单总线只需要一根数据线,布线确实省。但单总线时序敏感,时序要求严苛,软件模拟容易踩坑,而且同一组设备不多时不需要唯一的器件序号,现场更换传感器后固件里还要处理新序列号。PJ85718DM 走 I2C,每个传感器靠硬件地址区分,同一颗 MCU 的理论接入数量就是 I2C 地址位数的上限,管理起来直观得多。
第二个是“模拟量远程传输的抗干扰成本”。如果从零开始搭模拟温度变送器方案,几百毫伏级别的信号拉到几十米外,要么上屏蔽+差分,要么上 4-20mA 环路,成本都明显更高。数字传感器把电压转成数字量,在源头就完成了抗干扰的第一步,后面只需保证总线电平能被可靠识别。
第三个是“固件统一性”。一套读取函数通吃本地和远程所有点位,校准偏移和传感器 ID 按节点号记在一个表格里,后续换传感器、加点位都不用改动主逻辑。这个收益在做完第一版固件之后特别明显。
3. 硬件上怎么搭:本地点位的直连与远程点位的总线延伸
3.1 本地测温的最小电路:上拉、去耦、地址
本地部分是最小系统。PJ85718DM 的 VCC 接 3.3V 或者 5V,对地放一颗 100nF 陶瓷电容做去耦,位置一定要尽量贴近芯片引脚。SDA、SCL 各接一颗上拉电阻到 VCC,阻值我习惯在 4.7kΩ 到 10kΩ 之间选,短距离下两者差别不大,选 4.7kΩ 可以获得更陡的上升沿。
地址引脚是 I2C 通信里最容易忽略的地方。PJ85718DM 如果有 A0/A1 甚至 A2 引脚,本地板上这一颗可以直接接地固定下来,逻辑地址比如 0x48(七位地址),这样远程传感器即使不加电或者短路,也不影响总线上的地址分配。如果 MCU 附近要放两颗本地传感器,就通过地址引脚组合区分,注意 PCB 上给地址引脚并一个下拉电阻,防止焊盘虚焊时引脚悬空误接地。
电源方面还要考虑一个问题:HVAC 控制器板上往往有继电器。继电器吸合瞬间电流冲击很大,可能会把 VCC 拉低一个明显的尖峰,让传感器瞬间复位或者读数异常。我的处理是在 PJ85718DM 的供电入口加一颗 10Ω 电阻和 10uF 电容组成一个小型 RC 滤波,实测继电器动作时读数据不再丢包。
3.2 远程测温的 I2C 总线延伸:线缆、缓冲、拓扑
远程部分是这个项目的重头戏。先说结论:不加任何缓冲器件、直接拉 10 米 I2C 线,在 HVAC 现场大概率会翻车,至少我不推荐这么干。
典型翻车现场是这样的:传感器装在风管里,接线用四芯屏蔽线,长度大概 8 米,主控端上拉 4.7kΩ,标准 100k I2C 速率。运行起来 80% 的时间读数正常,偶尔读到 0xFFFF 或者连续几个字节 CRC 不对。用示波器看 SDA 线上的波形,低电平没问题,高电平上升沿慢得像充电曲线,到了靠近阈值区域的时间点已经超过主设备采样窗口,于是误读。
我会建议的做法是分成两段。主控到现场接线盒之间用“驱动端+缓冲器”的方式,I2C 缓冲器把总线电容隔离成两段,每段都不超过 150pF 的容性负载。以每米 70pF 估算,一段 1.5 米内的板内总线轻松满足;另一段 5 米线缆加远端节点,也刚好在余量内。
具体拓扑可以这样做:主控板上的 I2C 总线先接一颗缓冲器,缓冲器输出接到长线连接器。长线末端再接一颗缓冲器,作为“远端分支点”,然后从分支点分出最长 1~2 米的小段总线,挂若干颗远程传感器。两个缓冲器之间的长线虽然也贡献电容,但缓冲器会重新驱动信号,相当于每段总线电容都被“重置”了。这样设计后,总距离可以做到 20 米以上,远超过裸总线的能力。
如果实在简化到不加缓冲器,那就必须降速。我曾经把 I2C 时钟降到 20kHz,上拉改到 2.2kΩ,线长 3 米内勉强稳定。但 20kHz 会让远程点位一多、轮询周期明显变长,因此只能作为临场应急手段,不建议做进正式产品。
3.3 供电与抗干扰设计中的几个细节
远程节点供电,最稳妥的做法是 24V DC 电源传到远端节点,在传感器附近用稳压器降到 3.3V 或 5V,再给 PJ85718DM 供电。尽量不要用长线直接拉 MCU 的 5V 到远端,否则线压降和现场的电源噪声都会直接影响传感器测量。
屏蔽线该怎么接也有讲究。屏蔽层必须在主控端单点接地,远端悬空或者通过 1nF 电容接地,以提供射频泄放路径同时又不会形成地环路。地环路一旦形成,屏蔽层里就有工频电流跑,传感器读数会出现周期性的小幅度漂移,排查起来非常费劲。
线缆选型上,我用的是 4 芯 0.3mm² 或者 0.5mm² 的铜网屏蔽线,两根走 SDA/SCL,一根走电源,一根走地。把电源和地尽量隔在信号线旁边,利用地线做一定的隔离。有条件的话,SDA 和 SCL 之间保持绞合更好,即使普通平行线芯,只要屏蔽层可靠,工程上也能用。
4. 软件部分:从 I2C 驱动到温度值上屏
4.1 I2C 主模式的初始化与轮询框架
软件的第一步是初始化 MCU 的 I2C 主模式。PIC18F55K42 的 MSSP/I2C 模块支持主模式、从模式和多主模式,这里用主模式就够了。初始化时重点设置三件事:时钟速率、波特率发生器预分频、以及总线空闲检测。
时钟速率的计算可以直接套公式。举个例子,若主时钟为 16MHz、目标 I2C 速率为 100k,则波特率寄存器值约等于 16MHz / (4 × 100k) = 40,再配合相应的预分频配置即可。远程带缓冲的情况下,我通常把速率定在 100k,稳定性和速度平衡比较好;如果某天发现现场干扰强,我会全局降成 50k,只改一个宏定义的地方就行。
轮询框架我习惯用一个简单的状态机:
typedef enum { IDLE, SEND_ADDR_W, SEND_REG, REPEAT_START, SEND_ADDR_R, READ_DATA, STOP } i2c_state_t;主循环每隔一固定周期调用一次状态机,状态机不阻塞在总线上,这样 MCU 还能同时处理键盘、显示和继电器逻辑。如果数据还没准备好,就保持当前状态,空闲时间让给别的任务。
4.2 寄存器读取与温度数据换算
读取 PJ85718DM 的温度值,流程一般是:先发器件地址加写位,再发温度寄存器地址,然后发重复起始信号,接着发器件地址加读位,最后连续读两个字节(温度数据通常占两个字节)。以 12 位分辨率、0.0625℃ 为 LSB 的常见格式为例,读回来的 16 位数据中,前 12 位是有效温度数据,后 4 位是低半字节,通常清零或保留。
换算公式很直接:
int16_t raw = ((uint16_t)buf[0] << 8) | buf[1]; // 12 位数据右移 4 位后,乘以 LSB 0.0625 就是摄氏度 float tempC = (raw >> 4) * 0.0625f;如果传感器存在负温度,寄存器里会以二进制补码表示,比如 -25℃ 对应 raw 的高 12 位为 0xFE70,右移后做有符号数运算即可。这个地方最容易踩坑:如果乱用无符号类型,负温度一出来就是两百多度,控制逻辑立刻误动作。
软件里我会把浮点尽量少用,温度值统一用乘以 16 或乘以 100 的定点数传递。比如用 int16_t 保存 tempC × 100(精确到 0.01℃),上屏时手动加小数点,这样既省 CPU,也能避免浮点误差在多次累计后被放大。
4.3 滤波、超限保护与数据组织
HVAC 现场的读数必须过滤波。取 8 次采样做滑动平均是成本最低、效果也能接受的方案。滑动平均的窗口长度要权衡响应速度和平滑度,8 次在 100ms 轮询周期下对应 0.8 秒的响应延时,对温度这种大惯性物理量完全没问题。
超限保护是我这个项目里很坚持的一环。每一路温度读到后,先做合理性检查:读到 0xFFFF 说明总线故障,读到超出传感器量程的值说明寄存器错位或干扰,这两种情况都要单独标记,不能直接参与控制计算。我会在数据表里给每个点维护一个错误计数器,连续错 3 次就报故障并停止对该点的自动控制输出,以免系统开环瞎跑。
数据组织上,我建了一个简单的结构体数组,每个节点对应一个编号、地址、类型(本地/远程)、校准偏移、滤波值和状态位。这样后面加点位、换传感器地址都只是往表里填一行的事。
typedef struct { uint8_t ch_id; uint8_t i2c_addr; uint8_t is_remote; int8_t cal_off; // 偏移,单位 0.01℃ int16_t filt_val; // 滤波后值,单位 0.01℃ uint8_t fault_cnt; uint8_t status; } temp_node_t;5. 现场真实踩坑记录:远程温度出问题的排查过程
5.1 远程传感器读数乱跳,问题出在接线方式
调试时遇到最多的问题是远程传感器读数乱跳。第一次遇到时,我先怀疑软件滤波不够,把窗口从 4 次加到 16 次,结果波动只是变缓,并没有消失。再查传感器供电,示波器看远端 VCC 纹波 200mV 上下,显然不正常。
后来一步步排查,发现问题是远端地线接法不规范。那台设备的 24V 电源返回路径和信号地走的是同一根长线,传感器工作电流变化时,地线上产生约 100mV 的压降,直接叠加到 I2C 电平判断上。处理方案是:把传感器远端的地线单独走一根粗线,同时在远端节点加一颗 10μF 电容对地,将地线回路的动态阻抗压下去。改完之后纹波降到 30mV 以下,读数稳定。
这里值得记住的是:数字传感器不等于数字信号就绝对抗干扰,地线上的压降会影响电平裕量,噪声最终会通过 SDA/SCL 的参考地影响判决。
5.2 I2C 总线挂死与恢复的方法
I2C 总线挂死几乎人人都遇到过。现象是主设备连续多次读到总线忙,SDA 被拉低不放。原因通常是某个从设备在上电瞬间输出异常、或者传输过程中被噪声打断导致状态机卡死。
软件恢复的办法是时钟恢复:主设备在检测到总线忙后,主动产生 9 到 16 个时钟脉冲,同时观察 SDA 是否恢复高电平。这相当于给卡死的从设备“喂”完整的总线周期,让它有机会把内部状态机推到空闲。对应 PIC18F55K42,可以直接切换 GPIO 方式模拟 SCL,也可以用硬件主模式里的总线恢复功能。
硬件上更要防患于未然:每个远程节点增加总线释放逻辑,也就是在传感器供电上串联一个可控电源开关,总线一旦挂死,主控先把该路总线的电源断掉 100ms,强制所有节点复位,再重新枚举。这个方法在恶劣现场特别管用,我最终做成了产品里的一个自动恢复机制。
5.3 误差问题:自热、标定与安装位置
温度读数整体偏高 1~2℃,这类问题基本跟传感器本身精度没关系,大多出在安装方式和自热上。传感器紧贴大功率电阻、继电器或者变压器时,局部温度比环境高不少;封装引脚焊盘离发热铜皮太近,热量也会直接传导进芯片。
我在设计布局时会把本地传感器放在板边,并且不要铺大面积的铜皮在芯片底下,必要时挖掉一小块地铜做热隔离。远程传感器装在风管里时,探头不能贴着管壁,要用支架悬在气流中间,否则测到的更多是管壁温度而不是流体温度。
标定方面,先把传感器固定好,和参考温度计放在同一环境里稳定 30 分钟以上,读取差值作为该通道的标定偏移,写进结构体里的 cal_off 字段。这个方法不需要高精度恒温箱,工程现场足够用。
6. 从“能测”到“好用”:HVAC 场景的后续扩展
6.1 多点轮询与地址规划
点位一多,轮询顺序和地址规划就要提前做。每个模块上电时先发一个广播校验指令,把已存在的传感器地址扫描一遍,存放在位表里;后续只轮询在位表内的地址,遇到故障地址就自动跳过并计数。如果不做在位表,总线故障后 MCU 每次都会去访问不存在的地址,等待超时的时间白白浪费。
地址规划我会按区域划分:板载本地传感器统一用低地址,远程节点从高地址开始分配,代码里用宏定义做好映射。这样即使把两块板子的固件互换,也能从地址上快速判断某个通道到底是本地还是远程,方便产线组装和售后排查。
6.2 故障诊断与报警逻辑设计
温度监测不只是数据采集,更是整个机组保护机制的数据源。我设计的保护逻辑分三级:第一级是正常范围,不做动作;第二级是预警区间,比如水温接近上限的 85%,持续超过一定时间后输出预报警,同时把风扇从手动切到自动增强档;第三级是硬保护,直接触发继电器切断压缩机。
PIC18F55K42 的可配置逻辑单元在这时就派上用场了。把温度超限比较器的输出经过 CLC 组合,直接接到安全继电器控制线上,等于在 CPU 之外加了一层纯硬件看门狗。软件偶尔跑飞不做保护动作,硬保护照样生效,这对无人值守的机房设备很重要。
6.3 预留的升级空间:从裸机到任务化
这套代码我最初是裸机超循环写的,主循环里跑轮询时序,中断里处理 I2C 事件。当点位加到十几个、还要同时处理 RS-485 通信和液晶屏刷新时,裸机代码开始吃力了,于是我把任务拆分出来,做成简单的协作式调度器,再后来想跑实时操作系统也发现很容易,因为每个功能模块的接口从一开始就是按独立任务预留的。
如果你打算以后扩展,我建议第一版就把温度采集模块做成一个独立函数集,对外只暴露“读取某个通道当前值”“设置某通道报警阈值”这两个接口,主程序其他部分不要直接访问寄存器层。这样无论是加无线模块、加云平台上报,还是换 MCU,对温度监测模块本身的影响都能降到最小。
最后再分享一个个人习惯:每次去现场调试 HVAC 温控系统,我都会带一个红外测温枪和一根短接好的 I2C 延长转接板。红外枪用来快速核对现场温度是否与屏显一致;延长转接板则让我能在不拆线的情况下把总线接到逻辑分析仪上,第一时间定位是时序问题还是设备问题。这个习惯帮我省掉了无数次无谓的代码改动,也让我对“本地与远程温度到底怎么测才稳”这件事有了更具体的判断标准。你以后调试多点温度系统时,不妨也准备一套这样的现场工具,实测下来会省心很多。