ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

基于PJ85718DM与PIC18F86J15的嵌入式多点温度监测方案

基于PJ85718DM与PIC18F86J15的嵌入式多点温度监测方案

做嵌入式温度监测这几年,我遇到最多的需求其实不是“测不准”,而是“测不全”。一台HVAC机组、一个电控柜,往往既要看主控板附近的温度,又要盯风管、回风口这些离主控好几米远的点。前阵子某项目做了一个嵌入式温度监测模块,用的正是PJ85718DM这颗I2C数字温度传感器,搭配PIC18F86J15做主控,同时解决本地与远程温度采集的问题。这篇文章就把这个方案的完整思路、电路细节、固件框架和调试中踩过的坑都摊开来说,适合正在做HVAC控制器、嵌入式测温模块、多点温度采集的工程师参考。

1. 需求拆解:为什么要分开监测“本地”与“远程”温度

1.1 HVAC 机组的“本地”和“远程”到底指哪里

很多人一看到“本地”和“远程温度”,下意识以为一个指的是设备侧、一个指的是手机App远程查看。实际在嵌入式HVAC场景里,这两个词指的是物理位置的差别。

本地温度,通常指主控板所在环境、机箱内部、回风口附件这些离MCU只有几厘米到几十厘米的位置。它反映的是设备自身的工作状态——比如主板是否过热、电源模块附近温度是否异常。远程温度,则指风管深处、送风口、室外换热器、冷水管道表面这些需要拉线才能到达的位置。它反映的是空调系统服务的环境状态,比如送风温度是否达标、盘管有没有冻结风险。

两种温度用途完全不同。本地温度更多用于设备保护,远程温度更多用于系统控制。如果把两者混在一个采集点做,就会出现保护不及时或控制滞后的问题:你把传感器放在室外机侧,主板过热了根本看不到;放在主板旁边,送风温度又测不准,压缩机频率调度就成了瞎猜。

1.2 多点测温比单点测温多出来哪些价值

单点测温的局限,其实不是精度不够,而是信息量不够。HVAC系统本质上是一个热力环路,冷量、热量在管路里传递,中间任何一个点温度异常都可能引发连锁问题。只有本地一个温度点,你只能判断“设备这会儿热不热”,根本没法判断“冷媒走到哪一段出问题了”。

这个项目里一共布置了4个PJ85718DM温度传感器:两个本地点(主控板附近和电源模块附近)、两个远程点(送风管和回风管)。采集数据后做三件事:

  • 显示各点实时温度,让现场调试人员直接看到温度分布。
  • 用送风温差控制风机转速和冷阀开度。
  • 用本地温度做设备过热保护,温度超过阈值直接触发告警。

这里最核心的价值是温差。回风温度减去送风温度,得到的温差能直接反映空调负荷变化,比单点绝对值稳定得多,不受季节基准温度漂移影响。这是单点测温方案无论如何都做不到的。

2. 器件选型逻辑:PJ85718DM 与 PIC18F86J15 各司其职

2.1 PJ85718DM:I2C 数字温度传感器的工作定位

PJ85718DM是一颗I2C接口的数字温度传感器,主要参数定位非常贴合HVAC和嵌入式场景:测温范围覆盖-55°C到+125°C,分辨率可以配置到12位,对应0.0625°C的LSB。这个量程和分辨率对空调系统而言绰绰有余,毕竟风管温度常规范围也就-20°C到+80°C。

它最实用的两个特性,一个是地址可配置,另一个是片上报警比较。

地址可配置对多点方案至关重要。PJ85718DM的I2C地址由A0、A1、A2三个引脚电平决定,最多可以在同一条I2C总线上挂8颗芯片,地址范围通常是0x48到0x4F。这意味着我不需要为每个远程测温点拉独立的模拟线,只需要一条双绞线总线就能把所有传感器串起来。

片上报警比较则能减轻主控负担。芯片内置上限报警寄存器(T_OS)和下限报警寄存器(T_HYST),实测温度超限后ALERT引脚会主动拉低,主控可以直接用外部中断响应,不必毫秒级轮询温度寄存器做判断。这颗芯片定位很像经典的LM75/TMP275这一类I2C温度传感器,寄存器的使用习惯也类似,对用过I2C总线的人来说上手很快。

2.2 PIC18F86J15:做主控的取舍

PIC18F86J15是Microchip的8位PIC18系列单片机,80引脚TQFP封装,3.3V供电。选它做主控,并不是因为它性能有多强,而是因为它把HVAC场景需要的资源几乎都配齐了:

  • 两个MSSP硬件模块,同时支持I2C和SPI,一路I2C挂温度传感器总线,另一路还能留给外部EEPROM或显示驱动。
  • 多路EUSART串口,可以直接接RS-485收发器连上位机或触摸屏,不用软件模拟串口。
  • 10位ADC通道足够富裕,哪怕后续要加NTC模拟输入做冗余,也不缺引脚。
  • 内部支持外部晶振配合PLL到40MHz工作,在8位机里算中等偏上的算力。

另外很重要的一点:PIC18系列在工业环境下的稳定性比较可靠,I2C模块的时序由硬件产生,不占用CPU去翻转IO模拟时序。这在HVAC现场强电干扰环境下有实打实的优势,软件模拟I2C一旦被中断卡顿,时序很容易被拉长导致通信失败,硬件MSSP不会出这种问题。

2.3 为什么不用模拟 NTC 而用数字 I2C 传感器

HVAC行业里大量方案还在用NTC热敏电阻,有人质疑为什么绕一圈用I2C数字传感器。我的回答很简单:因为要测远程点。

NTC方案在本地测温没问题,短距离走线干扰小,只要一分压电阻和ADC引脚就能读数。但远程点一拉线就麻烦:NTC电阻值本身会随线阻变化,线缆长了几米就引入不可忽略的误差;而且模拟信号抗干扰能力弱,走线贴近压缩机、继电器这些强干扰源后,ADC读数会抖动得很厉害。

数字I2C传感器把模拟量到数字量的转换放到了传感器端,总线上传输的是数字信号,受线阻和共模干扰的影响小得多。只要I2C时序正确、地址不冲突、上拉电阻合理,几米线缆内的读数一致性非常好。项目中远程点只这种需求下,数字传感器几乎是唯一合理的选择。

3. 硬件设计细节:供电、总线拓扑与报警触发链路

3.1 供电与电平转换问题

PJ85718DM和PIC18F86J15的工作电压定位都是3.3V,这给电路设计省了很多事。电源用一颗AMS1117-3.3LDO从24V/12V系统电源降压得到,LDO前面加TVS管和共模电感,防止压缩机启停时电源毛刺打进来。

有一点必须提醒:HVAC控制板很多外围器件是5V逻辑,比如继电器驱动、LCD背光、老式按键板。如果板上同时存在5V和3.3V系统,I2C总线千万别直接连通。PIC18F86J15和PJ85718DM都是3.3V器件,挂到5V总线上轻则读取出错,重则长期过压损坏芯片。

我在这块板上的处理方式是:I2C总线完全跑在3.3V域,所有5V器件只接IO控制引脚,用三极管或光耦隔离,互不影响。如果你是现有5V系统改造,需要让温度传感器跑在5V,那I2C引脚必须加电平转换芯片,或者至少用开漏结构配合合适的上拉电压,让主控3.3V和传感器5V在总线上通过上拉电阻决定实际高电平。

3.2 多传感器地址分配与总线接线

4个PJ85718DM的地址分配在原理图上就要定好。A2、A1、A0三根引脚通过跳线电阻接到VDD或GND,焊板上用0欧电阻选择高或低电平。我这里用了0x48、0x49、0x4A、0x4B四个地址,分别对应本地主板、本地电源、远程送风、远程回风。地址跳线做成排针,方便现场如果传感器坏了可以临时换地址顶上。

总线接线采用了手拉手的菊花链式拓扑,从主控板出发,一根I2C线串过各个传感器。总线上拉电阻选了4.7kΩ,这个值在3.3V供电、总线下挂了4颗芯片、线缆总长约3米的情况下工作稳定。如果远程线缆超过5米,建议降到2.2kΩ增强驱动能力,或者把速率降到100kHz以下跑。

远程传感器的连接器我用了RJ45水晶头的壳,里面只用了4根线:VDD、GND、SCL、SDA。用RJ45纯属工程习惯,好插拔、锁扣牢靠,现场检修方便。但要注意,RJ45线序必须定死并在外壳上标注,不然现场的人随便接一根网线,很容易把SDA和SCL搞反。

3.3 报警输出如何接入主控中断

PJ85718DM的ALERT引脚是开漏输出,所以需要外接一个10kΩ上拉电阻到3.3V。平时高电平,当温度超过上限寄存器或低于下限寄存器时拉低。

硬件上我把四颗传感器的ALERT引脚分别接到了PIC18F86J15的四个外部中断引脚上,用的中断引脚资源分别是INT0、INT1、INT2和某个Input Capture引脚。这里不建议把四个ALERT直接短接成一个中断信号,虽然从省引脚的角度看很诱人,但一旦告警发生,你需要在中断里逐个I2C读寄存器才能知道是哪颗传感器报警,排查起来非常麻烦。分离中断输入,让中断服务程序直接根据中断标志位定位到报警源,省很多事。

另外,ALERT引脚配一个0.1μF电容到地,滤掉传感器附近干扰引发的毛刺误触发。在电控柜里,接触器吸合瞬间的干扰能轻松让一根悬空的开漏线产生几十毫秒的电平抖动,这个电容是必加的。

4. 固件实现:寄存器配置、轮询流程与阈值更新

4.1 系统初始化顺序

固件初始化有个顺序问题,我前后调过好几次才稳定。推荐顺序是:先初始化I2C硬件和GPIO,再配置PJ85718DM寄存器,最后开中断。

void System_Init(void) { // 1. IO口初始化,ALERT相关引脚设为输入 TRISAbits.RA0 = 1; // ALERT_1 外部中断输入 TRISAbits.RA1 = 1; // ALERT_2 TRISBbits.RB0 = 1; // ALERT_3 TRISBbits.RB1 = 1; // ALERT_4 // 2. 初始化I2C主模块,100kHz,32MHz时钟 SSP1CON1 = 0x08; // 使能I2C主模式 SSP1CON1bits.SSPM = 0b1000; // I2C master mode, clock = Fosc/(4*(SSPADD+1)) SSP1ADD = 79; // 32MHz / (4 * 100kHz) - 1 = 79 SSP1STATbits.SMP = 1; SSP1CON1bits.SSPEN = 1; // 3. 配置4个温度传感器 TempSensor_Init(0x48, ALERT1); TempSensor_Init(0x49, ALERT2); TempSensor_Init(0x4A, ALERT3); TempSensor_Init(0x4B, ALERT4); // 4. 开外部中断,使能全局中断 INTCONbits.PEIE = 1; INTCONbits.GIE = 1; }

初始化I2C时,SSPADD的设置是很多人容易搞错的地方。PIC18的MSSP模块,主模式下I2C时钟是系统时钟除以4再除以(SSPADD+1),和常见的计算方式不太一样。我系统时钟外部晶振10MHz经PLL倍频到32MHz,算下来SSPADD=79得到约100kHz,实测时序稳定。如果你改了晶振或倍频配置,一定要重新算,不要照搬例程里的数值。

4.2 多点温度轮询代码框架

PJ85718DM的温度值存放在两个8位寄存器里,高字节是整数部分,低字节的高4位是小数部分。读取顺序是先发地址字节,再发寄存器指针0x00,然后连续读2个字节。

float TempSensor_ReadChip(BYTE i2c_addr) { BYTE hi = 0, lo = 0; int raw; // 发送起始、器件地址(写)、寄存器指针 IdleI2C1(); StartI2C1(); MasterWriteI2C1((i2c_addr << 1) | 0); // 设备地址 + 写位 MasterWriteI2C1(0x00); // 指向温度寄存器 RestartI2C1(); // 重复起始 MasterWriteI2C1((i2c_addr << 1) | 1); // 设备地址 + 读位 hi = MasterReadI2C1(); // 读高字节 ACKDT = 0; AckI2C1(); // 发送ACK lo = MasterReadI2C1(); // 读低字节 NotAckI2C1(); // 最后一个字节发NACK StopI2C1(); raw = (hi << 8) | lo; // 12位分辨率,符号扩展 if (raw & 0x8000) raw |= 0xF000; return (float)raw * 0.0625f; // LSB = 0.0625°C }

轮询周期我设在1秒。100kbps的I2C总线,读4颗传感器总共也就要几十个字节的传输,耗时在几毫秒级别,1秒的周期对HVAC的热惯性来说绰绰有余。真正限制周期的不是I2C速度,而是你后续怎么处理数据。如果你要用PID控制压缩机频率,温度采样周期建议控制在0.5~2秒之间,太快的轮询反而会让控制环路对噪声更敏感。

4.3 本地远程数据融合与阈值管理

本地和远程温度采集回来不是各自独立的,在HVAC控制里经常要把它们组合成控制逻辑使用的量。这里给一个最常用的经验公式化处理方式:

typedef struct { float local_mainboard; float local_power; float remote_supply; float remote_return; } TempPoint; float GetSupplyAirDelta(TempPoint *t) { // 送风温差 = 回风温度 - 送风温度 return t->remote_return - t->remote_supply; }

阈值管理上,报警阈值不是一次性写死到PJ85718DM寄存器里的。启动时给一个保守默认值,比如本地报警上限设为85°C,远程上限设为60°C,运行过程中根据季节或用户设定动态写入。因为PJ85718DM报警寄存器可以直接通过I2C修改,所以我每隔5秒检查一次是否需要更新阈值。

还有一点要注意:PJ85718DM默认工作在比较模式下,ALERT引脚在温度回落到滞后值以下之后会自动释放,非常适合做设备保护。如果你想要锁存式告警(触发后必须软件清除),需要在配置寄存器里把中断模式打开。HVAC场景推荐比较模式,因为现场经常出现瞬时过热,如果锁存了,必须断电重启才能清除,太耽误事。

5. HVAC 场景的特殊考量:线缆衰减、干扰过滤与校准

5.1 远程探头线缆长了之后会怎样

HVAC现场的远程传感器线缆动辄三到五米,有的老项目还从配电间拉到空调机房,十几米都有。I2C在长线下有几个明显变化:信号边沿变缓、振铃加重、反射干扰。经验范围是,线缆长度超过3~4米时,I2C速率降到100kHz以下,超过10米直接降到50kHz或者改为I2C中继方案。

现场实测下来,3米双绞线在100kbps下波形还算干净,SCL高电平上升沿大约在200ns左右,能正常通信。但做过一个测试到6米的线,同样的上拉电阻和速率,开始出现随机通信失败,看波形SCL在跳变沿有振铃,SDA在数据翻转时偶发毛刺。把速率降到50kHz并且把上拉电阻改成2.2kΩ后,恢复正常。

所以说不要迷信“I2C只能跑短距离”这种结论。距离是速率、上拉强度、线缆类型三者的联合约束。工程上用好双绞线、降低速率、合理选上拉,5米内完全能稳定跑。再远,就该考虑用I2C总线缓冲器(比如PCA9515这一类的总线中继芯片)或者干脆换成RS-485远程采集方案。

5.2 滑动均值与异常值剔除

远程传感器在空调箱振动、风机变频器附近工作,读到的原始温度值偶尔会有毛刺。毛刺来源一般是供电波动或总线干扰导致某次读到的LSB异常偏大。消除毛刺最有效的手段是滑动均值加异常值剔除。

我用的窗口是5次滑动平均,每次采集后更新:

#define FILTER_WINDOW 5 float TempFilter_Add(float new_value, float *buf, BYTE *idx) { float sum = 0; BYTE i; buf[*idx] = new_value; *idx = (*idx + 1) % FILTER_WINDOW; for (i = 0; i < FILTER_WINDOW; i++) sum += buf[i]; return sum / FILTER_WINDOW; }

同时在进入滤波器之前先做物理合理性判断:温度值小于-55°C或大于+125°C直接丢弃,超过上一次读数正负8°C的突变也丢弃。这个“突变丢弃”能挡住绝大多数总线错误读数。有一次在调试现场,变频器一启动,SDA线上就出现干扰,导致某次读到的温度高字节从0x1D变成了0x5D,直接跳变几十度,就是靠突变判断拦下来的。

5.3 两点校准与误差补偿

PJ85718DM出厂精度在典型应用里够用,但HVAC系统对远程温度控制要求稍微苛刻时,还是做一次两点校准更稳。我用的办法很土但很有效:冰水混合物和恒温水浴锅,分别把传感器探头放进去,读取芯片原始值和标准温度计读数,然后按线性公式写入固件补偿。

typedef struct { float offset; float gain; } CalibParam; float TempSensor_ApplyCalib(float raw, CalibParam *c) { // y = (raw + offset) * gain return (raw + c->offset) * c->gain; }

标定时拿到的原始值与参考值的差值,以0°C和50°C两个点的误差算斜率修正系数。这颗芯片线性度本身不错,两点校准后的误差基本能控制在±0.2°C以内。还要提一个细节:传感器对环境温度变化的响应速度受探头封装影响很大,远程点测温别指望它秒级跟上温度变化,采样周期和校准只要关注稳态精度就够了,动态响应快慢由探头结构和安装位置决定,不是软件能解决的。

6. 实测数据与踩坑复盘

6.1 一组实测数据看稳定性和偏差

项目调试后期,我在现场连续跑了一整天,记录了一组稳态数据,对比读数与我们手持标准温度计的差值,结果如下:

采集点平均读数标准温度计差值
本地主板旁34.6°C34.5°C+0.1°C
本地电源模块41.2°C41.4°C-0.2°C
远程送风管16.8°C16.7°C+0.1°C
远程回风管24.3°C24.5°C-0.2°C

这个稳定性和偏差表现,对HVAC控制来说已经足够。而且注意,连续跑一天时间里,PJ85718DM读到的数值波动范围在正负0.3°C以内,完全没有出现长时间漂移。比较有意思的是送风和回风两个点成对使用,回风温度24.3°C减送风温度16.8°C得到7.5°C的温差,正好对应当时的空调负荷,这个数据反馈到控制逻辑里,比单独看任何一个绝对温度都更有意义。

6.2 几个印象深刻的坑

第一个坑是上拉电阻值选太大。最初选了10kΩ上拉,本地点短距离通信完全正常,一接上远程线缆就开始随机出错。用示波器看发现SDA上升沿快到1μs,SCL高电平被拉得很软,I2C主设备在快速翻转时就误判了。换成4.7kΩ之后远程就稳定了,这个问题绝对值得排在实际拉线之前提前考虑。

第二个坑是PIC18F86J15的MSSP模块寄存器初始化顺序。直接照抄某参考例程,把SSPEN放到了配置模式之前,导致I2C模块一直进不了主模式,单步调试了半天。正确顺序是先配置模式位,最后置SSPEN使能模块。

第三个坑是ALERT引脚忘记配置为漏极开路。我最初以为ALERT是推挽输出,用万用表量到低电平就以为正常。后来接上主控中断引脚后怎么都触发不了,才意识到该芯片ALERT内部是开漏,我没加上拉电阻时高电平根本出不来。这个问题代码上完全体现了,但硬件上不加上拉是查不出来的。

第四个坑关于远程传感器的接线极性。采购回来的RJ45成品线分直通和交叉两种,现场工人随意拿了一根接上去,SDA和SCL被内部调换了,温度读数直接变成固定值0xFF。后来我干脆在连接器旁边标注了线序并把连接器键位做防呆,才避免再犯。

6.3 可以直接照抄的几条经验

先给几条我这次项目做完之后觉得最值得固化的经验:

  • 三点决定I2C远程通信稳定性:上拉电阻、线缆长度、总线速率。三者要配套调整,先短距离调通,再加长线验证,不要指望单独调一个参数解决所有问题。
  • 多点测温的报警源识别:ALERT引脚分别接独立中断,不要为省引脚而合并,否则排查报警来源的时间会远大于省IO口节省的成本。
  • 温度采集周期1秒对HVAC足够,滤波窗口5次滑动加突变剔除是低成本高收益的标配组合。
  • 数字温度传感器真的不适合用软件模拟I2C去读,PIC18F86J15有硬件MSSP模块就优先用硬件模块,省下来的CPU时间可以做控制算法。
  • 原理图阶段定好A0~A2地址跳线,做成标准排针,现场换传感器或扩展测点时非常灵活。

做完整套方案后回到最初的需求看,PJ85718DM加PIC18F86J15的组合确实把本地和远程温度监测做成了模块化、可扩展的系统。远程点没有用繁琐的模拟采集,而是用一条数字总线串起了所有探头,既降低了布线复杂度,也让后续加测温点变得非常简单。对我个人来说,这套方案最大的成就感还不只是温度测准了,而是把原本需要一堆转换电路和调理电路的事情,简化成了几根线和一个寄存器表。下次遇到类似需求,我大概率会把这套硬件架构直接复用,只改协议栈去适配不同的上位机。

RELATED READING

延伸阅读

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