☰
PIC18F87K22与PJ85718DM数字温度传感器的I2C多点测温实现
2026/10/22 1:12:15 网站建设 项目流程

做嵌入式温度监测这么久,有一个组合我用了很多次:一颗 PIC18F87K22 做主控,配合两片 PJ85718DM 数字温度传感器,把本地板温和远程测点温度一起管起来,用在 HVAC 控制器和机箱热管理这类项目里,稳定省心,成本也可控。这个组合不算新潮,但它解决了一个很常见的需求——在同一套系统里同时拿到“设备自身的温度”和“远端环境的温度”,而且不需要引入模拟前端、长线补偿那一堆麻烦事。

这篇内容会把整个方案的来龙去脉讲清楚:为什么选这两颗芯片、硬件怎么接、软件怎么读、现场会踩哪些坑。适合正在做 HVAC 控制器、嵌入式机箱监测或者想用单片机挂多路 I2C 温度传感器的开发者参考,即使你没用过 PIC 也完全能看懂思路。

1. 方案整体思路:一颗主控两条链路,板温与远端温度分开测

先说说这个项目当初的需求。一个 HVAC 控制器需要监测三类温度:控制板本身的温度、回风口附近的温度、以及室外机组附近的温度。前一个是“本地”,后两个是“远程”,远程测点离控制器少则两三米,多则一二十米。这种需求在嵌入式系统里非常典型,不只是 HVAC,像机房动环、配电柜、种植箱、冷库控制器都会遇到。

当时选 PIC18F87K22 而不是更大牌的 ARM 系列,考虑很直接:整个系统工作电压是 5V,外设的继电器、阀门驱动器、显示屏逻辑都是 5V 电平,MCU 直接放在 5V 系统里不需要电平转换;PIC18F87K22 的 Flash 有 128KB,RAM 接近 4KB,对温度采集、PID 调节、液晶显示、按键处理来说完全够用;更重要的是这颗芯片在 HVAC 行业有长期供货保证,批量成本低,抗干扰能力也经过很多现场验证。

传感器选了 PJ85718DM,它是典型的 I2C/SMBus 数字温度传感器,测量范围覆盖 -55°C 到 +125°C,典型精度可以做到 ±0.5°C 以内,自带 12 位 ADC,结果直接通过寄存器读出,不需要校准电路。它跟 PIC18F87K22 搭配很顺手:5V 供电直连,I2C 接口只需两颗上拉电阻就能工作,OS 报警输出引脚还能直接接到 MCU 的外部中断。整条链路是数字信号,不会出现模拟温度传感器那种长线衰减和 AD 采样误差叠加的问题。

本地和远程分开测还有一个隐藏好处:诊断方便。如果整个系统温度数据异常,对比本板和远端两个测点就能快速判断是传感器故障、线缆问题还是设备本身真的过热。以前在项目里见过只测一个点的设计,报警之后根本分不清是控制板自己热还是外部环境热,排查起来很被动。

架构上也很简单:PIC18 的 MSSP1 模块做主 I2C 控制器,本地 PJ85718DM 挂在板内总线上,地址固定;远程 PJ85718DM 做成一个小板,放在测点处,通过线缆把 I2C 总线拉过去。两块传感器寄存器结构一样,软件处理逻辑完全复用,只是设备地址不同。这样选型、备料、写驱动都省了一圈事。

2. 硬件连接细节:引脚很少,陷阱不少

2.1 本地传感器的标准接法

PJ85718DM 的引脚非常少:电源、地、SCL、SDA、OS 报警输出,还有一个地址引脚 A2-A0(部分封装是 A0-A2 三个地址脚,具体看封装)。本地传感器放在主板上,靠近 MCU,I2C 走线不超过 3 厘米,这个距离几乎不需要考虑信号完整性。

电源去耦要注意。传感器虽然功耗很低,但如果它和继电器驱动共用一个电源,继电器动作瞬间的压降会导致传感器读数跳变。项目里我在传感器 VDD 脚放了一个 0.1uF 陶瓷电容,要求布局时尽量靠近电源引脚;如果板上空间允许,再加 1uF 的钽电容兜底,效果更好。VDD 和 GND 之间不能省这个电容,我有一次就是偷懒没布电容,结果每次继电器吸合,温度都会诡异地跳一两度。

上拉电阻的值要按总线长度和挂载数量算。板内只有两片传感器(本地和远程),总线不长,我用的是 4.7kΩ 上拉到 5V。如果远程线缆很长,总线电容变大,上拉电阻可以适当改成 2.2kΩ 甚至 1kΩ,否则上升沿太缓会导致通讯错误。不过这里有个矛盾:上拉电阻太小,正常工作时总线电流会增大,功耗和电磁干扰都会上来。所以不要盲目改小,应该先测波形再决定。

2.2 远程传感器走线与抗干扰

远程传感器小板和主板之间,我一开始图省事直接用普通排线拉,结果现场调试直接翻车:3 米不到的线,数据就开始偶发错误。后来换成了双绞线,问题立刻消失。I2C 总线本身不抗干扰,长线传输时 SCL 和 SDA 之间的串扰、外部电机启停带来的共模干扰都会破坏时序。正确做法是:SCL 和 GND 一对双绞,SDA 和 VDD 一对双绞,或者用四芯屏蔽线,屏蔽层单端接地。

距离再远一些,比如超过 10 米,直接拉 I2C 总线就不太可靠了。我见过有人在 20 米长线上用 400kHz 跑,完全是碰运气。合理的方案有两类:一是把 I2C 速率降下来,比如降到 10kHz 或更低的“标准模式以下”,牺牲速度换可靠性;二是在远端加总线缓冲/中继芯片,这类芯片能把 I2C 隔离成两端,驱动能力大幅提升,还能解决电平偏移问题。HVAC 项目里对采样速率不敏感,温度变化本来就很慢,所以我倾向直接用低速率加屏蔽双绞线,不加中继也够用。

还有地电位问题,这是远程传感器最容易踩的坑。主板和远端模块如果分别接了不同的地,两边的地存在电位差,轻则读数偏移,重则 I2C 通讯直接乱掉。所以远程传感器的电源和地必须从主板单独引过去,不能依赖现场其他设备的地。我在现场就遇到过把传感器地接到金属管道上导致通讯卡死的事故,原因就是管道对地有零点几伏的感应电压,把总线电位抬高了。

3. I2C 数据读取与温度换算:把寄存器内容变成人能看的温度

3.1 MSSP 模块初始化注意事项

PIC18F87K22 有两个 MSSP 模块,我在项目里用的 MSSP1 做主模式。初始化时必须搞清楚一个计算:波特率寄存器 SSP1ADD 的值不是拍脑袋定的,它和系统时钟以及目标 I2C 频率的关系是

SSP1ADD = (Fosc / (4 × Fsck)) - 1

比如系统时钟我用内部 16MHz,想要跑 100kHz 标准模式,算出来就是 (16000000 / (4 × 100000)) - 1 = 39,十六进制 0x27。如果改成 400kHz,就是 9,0x09。这个计算公式在数据手册里写得很清楚,但很多人不看手册直接抄例程,最后通讯失败也不知道为什么。

初始化 MSSP 为 I2C 主模式时,需要打开 SCL 和 SDA 对应的第二功能方向,把引脚配置为输入(开源极),然后设置 MSSP1CON1 的使能位,MSSP1ADD 写入 0x27,MSSP1CON2 的 ACKDT 和 ACKEN 根据是否需要主动应答来设置。用一个辅助函数把这几步封装起来,比每次在主循环里从头配置寄存器清晰得多。

代码层面要特别注意:I2C 启动和停止条件不是靠写电平实现的,而是通过给 MSSP 模块一个启动/停止触发位,硬件自己产生时序。也就是说你只要往相关控制位写 1,然后等待中断标志位置起来就行,不需要软件模拟 SCL/SDA。很多人在这里不理解,总想自己翻转引脚,结果把硬件 I2C 用成了 GPIO 模拟,白白浪费了 MSSP 模块。

3.2 读取温度寄存器的完整流程

PJ85718DM 读取温度的流程是标准的 I2C 寄存器读法:先发设备地址加写位,发送要读的寄存器指针,然后再次发送设备地址加读位,连续读两个字节。设备地址最常用的写法是 0x90(7 位地址 0x48 左移一位),如果 A2-A0 引脚接法不同,地址会偏移,这个后面再细说。

我用一个函数封装单字节读和双字节读,大体的通讯流程是:

uint8_t I2C_Start(void); uint8_t I2C_WriteByte(uint8_t data); uint8_t I2C_ReadByte(uint8_t ack); void I2C_Stop(void); uint16_t read_temp_raw(uint8_t dev_addr, uint8_t reg) { uint16_t raw = 0; I2C_Start(); I2C_WriteByte(dev_addr | 0); // 写方向 I2C_WriteByte(reg); // 寄存器指针 I2C_Start(); // 重复起始 I2C_WriteByte(dev_addr | 1); // 读方向 raw = (uint16_t)I2C_ReadByte(1) << 8; raw |= I2C_ReadByte(0); I2C_Stop(); return raw; }

这里最容易出错的是重复起始条件,有些 I2C 设备不认重复起始,非得先停止再启动。PJ85718DM 的手册写明支持重复起始,但我在调试中还是碰到过兼容性问题——换批次之后偶发读不到数据,只能把连续读改成“停止-启动”。所以写驱动时最好做一个超时重试机制,读不到就重新发一次启动条件,对现场应用来说这点冗余非常必要。

温度数据换算比较直接。12 位模式下的原始值会左对齐,高位是符号位,把寄存器值右移 3 位再乘以 0.125,就是摄氏度。举个例子:寄存器读出来 0x1A00,右移三位得到 0x340,即 832,乘以 0.125 就是 104.0°C;如果读出 0xF100,右移三位后符号位是 1,要按补码转换为 -30.0°C。用整数运算时建议直接写成 (raw >> 4) * 0.0625 之类,根据实际配置选择,关键是把符号位处理对,不然冬天读出来全是零下 200 度的诡异数值。

4. 程序架构:轮询、滤波和报警阈值设计

4.1 采样轮询状态机

我建议不要在主循环里直接阻塞地读 I2C,最好用一个定时中断驱动的小状态机。因为温度传感器响应慢,整个 I2C 读取过程如果被中断打断太久,MSSP 模块的状态会乱掉,特别是远程长线状态下,通讯时间本身就不稳定。

做法是:设定一个 1Hz 的软件标志位,主循环检测到标志后读取两块传感器的温度,每次读之前做超时判断。PIC18F87K22 的 I2C 等待可以用无限循环等待中断位,但真实产品里不能无限等,万一传感器突然没应答,系统就卡死了。我写了一个简单的超时循环:

uint8_t wait_i2c_flag(void) { uint16_t timeout = 0xFFFF; while (!I2C1IF) { if (--timeout == 0) { I2C1IF = 0; return 1; // 超时 } } return 0; }

超时之后要做总线复位——把 MSSP 模块关掉再重新打开,释放 SCL/SDA。远程传感器的线缆如果被老鼠咬断或者接头进水,总线会被锁死,这个复位逻辑是保底手段。我在现场就靠这个功能撑过了好几次接头松动的“意外”。

温度滤波方面,我用了最简单的滑动平均,每 5 次采样取平均。本来想用一阶低通滤波,但后来发现在 HVAC 中温度本来就是慢变量,5 点平均已经足够。滤波要区分本地和远程,不要混在一起算,因为远程通道在长线状态下偶发的坏值会影响平均值——可以在滤波前做一次合理性检查,判断读数是否在 -60°C 到 150°C 之间,超出就丢弃本次值。这一招很土但很有效。

4.2 报警阈值和迟滞

PJ85718DM 有一个报警输出脚 OS,内部寄存器可以设置上限温度 TH 和迟滞温度 TS。把 OS 配置成中断模式,当温度超过上限时输出有效信号,可以接到 PIC18F87K22 的外部中断引脚上。这样即使主循环卡死,超温还是能触发硬件中断,这种冗余设计在 HVAC 里用来做风扇联动和超温保护特别合适。

但要注意:报警寄存器不是写一个值就行,还要选比较模式还是中断模式,以及输出有效电平极性。我在项目里用中断模式,因为我想让处理器每次超温都得到通知,而不是报警之后一直保持状态。中断模式下传感器会在每次温度回落穿过阈值时再拉一次,需要软件读温度寄存器来清除中断标志,不然会一直触发。

实际的阈值也不能定得太死。HVAC 控制板的本地温度和环境温度差异很大,控制板有继电器和变压器发热,夏天可能比环境高十几度;远端回风口温度接近真实环境温度。所以我给了两组独立的报警参数,一个针对本地传感器,一个针对远程传感器,生产调试时通过参数表下发到 Flash 存储,用户现场可以改。这个设计来自一次惨痛教训:最初把两个通道阈值设得一样,结果高温天气控制板没报警,远端房间已经热到 35 度了。温度监测不能只设一组阈值,不同测点的物理含义完全不同,一定要分开配置。

5. 实测调参记录与现场问题排查

5.1 整机校准和现场实测

批量生产时,PJ85718DM 的理论精度是够的,但 PCB 上如果有热的元件靠近传感器,自热误差会让读数偏高,这个靠芯片本身精度救不回来,只能靠布局和校准。我做过一批板子,本地传感器离一个降压芯片太近,实测读数比标准温度计高了 2 到 3 度。后来在装配工艺里加了一个单点校准:每个产品在常温下用恒温槽测温,把偏差存到 EEPROM 的校准字段里,软件读出来之后统一加偏移量。

有人说这样成本高,但实际上在 HVAC 行业很多产品出厂本来就有一道高温老化测试,顺手多测一个温度点几乎不增加工时。校准模型用最简单的线性修正就够了,不要上复杂的插值,因为数字传感器本身非线性很小。我测试过从 0 到 60 度区间的偏差,最大不超过 1 度,线性修正在这个范围内完全够用。

装机之后的整机试验比单板测试更有价值。我把整个控制器放在一个可调温箱里,模拟了三个场景:环境温度从 20 度升到 50 度、远程传感器放在箱外、远端传感器接 20 米双绞线。结果让我印象深刻的是,20 米线缆加上屏蔽层接地之后,100kHz 速率下读数非常稳定,但 400kHz 会偶尔产生坏值。产品对采样速度不敏感,所以我最终把 I2C 频率锁在 50kHz,留了足够余量。

5.2 常见问题速查表

做这个项目,前后遇到的坑我都整理成了一张速查表,后来团队调试新项目也直接拿它当参考。最典型的是通讯时好时坏:往往是线缆太长导致上升沿过缓,表现为偶发无 ACK,解决办法是换双绞线、降速率、减小上拉电阻或加中继。传感器地址冲突:如果用了多片同型号传感器,A2-A0 引脚必须接不同电平来区分地址,否则多个设备会抢总线,读到的数据一片混乱。芯片发热误报:先检查布局,再看校准,PB 板布局紧挨线圈或功率器件,即使算法再对也救不回来。低温读数卡死:远程接口进水或绝缘下降时总线电压不稳,要重点加护套和用防水接头。

现场经验与后续扩展

我个人在实际操作中体会最深的一点是,温度监测方案的核心并不在传感器精度,而在系统对错误数据的容忍能力。同一个 I2C 驱动,放在实验室里怎么读都对,一接现场长线就露馅;同一片 PJ85718DM,放在角落和放在风道口,读数能差好几度。所以硬件设计、软件滤波、校准和总线复位,这几个环节必须作为一个整体来考虑,单独强调任何一环都解决不了实际现场的问题。

如果后续要在系统里加更多测点,我建议直接用 I2C 多路复用开关芯片把总线分成几路,每路拖几条长线,而不是把所有传感器并联在同一条总线上。复用开关切换后总线电容变小,距离还能拉得更远,代码只是多一个写通道寄存器的操作。这个扩展思路在做多房间空调控制或农业大棚多点测温时特别实用,能够复用现有框架而不改动传感器驱动。

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

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

立即咨询