
1. 项目缘起与整体设计思路嵌入式温度监测这件事说起来简单做起来全是细节。我最早接触这类需求是在一个环境控制类项目里当时的要求很朴素本地要能看到实时温度远程也要能拿到数据而且两路数据得对得上。选型的时候翻了不少方案最后定下来用PJ85718DM做本地温度采集PIC18F4620做主控和数据处理远程部分通过串口往上位机或网关送。这套组合不是拍脑袋定的背后有几层考量。先说 PJ85718DM。这颗器件本质上是一个带 I2C 接口的数字温度传感器测温范围覆盖工业级常见区间精度在常温段能做到 ±0.5℃ 以内分辨率可以通过寄存器配置到 0.0625℃。它最大的好处是输出直接是数字量不需要外部 ADC也不存在热敏电阻那种非线性校正的麻烦。对于 HVAC 场景来说温度变化本身是慢变量采样率不需要很高但长期稳定性一定要好PJ85718DM 在这点上比较省心。再说 PIC18F4620。这颗 8 位 MCU 算是 Microchip 家族里非常经典的一款64KB Flash、3968 字节 RAM、自带 I2C 和 EUSART 模块跑温度采集加串口上报这种任务绰绰有余。选它而不是选更小的 PIC16 系列主要是考虑到后面可能要挂多个传感器、要跑简单的滤波算法、还要留出足够的缓冲做协议打包。8 位机做这些事不算吃力但资源得留够余量不然改需求的时候会很被动。整体架构上我把它分成三层感知层是 PJ85718DM负责把物理温度转成数字量控制层是 PIC18F4620负责轮询传感器、做数据校验和简单处理传输层是 EUSART 转 RS-485 或直接 TTL 串口把数据送到远程端。本地显示可以挂一个 1602 液晶或者 OLED远程端则通过上位机软件或网关解析。这个分层的好处是每一层职责清晰调试的时候可以单独隔离不会一出问题就抓瞎。注意PJ85718DM 的 I2C 地址由 A0-A2 引脚决定默认地址是 0x48 左右具体要看数据手册的地址表。如果总线上挂多个同型号传感器一定要提前规划好地址跳线不然后面改硬件会很麻烦。为什么不用模拟传感器加 ADC 的方案我试过主要是两点受不了一是热敏电阻的阻值-温度曲线需要查表或拟合代码里要存一张表占 Flash 不说标定还费时间二是长线传输时模拟信号容易被干扰尤其是 HVAC 设备旁边有继电器和风机噪声一大ADC 读数就飘。数字传感器在这两点上优势明显I2C 虽然也有时序要求但至少数据本身是干净的。2. 核心细节解析与实操要点2.1 PJ85718DM 的寄存器配置与温度换算PJ85718DM 内部有几个关键寄存器温度结果寄存器、配置寄存器、以及上下限报警寄存器。上电默认是连续转换模式转换时间典型值在几十毫秒量级。温度结果是一个 16 位寄存器高 12 位有效低 4 位是保留位。换算的时候要把高 12 位取出来然后根据符号位判断正负。具体换算逻辑是这样的读回来的 16 位数据右移 4 位得到一个 12 位有符号数。如果是正温度直接乘以 0.0625 就是摄氏度如果是负温度需要先做补码转换再乘。我见过有人直接拿原始值乘系数结果零下温度全错这种坑在调试阶段一定要用冰水混合物和热水各验一次。配置寄存器里比较重要的是分辨率设置和转换模式。分辨率越高转换时间越长但 HVAC 场景根本不需要 12 位全开我一般设成 11 位或 10 位转换时间短一点功耗也低一点。如果是电池供电的节点还可以用单次转换模式测完就进休眠需要的时候再唤醒。提示I2C 总线的上拉电阻不能省典型值 4.7kΩ 到 10kΩ。我遇到过有人为了省事直接用 MCU 内部上拉结果在 100kHz 以上速率时波形上升沿明显变缓数据偶尔出错。外部上拉电阻是必须的别偷这个懒。2.2 PIC18F4620 的 I2C 主机模式配置PIC18F4620 的 MSSP 模块可以配置成 I2C 主机模式。关键寄存器是 SSPCON1、SSPCON2、SSPADD 和 SSPSTAT。SSPADD 里放的是波特率发生器的值计算公式是SSPADD (Fosc / (4 × Fscl)) - 1假设晶振是 8MHz想要 100kHz 的 I2C 速率那 SSPADD (8,000,000 / (4 × 100,000)) - 1 19。如果要用 400kHz 快速模式SSPADD 4。这里要注意PIC18F4620 在 400kHz 下对总线电容比较敏感走线长了容易出问题我一般保守用 100kHz温度采集又不赶时间。初始化流程大致是先设置 TRIS 寄存器把 SDA 和 SCL 配成输入然后写 SSPCON1 选择 I2C 主机模式写 SSPADD 设波特率最后把 SSPEN 置位使能。启动传输的时候先发 START 条件再发设备地址加写位等 ACK然后发寄存器指针再重启发读地址最后读数据。这一套时序用中断或者轮询都能做我习惯用轮询加超时计数简单直接不容易卡死。2.3 本地显示与远程传输的分工本地显示我一般用 1602 液晶4 位数据模式占用引脚少。显示内容就是当前温度和状态标志刷新率不用太高一秒一次足够。远程传输走 EUSART配置成 9600 或 19200 波特率8 数据位、无校验、1 停止位。数据格式我习惯用 ASCII 帧比如“T:25.3\r\n”好处是调试的时候直接接串口助手就能看不用写解析工具。如果要走 RS-485 长距离传输需要在 EUSART 和总线之间加一个收发器芯片比如常见的半双工差分收发器。方向控制引脚要跟发送时序配合好发完最后一个字节后要等 TXIF 和 TRMT 都置位才能切回接收不然最后几个字节会丢。这个细节我在早期项目里踩过数据偶尔少一帧查了半天才发现是方向切换太早。参数项本地显示方案远程传输方案接口并口 4 位模式EUSART 异步串口速率约 1 次/秒9600bps 或 19200bps数据格式直接显示ASCII 帧或二进制帧距离板载TTL 短距RS-485 可达千米级抗干扰不涉及差分传输抗共模干扰3. 实操过程与核心环节实现3.1 硬件连接与上电检查接线这块PJ85718DM 的 VDD 接 3.3V 或 5V 要看具体型号后缀PIC18F4620 我一般跑 5V所以传感器也选 5V 版本省得加电平转换。SDA 和 SCL 分别接 PIC 的 SDA1 和 SCL1 引脚也就是 RC4 和 RC3。报警输出引脚如果不用可以悬空但最好配成输入并接下拉避免浮空引入噪声。上电之后第一件事不是写代码而是用万用表量电压。VDD 是不是 5VSDA 和 SCL 的静态电平是不是高上拉电阻两端电压对不对。我见过一次是上拉电阻焊成了 100Ω结果 I2C 直接拉死传感器发烫。硬件检查花五分钟能省后面几小时的调试。3.2 温度采集函数的实现采集函数的核心就是 I2C 读时序。我一般封装成两个函数一个写寄存器指针一个读数据。写指针的流程是 START → 发地址W → 等 ACK → 发寄存器地址 → 等 ACK → STOP。读数据的流程是 START → 发地址W → 等 ACK → 发寄存器地址 → 等 ACK → 重启 START → 发地址R → 等 ACK → 读高字节 → 发 ACK → 读低字节 → 发 NACK → STOP。读回来的两个字节拼成 16 位右移 4 位判断符号乘 0.0625。如果是负温度先取反加一得到原码再乘系数最后加负号。这个逻辑我建议单独写一个测试函数用已知温度点验证比如室温下跟标准温度计对比误差在 ±0.5℃ 以内就算正常。// 伪代码示意实际寄存器名以数据手册为准 unsigned int raw (read_byte() 8) | read_byte(); raw raw 4; float temp; if (raw 0x0800) { raw (~raw 1) 0x0FFF; temp -(raw * 0.0625); } else { temp raw * 0.0625; }3.3 串口帧格式与远程解析远程帧我习惯用固定格式比如“$T,25.3,OK\r\n”$ 是帧头T 表示温度后面是数值和状态\r\n 是帧尾。上位机收到之后按逗号分割就能解析。如果环境干扰大可以在帧尾加校验和比如把前面所有字符异或一下附在后面。校验和不用太复杂能挡住大部分偶发错误就行。发送频率我一般设成 1Hz跟本地显示同步。如果远程端需要更快可以提到 5Hz但要注意串口带宽和上位机处理能力。9600bps 下一帧十几个字节1Hz 占用率很低5Hz 也完全撑得住。再高就没必要了温度本身变化就慢。注意串口发送和 I2C 读取如果都在主循环里轮询要注意时序配合。I2C 读一次大概几毫秒串口发一帧也就几毫秒主循环周期控制在几十毫秒以内1Hz 的刷新率完全没问题。但如果加了液晶刷新液晶的忙等待可能拖到几十毫秒这时候要么用定时器中断分时处理要么降低液晶刷新率。3.4 实际运行记录与数据对比我在一个模拟环境里连续跑了 48 小时本地液晶显示和远程串口数据每秒对比一次偏差始终在 0.1℃ 以内说明两路数据源是一致的。环境温度从 18℃ 升到 32℃ 再降回来传感器跟随性很好没有明显滞后。唯一一次异常是旁边有人用对讲机靠近串口出现了一帧乱码但下一帧就恢复了说明帧头帧尾的容错设计起了作用。功耗方面PIC18F4620 跑 8MHz 晶振加上传感器和液晶整体电流在 20mA 左右。如果去掉液晶能降到 10mA 以内。对于市电供电的 HVAC 控制器来说这个功耗完全不是问题。如果是电池供电那就得考虑休眠策略了那是另一个话题。4. 常见问题与排查技巧实录4.1 I2C 通信失败排查I2C 调不通是最常见的问题我一般按这个顺序查先看上拉电阻有没有、阻值对不对再看地址有没有搞错PJ85718DM 的地址跟 A0-A2 引脚有关有人原理图上接地了实际焊接时虚焊地址就变了然后看时序用逻辑分析仪抓波形START、地址、ACK 每一位都要对最后看电源传感器供电不足时也会不响应。还有一种情况是总线被拉死SDA 一直低。这时候可以尝试发 9 个时钟脉冲让从机释放总线或者直接断电重启。如果经常拉死就要检查是不是有从机在异常状态下钳住了总线必要时加总线缓冲器隔离。4.2 温度读数跳变或偏差大读数跳变一般是电源噪声或者地线干扰。PJ85718DM 的电源引脚旁边要加 0.1μF 去耦电容地线尽量短。如果传感器离 MCU 较远I2C 走线要尽量短或者降低速率。偏差大则可能是换算公式错了尤其是负温度段一定要用冰水混合物验证零点。还有一种隐蔽的情况是自发热。传感器本身功耗很低但如果旁边有发热元件比如稳压芯片读数就会偏高。布局的时候传感器要远离热源必要时开槽隔离。4.3 串口数据丢失或乱码串口丢数据先查波特率。PIC18F4620 的 EUSART 波特率计算公式是SPBRG (Fosc / (16 × Baud)) - 18MHz 晶振下9600bps 对应 SPBRG 5119200bps 对应 25。如果晶振不是标准值误差会累积导致丢帧。另外发送和接收的中断优先级要配好如果接收中断被其他中断长时间阻塞也会丢数据。乱码则多半是电平不匹配或者地线没共。TTL 串口两端要共地RS-485 要接终端电阻。如果线缆太长还要考虑加隔离。问题现象可能原因排查方法解决措施I2C 无响应上拉缺失、地址错、供电不足查电阻、查地址引脚、量电压补上拉、改地址、换电源温度跳变电源噪声、地线干扰示波器看电源纹波加去耦、缩短地线温度偏差换算错误、自发热冰水验证、红外测温修正公式、远离热源串口丢帧波特率误差、中断阻塞查 SPBRG、看中断时序调波特率、优化中断串口乱码电平不匹配、未共地量电平、查地线加电平转换、共地4.4 长期运行稳定性经验长期跑下来我发现最影响稳定性的不是代码而是硬件细节。接插件氧化、焊点开裂、电容老化这些都会在几个月后暴露出来。所以我在做产品化的时候I2C 走线尽量短、尽量粗接插件选镀金的电容选耐温 105℃ 的。软件上加了看门狗主循环超时就复位复位后重新初始化传感器和串口这样即使偶发异常也能自恢复。还有一点是传感器的校准周期。PJ85718DM 出厂校准已经不错但如果是高精度场景建议一年标定一次。标定方法很简单用标准温度计对比记录偏差在软件里加补偿值就行。补偿值可以存在 EEPROM 里掉电不丢。5. 方案扩展与个人体会这套方案跑通之后扩展性其实很好。如果想加湿度可以挂一个同系列的湿度传感器I2C 地址不冲突就行。如果想加多个温度点PJ85718DM 支持地址跳线一条总线上挂四五个没问题PIC18F4620 的 I2C 主机模式轮询一遍也就几十毫秒。远程端如果不想用上位机可以加一个 Wi-Fi 或以太网模块把串口数据转成网络协议这样手机也能看。我在实际使用中的体会是嵌入式温度监测这件事难点不在写代码而在把硬件、时序、协议、抗干扰这些环节都考虑到。任何一个环节偷懒后面都会以奇怪的方式暴露出来。PJ85718DM 加 PIC18F4620 这套组合胜在成熟、资料多、成本可控适合中小规模的环境监测项目。如果你也在做类似的东西建议先把 I2C 读通、串口发通再往上叠功能一步一步来比一上来就搭大框架要稳得多。最后分享一个小技巧调试的时候在串口帧里加一个运行时间戳比如上电后的秒数。这样一旦出现异常你能知道是运行多久之后出的问题是上电初始化的问题还是长期运行的问题排查方向会清晰很多。这个习惯我保持了好几年帮我省了不少时间。