
1. 项目缘起与整体设计思路嵌入式温度监测这个方向看起来简单实际上手才知道坑有多密。我这次做的项目核心是用PJ85718DM这颗远程温度传感器配合PIC18F47K40单片机搭一套能同时盯住本地和远程温度的监测系统目标场景是暖通空调HVAC设备的状态监控。说白了就是让设备自己知道“我这儿热不热、远处那个探头凉不凉”然后根据温度做出相应动作。为什么选这两颗器件先说 PIC18F47K40它是 Microchip 家 PIC18 系列里比较新的一颗带 128KB Flash、3.5KB RAM外设资源很够用4 个 UART、2 个 SPI、2 个 I2C还有 12 位 ADC 和一堆定时器。做温度监测I2C 和 ADC 是刚需它都有而且引脚数 40 个做小系统不浪费做复杂系统也撑得住。PJ85718DM 则是一颗远程温度传感器走 I2C 或者 SMBus 接口能接外部二极管或者三极管做远距离测温典型精度在 1°C 以内分辨率可以到 0.0625°C。它最大的好处是把远程测温点从单片机旁边挪开HVAC 里风道、换热器、回风口这些位置离控制板很远用这颗芯片就能把探头甩过去信号在板级走 I2C 回来比直接拉模拟线抗干扰强得多。整体设计思路分三层感知层用 PJ85718DM 挂远程探头同时用 PIC18F47K40 内部 ADC 采本地热敏电阻或者内部温度传感器处理层在单片机里做温度换算、滤波、阈值判断执行与通信层通过 UART 或者 I2C 把数据送到上位机或者显示屏同时驱动继电器、风扇这类执行器。这样一套下来本地和远程温度都能实时看到超限还能自动响应。注意PJ85718DM 的 I2C 地址是可以通过引脚配置的默认 0x18 或者 0x4C 这类具体看型号后缀。上手前一定先翻数据手册的地址表别直接抄别人的代码地址不对死活读不出数据。适合谁来参考如果你在做嵌入式温度采集、HVAC 控制板、机房环境监控或者单纯想学 I2C 多器件挂载和远程测温这套方案都能直接抄作业。下面我把设计取舍、硬件细节、代码实现和踩坑记录全部分享出来。2. 核心器件选型与硬件设计细节2.1 为什么是 PJ85718DM 而不是普通热敏电阻普通 NTC 热敏电阻便宜但线性度差需要查表或者复杂的 Steinhart-Hart 公式换算而且长线传输时引线电阻会直接影响精度。PJ85718DM 内部集成了 ADC 和调理电路直接输出数字温度值远程探头走的是恒流源激励引线电阻的影响被大幅抵消。HVAC 设备里电磁环境不干净风机、压缩机启停都会带来干扰数字接口比模拟线稳得多。这颗芯片支持两种测温模式本地温度测芯片自身和远程温度测外接三极管。远程测温的物理原理是测量三极管 Vbe 在不同电流下的压差这个压差和绝对温度成正比。芯片内部会交替注入两个不同电流测两次 Vbe算出差值再换算成温度。这个过程对噪声不敏感因为差值抵消了大部分共模干扰。2.2 PIC18F47K40 的外设分配PIC18F47K40 在这个项目里承担主控角色外设分配如下外设用途引脚示例I2C1挂 PJ85718DM 和 OLED 屏RC3/RC4ADC采本地 NTC 或内部温度RA0UART1上传数据到上位机RC6/RC7Timer11 秒定时采样内部GPIO继电器、LED 指示RB0-RB3I2C 总线上挂两个器件没问题PJ85718DM 和 OLED 地址不冲突就行。如果还要加 EEPROM 存阈值注意地址别撞。PIC18F47K40 的 I2C 支持标准模式 100kHz 和快速模式 400kHzPJ85718DM 最高支持 400kHz所以跑 400kHz 能缩短读取时间减少总线占用。2.3 远程探头的选择和布线远程测温三极管建议用 MMBT3904 这类小信号 NPN或者专用二极管接法的三极管。PCB 上走线要尽量短如果探头离板子超过 30cm建议用屏蔽线屏蔽层单端接地。探头和芯片之间的走线避免和电机、继电器驱动线平行交叉时尽量垂直。实操心得远程三极管的基极和集电极短接当二极管用发射极接芯片的 D基极-集电极接 D-。如果接反了读出来的温度会离谱通常是负几十度或者上百度遇到这种先查接线。本地温度采集我用的是 10k NTC 加 10k 上拉电阻分压接到 ADC 通道。NTC 选 B 值 3950 的精度够用。分压后电压范围在 0.5V 到 3V 之间避开 ADC 的上下轨非线性区。ADC 参考电压用内部 4.096V 或者 VDD看供电稳定性。3. 软件架构与关键代码实现3.1 I2C 驱动层先搞定底层读写PIC18F47K40 的 I2C 用 MCC 生成初始化代码最省事但建议自己把读写函数封装一遍方便调试。下面是我用的 I2C 读写基础函数基于 MCC 的 API 再包一层#include mcc_generated_files/i2c1.h #define PJ85718_ADDR 0x18 uint8_t pj_read_reg(uint8_t reg) { uint8_t data; I2C1_Write(PJ85718_ADDR, reg, 1); I2C1_Read(PJ85718_ADDR, data, 1); return data; } void pj_write_reg(uint8_t reg, uint8_t val) { uint8_t buf[2] {reg, val}; I2C1_Write(PJ85718_ADDR, buf, 2); }这里有个细节PJ85718DM 的寄存器地址是 8 位的写寄存器时先发地址再发数据读的时候先写地址再重启读。MCC 的 I2C1_Write 和 I2C1_Read 已经处理了起始和停止条件直接用就行。如果自己写底层注意在写地址后要发 Repeated Start不能发 Stop 再 Start否则时序不对。3.2 温度数据读取与换算PJ85718DM 的远程温度寄存器通常是 16 位高字节整数部分低字节小数部分分辨率 0.0625°C。本地温度寄存器类似。读取代码如下float pj_read_remote_temp(void) { uint8_t msb pj_read_reg(0x01); uint8_t lsb pj_read_reg(0x10); int16_t raw (msb 8) | lsb; raw 4; // 低4位是标志位右移掉 return raw * 0.0625f; }注意不同型号寄存器地址可能不一样PJ85718DM 的远程温度高字节在 0x01低字节在 0x10本地温度在 0x00 和 0x15。这个地址表一定要以手头的数据手册为准我见过同系列不同后缀地址完全不同的情况。本地 NTC 温度换算用查表加线性插值或者直接上 Steinhart-Hart 公式。我为了省 Flash用的是分段线性插值每 10°C 一个点误差在 0.5°C 以内够 HVAC 用了。float ntc_to_temp(uint16_t adc_val) { // 分压公式反推电阻 float v adc_val * 4.096f / 4095.0f; float r 10000.0f * v / (4.096f - v); // 查表插值表省略 return lookup_temp(r); }3.3 主循环与定时采样主循环用 Timer1 做 1 秒定时到点采一次本地和远程温度做滑动平均滤波然后判断阈值。滑动平均窗口取 8 个点既能滤掉突发干扰又不会太迟钝。#define WINDOW_SIZE 8 float remote_buf[WINDOW_SIZE]; uint8_t buf_idx 0; float moving_average(float new_val) { remote_buf[buf_idx] new_val; buf_idx (buf_idx 1) % WINDOW_SIZE; float sum 0; for (int i 0; i WINDOW_SIZE; i) sum remote_buf[i]; return sum / WINDOW_SIZE; }阈值判断做迟滞比如高温报警设 60°C恢复设 55°C避免在阈值附近反复跳变。继电器输出加最小切换间隔比如 30 秒内不重复动作保护压缩机。3.4 通信协议与上位机对接UART 上传数据用简单的 ASCII 帧格式T:25.3,R:48.7\n上位机解析方便调试时串口助手直接看。如果要更规范可以上 Modbus RTU但 HVAC 小系统里 ASCII 够用。printf(T:%.1f,R:%.1f\n, local_temp, remote_temp);PIC18F47K40 的 UART 波特率用 115200误差在 1% 以内。printf 重定向到 UART 需要实现_putch函数MCC 里有现成的。注意I2C 读取失败时要有重试机制连续 3 次失败就报传感器故障别让程序卡死在 while 循环里。我一般加超时计数超过 10ms 没响应就跳过本次采样。4. 常见问题与排查技巧实录4.1 温度读数跳变或固定不变这是最常见的问题。先分清楚是跳变还是固定。固定不变通常是 I2C 没通用示波器看 SDA/SCL 有没有波形或者用逻辑分析仪抓包。如果波形有但数据不对查地址。跳变厉害的话先看电源纹波PJ85718DM 的 VDD 要加 0.1uF 和 10uF 去耦靠近引脚放。远程探头线太长也会跳加屏蔽或者降低 I2C 速率试试。4.2 远程温度偏差大远程测温对三极管型号敏感MMBT3904 和 2N3904 的 Vbe 特性有差异芯片内部有补偿但有限。如果偏差稳定在几度可以在软件里加偏移校准。偏差不稳定的检查三极管焊接有没有虚焊或者换一个批次的管子。另外芯片的远程测温电流档位可以配置默认档位如果噪声大可以换低电流档但灵敏度会降。4.3 I2C 总线锁死PIC18F47K40 的 I2C 在从器件拉低 SDA 不放时会锁死。解决办法是在初始化时发 9 个时钟脉冲解锁或者直接复位 I2C 模块。MCC 生成的代码里一般有I2C1_Initialize()重新调用一次能恢复。更稳妥的做法是加外部看门狗或者用 GPIO 模拟 I2C 做备份。4.4 常见问题速查表现象可能原因排查方法读不到器件地址错、上拉电阻缺失查手册地址测 SDA/SCL 上拉温度固定 -64°C远程三极管未接或接反检查 D/D- 接线温度跳变 ±5°C电源噪声、探头线太长加去耦缩短线降速I2C 锁死从器件异常拉低发时钟脉冲解锁复位模块UART 无输出波特率错、TX/RX 接反测波形交换线序避坑技巧调试 I2C 时先把速率降到 100kHz稳定后再升 400kHz。很多“玄学”问题都是速率太高导致的。另外PJ85718DM 的配置寄存器写完后要等一个转换周期再读温度 datasheet 里写的是 100ms 左右别写完立刻读读出来是旧值。5. 系统联调与实测数据硬件焊好、代码烧进去之后我做了几组实测。本地温度用 NTC 和标准温度计对比25°C 时偏差 0.3°C50°C 时偏差 0.8°C在可接受范围。远程温度用 MMBT3904 探头放在恒温水槽里从 10°C 到 80°C 每隔 10°C 测一个点偏差最大 1.2°C出现在 80°C 附近可能是三极管自热导致。整体精度满足 HVAC 监控需求。响应速度方面I2C 400kHz 下读一次远程温度大概 2ms加上本地 ADC 采样和滤波1 秒周期绰绰有余。继电器动作延迟在 100ms 以内对风机控制来说足够快。功耗测了一下PIC18F47K40 跑 16MHzPJ85718DM 连续转换整板电流约 12mA如果用低功耗模式间歇采样可以降到 2mA 以下。电池供电的场景可以这么玩。这套方案我后来还扩展了 OLED 本地显示和蜂鸣器报警代码结构没大改只是加了显示刷新和 GPIO 控制。如果你也要做类似项目建议先把 I2C 读温度跑通再加显示和通信一步一步来别一上来就全功能联调出了问题不好定位。