☰
基于PJ85718DM与PIC18F55K42的本地远程温度采集方案详解
2026/10/22 6:41:34 网站建设 项目流程

每年入夏,总有一批项目卡在温度上:嵌入式设备机箱里温度起来了,代码里却不知道哪个点是热源;暖通项目现场风管两头温差明显,控制器却只看一个测点。近端远程都要同时掌握,我最后把方案落在 PJ85718DM 与 PIC18F55K42 这套组合上。PJ85718DM 是一颗带本地测温通道、同时支持一路远程温度探头的数字温度传感器,PIC18F55K42 作为主控,通过 I2C 总线把两路温度读回,再换算成可用的工程温度。这篇文章把我从选型、硬件连线、软件读取,到现场调试踩坑的完整过程写出来,适合正在做嵌入式散热监测、HVAC 送风回风温差控制或者类似远程测温需求的工程师参考。

1. 一冷一热两种工况,为什么最终选定双通道测温方案

1.1 嵌入式测温与HVAC测温差在哪

做测温方案之前,先得想明白一件事:嵌入式设备和 HVAC 现场对"温度"的要求根本不是同一个维度。嵌入式关心的是板上局部过热,传感器离热源可能就是几厘米,采样周期可以做到每秒几次甚至几十次,追求的是快速响应和低功耗;HVAC 关心的是风管、房间、冷却塔这些大空间里的温度趋势,探头往往拉出去好几米甚至十几米,采样周期一秒一次就已经非常充裕,反而更在意长线缆带来的干扰和温度滞后。

我把两种场景的核心差异列个表,方便后面所有设计决策对号入座:

对比项嵌入式设备内测温HVAC现场测温
典型测温点机箱内、PCB附近、功率器件风管送风/回风、房间、室外
探头距离厘米级数米到十几米
采样频率需求1Hz~10Hz0.2Hz~1Hz
主要干扰电源纹波、自热电机启停、长线耦合、凝露
精度侧重短时过温保护温差趋势与稳定性

"本地与远程温度"在这两类项目里有个共同理解:本地温度指传感器所在电路板或设备附近的温度,远程温度指通过探头拉到被测点的温度。很多控制器真正想知道的不是绝对温度有多准,而是看两个温度的差值——嵌入式里是机箱内部和外部环境的温差,HVAC 里是送风和回风的温差。所以测两路不是奢侈,而是控制逻辑的基本盘。

1.2 PJ85718DM 的本地加远程测温工作方式

PJ85718DM 最吸引我的地方,是它一颗芯片同时处理本地和远程两路温度,不需要我再外挂一颗模拟前端。本地通道内部用带隙基准电路感知芯片自身温度,这个原理和多数数字温度传感器一样,核心是把 PTAT(与绝对温度成正比)电压转换成数字量。芯片自己知道自身体温,这就够了。

远程通道则是通过激励外部 PN 结来测量远端温度。具体工作时,芯片给探头的二极管或三极管 PN 结施加两个不同大小的电流,通过测量两次正向压降的差值来反推温度。为什么不用直接测 Vbe 绝对电压的办法?因为单个 PN 结的绝对压降受工艺、型号、线阻影响很大,离散性太严重;而 ΔVbe 与温度的关系几乎只取决于玻尔兹曼常数和电子电荷,线性度和一致性都好很多,所以工业测温芯片普遍走这条路。

如果你的现场条件不适合拉 PN 结探头,也有更朴素的用法:利用芯片的地址引脚,在同一根 I2C 总线上挂两颗 PJ85718DM,一颗放本地、一颗放远程。我实际的项目里两种方式都用过,双通道方案适合机箱内部加一个远端温度监测点,双芯片方案适合多点分布式测温。标题里说的"本地与远程温度",用双通道模式就能覆盖。

1.3 PIC18F55K42 在此承担的角色与选型理由

PIC18F55K42 是 8 位单片机,我在这套方案里主要用到它的 I2C 主模式、定时器中断、看门狗和低功耗模式。它是一个 K42 系列器件,自带可配置逻辑单元、带计算功能的 ADC 等外设,但对于我们这个纯数字温度读取场景,最实用的其实是稳定的内部振荡器和成熟的 MSSP 串口模块。

为什么用 8 位机而不是上 32 位?成本、功耗、启动时间都能省一点,而且温度监测根本不需要跑复杂算法,读两个寄存器、做滑动平均、做阈值判断,8 位机的资源绰绰有余。为什么又不用纯模拟方案?温度传感器直接输出数字信号,长线传输不受模拟小信号衰减的影响,现场校准和调试都更简单。PIC18F55K42 在我这边的定位就是一个可靠的"温度数据搬运工":定时唤醒,读温度,处理数据,输出控制。

2. 从传感器到单片机:接口电路里不能省的部分

2.1 I2C上拉、供电与地址位的处理

PJ85718DM 和单片机的物理连接看起来简单,就四根线:VDD、GND、SDA、SCL,最多再加一根 ALERT 报警输出。但 I2C 是开漏结构,SDA 和 SCL 必须有上拉电阻才能输出高电平,这个电阻值的选择直接影响整条总线的稳定性。

我在 3.3V 系统里通常从 4.7kΩ 上拉起步,如果总线上挂的设备多、或者线缆超过半米,就换成 2.2kΩ。上拉电阻太大,上升沿会变缓,超过规范要求的上升时间后,设备可能把高电平误判成低电平;上拉电阻太小,低电平灌电流变大,又会增加功耗和干扰。400kHz 速率下,总线电容一般控制在 400pF 以内,普通跳线超过 20cm 就得留意了。

供电策略也很关键。如果 MCU 是 3.3V、传感器也工作在 3.3V,那 SDA/SCL 直接上拉到 3.3V 就行。如果传感器只支持 5V,而 MCU 是 3.3V,就不能简单地把上拉电阻接到 5V,否则高电平会直接灌进 MCU 引脚,时间长了必然损坏。稳妥做法是传感器和 MCU 统一用 3.3V 供电,或者加一颗双向电平转换芯片,高压侧上拉到 5V、低压侧上拉到 3.3V。

地址位是另一个经常翻车的地方。PJ85718DM 的 A0/A1(有些型号是 A2/A1/A0)引脚可以硬接 GND 或 VDD 来选择总线地址,挂多颗传感器时靠这个区分。我习惯在原理图上把地址引脚附近的走线画明显,并且直接标注电平,避免调试时反复猜。

2.2 远程探头线缆的屏蔽、保护与补偿

远程探头是这套方案里最脆弱的一环。HVAC 现场,探头常常要穿到风管里、拉到天花板上面,线缆一长,什么怪问题都可能出现。远程 PN 结探头不要用普通杜邦线,要选屏蔽双绞线,屏蔽层在单片机侧单端接地。双绞是为了抵抗差模磁场的耦合,屏蔽层单端接地是防止地环路变成天线。

线缆本身的电阻不能忽略。远程探头的线缆越长,串联电阻越大,测量结果会出现单调偏移。遇到这个问题,先别急着改软件,用万用表量一下线缆两端的实际电阻,对照数据手册给出的补偿曲线或者经验系数做修正。更隐蔽的是接插件氧化,端子压接不紧、镀层不好,接触电阻从几十毫欧漂到几欧,读数就会跟着跳。所以现场探头连接处我坚持用压接端子,接头位置喷涂一层保护剂,防止凝露腐蚀。

如果传感器接口附近有雷击浪涌风险,比如室外风管温度探头,一定在 PCB 上加 TVS 管。HVAC 现场很多 I2C 芯片坏掉不是单片机的问题,是浪涌从长线缆直接灌进传感器造成的。

2.3 电源、地线和板级布局的注意

温度传感器本身是低功耗器件,但它的读数很诚实,PCB 上的任何发热都会通过铜箔传导过来。想测空气温度,传感器下方就不要铺大铜皮,让它和空气接触;想测板温,反而要通过铜皮保证热耦合。很多样机测出来温度偏高,不是传感器坏了,是底下铺了一块电源铜皮。

地线处理上,信号地和风机、继电器、加热器的大电流地要分开走,最后单点汇合。电机启动瞬间有几安培的电流突变,哪怕是 0.1Ω 的地线阻抗,也会产生 0.5V 以上的地弹,直接落在传感器和单片机之间的通信基准上。这个问题在实验室样机上几乎看不出来,一到现场装进机组就原形毕露。

3. 用代码配置工具生成工程,跑通本地与远程温度读取

3.1 时钟、I2C主模式与采样节拍配置

PIC18F55K42 支持内部振荡器工作,温度监测这个场景对时间精度要求不高,我直接用内部高频振荡器分频到 32MHz,省掉外部晶振。然后在开发环境的代码配置工具(MCC)里勾选 MSSP 模块,配置成 I2C 主模式,速率选 400kHz。如果探头线缆超过一米,我会主动降到 100kHz,牺牲一点速度换稳定。

有一个细节很容易坑人:MCC 生成代码时会默认分配引脚,但默认分配的不一定是你实际板子上接线的引脚。生成完工程后,第一件事是打开引脚分配页面,核对 SDA 和 SCL 到底映射到哪两个引脚,再对照原理图确认。我见过不止一个朋友拿着默认生成的代码去调,查了半天才发现传感器根本没接到 MCU 默认的 I2C 引脚上。

采样节拍用定时器来做,不要在主循环里写软件延时。我习惯用 Timer0 配置成 1ms 中断,在中断里累加计数,到 1000 次就置一个"1秒到"的标志,主循环里读到标志再去读温度。这样采样周期稳定,也不会因为某次 I2C 通信卡顿影响整个系统节拍。

3.2 传感器寄存器的读写思路

PJ85718DM 这类数字温度传感器的寄存器操作思路大同小异,一般有指针寄存器、配置寄存器、本地温度寄存器、远程温度寄存器、报警上限和迟滞寄存器。读温度前,先要把指针指向温度寄存器地址,然后连续读两个字节。写配置时,先写指针寄存器,再写配置字节。

具体 I2C 事务流程是:启动信号 → 发送器件地址加写位 → 发送寄存器指针 → 重复启动 → 发送器件地址加读位 → 读高字节 → 读低字节 → 发送非应答 → 停止信号。读两个字节时,第一个字节要应答,第二个字节要让传感器知道后面没有数据了,所以发 NACK。很多人在这里搞反,两个字节都发 ACK,传感器就会一直输出下一位,导致数据错位。

配置寄存器里最关键的是转换模式选择。连续转换模式适合调试和实时监控,传感器每秒钟都在转换;单次转换模式适合低功耗场景,MCU 需要的时候才触发一次转换,转换完成后读取,读完让传感器继续休眠。

3.3 本地与远程温度读取的完整代码流程

我给出一个非常简化的流程,用来表达核心思路。实际工程里要把错误处理和超时判断加进去,尤其是 I2C 总线 NACK 之后的重试逻辑。

#define SENSOR_ADDR 0x48 // 根据A0/A1实际配置调整 #define REG_LOCAL 0x00 // 本地温度寄存器 #define REG_REMOTE 0x01 // 远程温度寄存器 void sensor_write_byte(uint8_t reg, uint8_t val) { i2c_start(); i2c_write((SENSOR_ADDR << 1) | 0); // 写地址 i2c_write(reg); // 指针 i2c_write(val); // 数据 i2c_stop(); } void sensor_read_word(uint8_t reg, int16_t *raw) { uint8_t hi, lo; i2c_start(); i2c_write((SENSOR_ADDR << 1) | 0); i2c_write(reg); i2c_restart(); i2c_write((SENSOR_ADDR << 1) | 1); // 读地址 hi = i2c_read(1); // ACK lo = i2c_read(0); // NACK i2c_stop(); *raw = (int16_t)((hi << 8) | lo); } int main(void) { // 初始化时钟、I2C、定时器等 // 配置传感器:12位分辨率,连续转换 sensor_write_byte(0x01, 0x60); while (1) { if (sample_flag) { sample_flag = 0; int16_t local_raw, remote_raw; sensor_read_word(REG_LOCAL, &local_raw); sensor_read_word(REG_REMOTE, &remote_raw); // 交给换算和滤波模块处理 } } }

读取期间要小心中断打断 I2C 事务。在 I2C 的 start/stop 序列中间,如果单片机被一个长中断抢走几十微秒,总线时序就乱了。我把 I2C 读取放在主循环里执行,定时器中断只负责置标志,不让中断函数里去直接动 I2C 总线。

4. 把原始寄存器值变成可靠温度值:换算、滤波与报警

4.1 补码换算与分辨率处理

PJ85718DM 这类 12 位精度温度传感器,温度数据在寄存器里通常占高 12 位,低 4 位是无效位。每个 LSB 对应 0.0625℃,这个值来自 1/16℃ 的分辨率,也就是 12 位温度数据里,每 16 个计数对应 1℃。换算时先把原始 16 位数据右移 4 位,再按有符号数做符号扩展,最后乘 0.0625。

int16_t raw; // 从传感器读回的原始值 int16_t value; // 右移后带符号的整数 float temp_c; // 摄氏度 value = (int16_t)(raw >> 4); temp_c = (float)value * 0.0625f;

下面几个典型值可以自检代码有没有写对:

寄存器原始值(16bit)右移4位后的有符号整数对应温度(°C)
0x000000
0x00A0100.625
0x0A0016010
0x19A041025.625
0xFF00-16-1

负温度一定要小心符号扩展。如果直接把 0xFF00 当成无符号数右移,会得到 0x0FF0,也就是 255,换算出来变成 15.9375℃,完全错了。用 int16_t 类型承载原始数据再右移,编译器会保留符号位,才能得到正确的 -1℃。

4.2 滑动平均和阈值迟滞怎么选

转换出来的温度不要直接用,原始数据会有随机噪声,尤其远程通道受线缆和接插件影响更大。我一般加一个滑动平均:保留最近 N 个样本求平均。嵌入式场景 N 取 8 到 16,既有平滑效果,又不会让响应慢到没法用;HVAC 场景我常常取 60 个样本,相当于一分钟平均,因为暖通控制本来就不需要反应那么快,反而需要抑制风门、风机短暂动作带来的温度尖峰。

#define WINDOW 8 static float samples[WINDOW] = {0}; static uint8_t idx = 0; float avg = 0.0f; samples[idx] = temp_c; idx = (idx + 1) % WINDOW; for (uint8_t i = 0; i < WINDOW; i++) { avg += samples[i]; } avg /= WINDOW;

阈值报警一定要加迟滞。比如设定超过 50℃ 报警,到 50℃ 触发;但回落时不要到 50℃ 以下就立刻取消,那样风机关了又开,继电器会在一两度区间里反复跳动。我通常设置 0.5℃ 到 1℃ 的迟滞窗口:超过 50℃ 报警,低于 49.5℃ 才复位。嵌入式散热风扇的迟滞甚至可以给到 2~3℃,避免频繁启停。

4.3 温差计算在 HVAC 和嵌入式里的落地

有了本地和远程两路温度,最有价值的其实是差值。HVAC 系统里,送风温度和回风温度的差值能直接反映系统换热效果:制冷模式下,回风温度比送风温度高,说明房间里的热量正在被带走;差值太小,可能是风量不足或者滤网堵了。嵌入式设备里,机箱内部温度和外部环境温度的差值如果持续变大,说明散热风道出了问题。

这个温差不一定要在 MCU 里算得多么复杂,做两个标志位就够:local_overlimit、remote_overlimit,然后在控制逻辑里组合判断。PIC18F55K42 的 CLC 模块甚至可以把比较结果做成硬件自动输出,不需要 CPU 干预,适合做硬件风扇联动,不过初学者不要一上来就搞这些,先用软件标志位,逻辑更透明,出了毛病也好查。

5. 现场调试踩过的三个坑,以及完整排查链路

5.1 总线读回全FF或总是NACK

第一个坑:上电后读任何寄存器,返回值不是全 FF 就是 I2C 总线在地址阶段就收到 NACK。遇到这个问题,我的排查链路非常固定。

先用示波器看 SDA 和 SCL 的波形:如果 START 之后地址字节发出去,第九个时钟上 SDA 没有被拉低,说明传感器没有应答。第一步查器件地址,看 A0/A1 引脚实际电平是否和代码一致。我踩过一次,原理图上 A1 接了 VCC,结果代码里还按两个地址位都接地的 0x48 去读,一直 NACK。改成 0x4A 之后立刻通了。

第二步查 SDA 高电平。用示波器看 SDA 空闲时是否被拉到接近 VDD,如果只有零点几伏,大概率上拉电阻没焊或者短路了。第三步查供电。传感器 VDD 有没有 3.3V?有些现场的辅助供电线压接不牢,一上电有电,一接负载掉压,也会让传感器工作不稳定。

最后还有一个隐蔽问题:5V 传感器配 3.3V 单片机,上拉电阻接在 5V 上,MCU 输入引脚长期承受 5V 高电平,虽然没烧,但内部保护二极管漏电会把 I2C 电平拉得很怪。排查到最后一步,我一般会强制把速率降到 100kHz 再试一次,排除总线电容太大导致边沿过缓的影响。

5.2 远程读数跳变或单调偏低

第二个坑:本地温度很稳,远程温度一会儿跳 ±1℃,一会儿整体偏低两度。这种问题最容易让人怀疑传感器坏了,但大多数时候坏的是物理连接。

我的排查顺序是:先关掉软件滤波,看原始值是否真跳。然后万用表量远程探头连接端子到 PN 结之间的电阻,如果阻值在几十欧以上,大概率是接插端子氧化或者压接不紧。把线缆换成短线,把探头直接挪到传感器旁边对比,如果读数接近本地温度,说明原来的长线缆引入的电阻或者耦合噪声是主因。

还有一种情况:探头虽然没坏,但激励电流在细长线上造成额外压降,导致测得温度偏低,这是线阻补偿的问题。查清楚线缆电阻后,在软件里做固定偏移修正,或者在硬件上加大线径。远程探头最忌讳用杜邦线"怼"着用,当时测没事,三个月后端子氧化,读数漂到你怀疑人生。

5.3 风机一启动,温度读数就漂

第三个坑是 HVAC 现场特有的:风机或者变频器一启动,远程温度瞬间跳 2~3℃,严重时 I2C 直接乱码,过一会儿自己恢复。我开始以为是软件问题,加滤波、加延时都没用,后来用示波器把探棒夹在 SDA 和 SCL 上,盯着风机启动瞬间看,线缆上会感应出一串毛刺。

排除链路是这样的:先把远程探头线从原路径拉出来,悬空或者临时穿一根金属软管,看读数是否稳定。如果稳定,说明是空间辐射耦合进长线;如果还不稳定,查电源地线,看电机启动电流是不是在公共地上砸出电压尖峰,导致传感器电源抖动。这两种路径的解决办法不一样,前者靠屏蔽,后者靠分离接地和电源滤波。

最后我用的组合方案是:远程线全程套金属软管,屏蔽层单端接地;传感器电源入口加磁珠加 10μF 和 100nF 电容滤波;I2C 线上串 33Ω 电阻;软件里风机启动后延迟 200ms 再采一次,连续读三次取中值而不是平均值。故障彻底消失。这类问题在样机阶段根本复现不出来,所以做 HVAC 项目的朋友一定要在立项时就按长线、强干扰场景预留硬件措施。

6. 低功耗采样与总线异常恢复的收尾建议

6.1 单次转换加定时唤醒,把功耗降下来

如果这套系统是电池供电的嵌入式设备,传感器长期连续转换就是一种浪费。把配置寄存器改成单次转换模式:MCU 在定时器唤醒后,给传感器发一条触发转换命令,等待几十毫秒转换完成,读取结果,然后让传感器进入关断状态,MCU 也继续睡。我做过一个 30 秒采样一次的设备,电池续航比连续模式延长了一个量级。

单片机这边也要配合。PIC18F55K42 在等待转换完成期间,可以直接进 SLEEP,用定时器唤醒。需要注意的是,唤醒后 I2C 模块可能处于不确定状态,所以我把 I2C 重新初始化放在每次唤醒后执行,而不是依赖上一次的状态。这样稍微花几微秒,但换来的是稳定。

6.2 总线卡死的恢复和看门狗配合

I2C 总线卡死是个经典问题。最常见的原因是:单片机在 I2C 事务中途复位,SCL 恰好停在低电平,那么总线就被这个单片机自己锁住了。看门狗复位只能让程序重来,但如果 SCL 还是低,重新初始化 I2C 也没用,必须手动把总线"解放"出来。

恢复办法是把 SCL 临时配置成普通 GPIO 输出,手动产生 9 个时钟脉冲,让从设备释放总线,然后再重新初始化 I2C:

void i2c_bus_recover(void) { SCL_TRIS = 0; // 设为输出 SCL_LAT = 0; for (int i = 0; i < 9; i++) { SCL_LAT = 0; __delay_us(5); SCL_LAT = 1; __delay_us(5); } // 重新初始化I2C模块 }

看门狗的超时时间一定要大于单次 I2C 操作的最长时间。我之前把看门狗设得太短,结果总线上一笔正常的长时间读取操作触发了复位,反而更容易卡总线。现在我的习惯是看门狗超时设为最长 I2C 事务时间的 5 倍以上,这样既能兜底死循环,又不误伤正常通信。

这套方案我前后改了三轮,最后沉淀下来的经验是:温度采集看着是单片机最基础的活,但本地加远程两路一起做,难点全在物理层和现场环境。PJ85718DM 负责把两路温度变成数字,PIC18F55K42 负责把数字变成可靠的数据,真正决定系统成败的是探头线怎么走、滤波窗口怎么设、总线异常怎么恢复。如果你也在搭类似的系统,建议先按这个顺序验证:短线上电跑通 I2C,再拉长探头线,最后再进现场环境。每一步都稳定了,后面才省心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询