1. 方案选型与整体设计思路1.1 为什么是 PJ85718DM 加 PIC18F86J16 这个组合拿到这个项目标题的时候我第一反应是这又是一套非常典型的“传感器 MCU”的嵌入式温度监测方案但仔细看芯片型号组合其实挺讲究的。PJ85718DM 是一款数字温度传感器芯片测温范围、精度和接口形式都比较适合工业级和HVAC暖通空调场景而 PIC18F86J16 是微芯旗下的一颗 8 位单片机最核心的特点就是内部集成了以太网控制器也就是 MAC 层硬件配合外部物理层芯片就能直接联网。为什么强调“本地与远程”两个词?因为很多嵌入式温度采集项目只做了本地显示或者本地存储一旦要远程查看数据就得额外挂一个通信模块WiFi、蓝牙、4G或者以太网。而这个方案里PIC18F86J16 本身就有以太网接口资源做远程监测时不需要再外接昂贵的协议转换器一颗芯片既负责采集本地温度又负责把数据发到远端整个 BOM 成本可以控制得很紧。我帮某公司做过一个挂机盘管温度监测的小项目最开始用的方案是“普通 MCU 串口转以太网模块”模块单价就要几十块而且调试的时候经常遇到模块固件不稳定、协议栈不透明的问题。后来换成 PIC18F86J16 这类内置以太网控制器的 MCU整体物料成本降了软件层面也有了完全可控的 TCP/IP 协议栈实现排查起问题来心里踏实很多。1.2 本地与远程温度监测的场景到底有哪些差异从应用场景拆解这个方案覆盖了两大类需求。第一类是嵌入式设备自身的温度监测。比如功率电源模块、电机控制器、工业控制板这类场景关注的是设备内部温度是否异常测温点往往靠近功率器件对传感器的响应速度和精度要求比较高而且要求传感器体积小、走线方便。PJ85718DM 这类数字温度传感器可以直接贴在 PCB 上也可以用小封装放到功率管旁边MCU 通过 I2C 或 SPI 就能读回温度值不需要额外的信号调理电路。第二类是 HVAC 场景的室温、风管温度、水管温度监测。空调箱的进出风温度、冷冻水供回水温度、机房环境温度这些测温点的特点是距离控制器比较远而且现场可能存在强电干扰。PIC18F86J16 做控制器通过总线把多个温度传感器挂上去每个传感器分配一个地址定期轮询采集数据除了本地显示还可以借助以太网口把数据上报给楼宇自控系统。两类场景的差异在于嵌入式设备监测更看重实时性和精度HVAC 场景更看重布线距离和抗干扰能力。好在 PJ85718DM 的数字接口输出在一定程度上解决了长线传输的干扰问题数字信号不会像模拟信号那样随着线长衰减太多这在工程实施上省了不少心。2. 硬件设计中的核心细节解析2.1 PJ85718DM 传感器选型的技术考量PJ85718DM 这款传感器我在之前的好几个项目里都做过替代测试它的基本定位是宽温区数字温度传感器常见的技术指标大致覆盖 -55℃ 到 125℃ 的测温范围分辨率能做到 0.0625℃ 这个级别部分型号的精度在 ±0.5℃ 以内。这样的参数放在 HVAC 环境监测里绰绰有余放在工业嵌入式设备里做功率器件温度监测也基本够用。选它还有一个重要原因是接口简单。它支持标准的 I2C 总线接口MCU 只需要两根线SCL、SDA就能挂载多个传感器节点。假如你有 8 个测温点只需要把 8 颗传感器挂到同一条 I2C 总线上通过地址位区分设备。I2C 总线的抗干扰能力虽然比不上 RS485但在 PCB 板上或者短距离布线1米以内时是完全没有问题的。对比我以往用过的热敏电阻方案PJ85718DM 省掉了分压电阻、滤波电容、运放和 ADC 采样通道所有温度数据直接通过 I2C 读出来误差来源少了一大截。热敏电阻是模拟量输出受电源波动影响明显需要软件做查表和线性化处理数字传感器把这些事都做完了数据手册里给了现成的寄存器格式软件上“读—转换—显示”三步就完成了。注意如果你要把传感器放在长距离线缆末端比如风管里尽量用屏蔽双绞线I2C 的 SCL 和 SDA 分别用一对线信号地和屏蔽层在控制器端单点接地能显著减少变频器等强干扰源带来的误码。2.2 PIC18F86J16 的资源分配与引脚规划PIC18F86J16 在微芯产品线里属于带以太网功能的 8 位 MCU内部程序存储器和 RAM 在 8 位机里算是比较充裕的可以跑一个精简的 TCP/IP 协议栈。它的以太网 MAC 层是硬件实现的MCU 只需要做协议栈软件和底层驱动配合一颗外部 PHY 芯片比如常用的 LAN8720加上网络变压器和 RJ45 座就能实现标准 10/100M 以太网接口。做硬件规划的时候我建议把引脚功能提前用表格列清楚尤其是 I2C 引脚、以太网接口引脚和调试串口引脚避免后期画 PCB 时出现资源冲突。我当时做的一个项目引脚规划是这样的功能模块引脚/外设备注传感器 I2CSCL1/SDA1接 PJ85718DM上拉电阻 4.7kΩ本地调试串口UART1 TX/RX输出温度日志和调试信息以太网 PHYRMII 接口接 LAN872050MHz 参考时钟状态指示2 个 GPIO分别指示网络状态、传感器状态报警输出1 个 GPIO驱动继电器或 LED 蜂鸣器在实际操作中RMII简化媒体独立接口的时钟线布线要特别注意它需要 50MHz 的参考时钟源如果共享晶振要确认 PHY 能接受 MCU 输出的时钟并且走线距离尽量短避免反射导致通信不稳定。我第一次画板的时候就是吃了这个亏RMII 时钟线走了太长结果网络时通时断后来缩短走线并加串阻才稳定。2.3 电源与抗干扰设计的几条实战经验嵌入式温度采集系统的电源设计看起来简单实际上坑最多。传感器本身功耗不高但系统一旦联网以太网 PHY 芯片在发送数据时电流会有明显波动如果电源设计余量不足温度读数可能周期性跳动。我的做法是分两级处理MCU 和数字逻辑统一用 3.3V 供电选用 LDO 稳压芯片输入加钽电容和陶瓷电容组合滤波。以太网 PHY 的模拟电源和数字电源尽量分开走线虽然很多 PHY 芯片内部已经做了隔离但外部 PCB 上还是要注意不要共用一小段很细的走线。PJ85718DM 的供电建议直接挂在 3.3V 数字电源上并在 VDD 和 GND 之间加一个 100nF 的陶瓷电容位置尽可能靠近传感器引脚。I2C 总线上拉电阻的取值也有讲究标准模式下 4.7kΩ 没问题但如果总线挂载设备多、走线较长可以换成 2.2kΩ提升驱动能力。不过下拉太强会增加功耗这个平衡要自己测试。3. 固件开发与核心逻辑实现3.1 用状态机架构管理温度采集与网络上报写这类带网络功能的嵌入式固件我最怕的就是把所有逻辑混在一个大 while 循环里温度采集写一段、网络处理再写一段最后逻辑复杂到不敢动代码。正确的做法是用状态机架构把整个系统拆成几个独立运行的任务并用一个简单的调度器来轮询。我从这个项目中提炼出来的核心状态机分三层采集层定时触发 I2C 读取 PJ85718DM把温度数据存到全局变量通信层处理网络协议栈事件维护 TCP 连接或 UDP 会话应用层按需向上报端发送数据帧处理远程控制指令。比如温度采集周期设定为 1 秒以太网数据上报周期可以设定为 5 秒或 10 秒这两者之间解耦。因为网络上报如果失败不应该影响本地温度采集和显示反过来如果 I2C 总线忙也不应该阻塞网络协议栈的轮询。这种解耦做法在工程上非常实用尤其是现场环境不稳定的时候不至于因为一个传感器故障导致整个系统“卡死”。下面是一段简化版的调度逻辑示例我用伪 C 代码写出来方便说明结构while (1) { // 任务1周期采集温度每1秒执行 if (flag_1s) { read_temperature_all(); flag_1s 0; } // 任务2处理网络协议栈尽量尽快执行 network_poll(); // 任务3周期上报数据每5秒执行 if (flag_5s) { send_temperature_report(); flag_5s 0; } // 任务4处理调试串口指令 uart_debug_poll(); }实际项目中网络协议栈的轮询频率决定了通信实时性需要保证在最短时间内处理完 PHY 中断和 TCP 定时器。如果主循环里其他任务占用了太多时间网络容易出现异常。调试的时候我习惯在关键任务入口翻转一个 GPIO 引脚用示波器量各任务的执行时间占比定位瓶颈。3.2 与 PJ85718DM 通信的寄存器配置和代码要点温度传感器通信的关键在于初始化和数据读取。PJ85718DM 的 I2C 地址通常由 AD0/AD1 引脚决定设计硬件时可以预留跳线电阻方便后期调整地址。软件初始化的主要动作是配置传感器的分辨率、转换周期和报警阈值。以 12 位分辨率为例典型转换时间大约在几百毫秒量级。读取流程是先发送传感器地址加写位写入读取温度寄存器的命令字然后重新发送地址加读位连续读两个字节把高字节和低字节拼起来得到原始温度值再乘以分辨率对应的系数得到实际温度。下面是一段基于标准 I2C 驱动的读取函数示例说明关键流程uint8_t sensor_read_temperature(float *temp) { uint8_t buf[2]; uint8_t reg 0x00; // 以数据手册为准的温度寄存器地址 i2c_start(); if (i2c_write(SENSOR_ADDR 1 | 0) 0) { i2c_write(reg); i2c_restart(); if (i2c_write(SENSOR_ADDR 1 | 1) 0) { buf[0] i2c_read(ACK); buf[1] i2c_read(NACK); i2c_stop(); int16_t raw (buf[0] 8) | buf[1]; raw 4; // 去掉低4位无效位 *temp raw * 0.0625f; return SUCCESS; } } i2c_stop(); return ERROR; }实际代码里的细节比这个多比如要处理 I2C 忙检测、时钟拉低仲裁和 NACK 重试等但核心就是这套流程。生产环境里传感器偶尔会因为总线上干扰出现通信错误我的习惯是连续读三次取中间值或者取多数值作为最终结果这种软件滤波方式在很多场合比单纯靠硬件滤波更实用。3.3 本地显示与远程上报的数据格式设计温度数据读回来了怎么呈现也很关键。本地显示一般有两种做法用数码管或者段码 LCD 显示当前温度用带字库的工业串口屏做一个简单的监控界面。我自己倾向于后者因为 MCU 把温度值通过串口发给屏屏上通过简单的脚本指令就能显示不需要为显示功能消耗大量 MCU 资源。如果是简单的指示灯方案也可以把温度分成几个区间正常、偏高、报警用不同颜色的 LED 指示。远程上报的数据格式我做的是 JSON 格式的心跳数据包每 5 秒发送一次{ dev_id: TEMP01, timestamp: 1794931200, temp_local: 23.5, temp_remote: 24.1, alarm: 0 }选择 JSON 而不是自定义二进制协议最大的好处是调试方便上位机用现成的工具就能解析浏览器和 Node-RED 这类工具直接认不需要为每个项目单独写解析器。代价是数据量稍大但在以太网链路上这完全不是瓶颈。3.4 远程通信中的 TCP 与 UDP 取舍说到远程通信总有人纠结用 TCP 还是 UDP。我的经验是分场景选择如果数据只是周期性上报比如温度记录、对丢包不敏感用 UDP 更轻量服务器端甚至不需要维护连接状态异常重启后能自动恢复通信。如果还涉及远程控制指令下发比如设定温度阈值、远程开关机那就必须用 TCP因为指令不能丢丢了要重传。实际做 HVAC 控制器项目时我两种都遇到过。有一个场景是空调机房温度监测现场网络环境差TCP 连接频繁断开重连后来干脆改成 UDP 上报数据采集端不丢数据服务器端如果收到乱序或缺失的包直接依赖时间戳重排就行。温度数据本身有惯性缺一两秒数据对分析没有本质影响用 UDP 反而省心。但这个项目中我最终采用了“双通道”设计传感器状态和温度数据走 UDP 上报控制指令走 TCP 下发。这样既保证了控制指令的实时性和可靠性又让海量的温度数据不至于压垮 TCP 连接维护。4. 完整实操过程与关键环节实现4.1 硬件连接传感器、MCU 到以太网的搭建步骤如果你要自己搭一套最小验证方案硬件连接可以按下面这几个步骤来焊接 PIC18F86J16 最小系统板包括电源、晶振、复位电路和编程调试接口焊接或外接 PJ85718DM 传感器模块连接 SCL、SDA、VDD、GND 四根线注意上拉电阻焊接以太网 PHY 芯片如 LAN8720、网络变压器和 RJ45 座RMII 接口按数据手册连接到 MCU连接调试串口方便输出日志通电后用万用表检查各点电压确认 3.3V 正常没有短路。以太网部分我第一次调试的时候浪费了整整两天最后发现是 PHY 芯片的复位引脚没有拉高芯片一直处于复位状态网络自然不通。后来习惯在所有硬件连接完成后用示波器检查 PHY 的时钟输出和复位引脚状态再开始写软件排错效率高很多。4.2 固件初始化流程与网络协议栈移植PIC18F86J16 这类带以太网 MAC 的 MCU官方其实提供了相对成熟的 TCP/IP 协议栈源码包含 TCP、UDP、ARP、ICMP 等基础协议。开发上省了不少事但移植时要注意协议栈的配置。关键配置项包括本地 IP 地址、子网掩码、默认网关MAC 地址TCP/UDP 端口号协议栈缓冲区大小。缓冲区大小是个性能瓶颈点。TCP 收发缓冲区如果太小一次发送稍大的数据包就会被截断但如果太大8 位 MCU 的 RAM 根本吃不消。我的经验是对于温度采集这种小数据包应用TCP 缓冲区 256 字节就足够UDP 接收缓冲可以适当放大到 512 字节避免大包被丢弃。下面是一个协议栈初始化的简化示意void network_init(void) { // 配置MAC地址和IP eth_init_macaddr(0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E); ip_set_address(192, 168, 1, 100); ip_set_mask(255, 255, 255, 0); ip_set_gateway(192, 168, 1, 1); // 注册UDP和TCP服务端口 udp_open(5000); // 温度上报UDP端口 tcp_open(5001); // 控制指令TCP端口 // 启动协议栈调度 eth_enable(); }初始化完成后主循环里持续调用网络轮询函数协议栈就会自动处理 ARP 请求、回复 Ping 报文、处理 TCP 连接和数据收发。调试时先用 PC 端 ping 一下板子的 IP如果能通说明底层链路已经正常再测试 UDP 和 TCP。4.3 温度数据轮询与远程指令下发的联调联调是整个项目最耗时也最有意思的阶段。我习惯先做三件事本地用调试串口周期性打印温度值确认传感器读数稳定用 PC 上的网络调试工具监听 UDP 端口确认板子上报数据能到达 PC用网络调试工具给板子发一条 TCP 指令确认板子能正确解析并执行。某次联调过程中我发现温度上报数据偶尔会出现连续 255 或者异常跳变排查后定位到 I2C 总线上有脉冲干扰传感器在转换过程中被干扰导致读取错误。解决办法是在程序里做了数据合理性判断超出合理范围比如 -20℃ 到 80℃就丢弃这帧数据并用上一次的正常值代替。这虽然是一个简单技巧但在现场应用里非常管用。远程指令下发的格式我是用简单文本协议设计的比如SET_THRESHOLD:45.0 REBOOT STATUS这种文本指令比二进制指令好调试上位机甚至可以用串口终端直接发不需要专门写客户端。MCU 解析时按行读取匹配字符串前缀再处理后面的参数。文本帧长度短控制逻辑也直观很适合 MCU 这种资源受限的平台。5. 常见问题与排查技巧实录现象可能原因排查方向I2C 读取温度偶尔失败上拉电阻过大、总线干扰、地址错误检查上拉阻值、用示波器看 SCL/SDA 波形温度读数偏高或偏低传感器校准偏移、靠近热源、封装散热差异在标准温度源中校准检查布线以太网 Ping 不通PHY 复位、RMII 时钟、网络变压器焊接量 PHY 复位脚、时钟输出、检查 RJ45 指示灯TCP 连接频繁断开缓冲区不足、协议栈看门狗超时加大缓冲区调整协议栈超时参数本地显示正常但远程无数据UDP 数据未出网、端口被防火墙拦截先在本机测试再检查服务器监听端口系统长时间运行后通信死掉内存泄漏或协议栈状态异常定期重启 TCP 连接或增加看门狗复位逻辑这些坑里最值得展开说的是“系统长时间运行后通信死掉”的问题。8 位 MCU 的 RAM 资源有限TCP/IP 协议栈在极端情况下可能出现内存碎片或者状态机错误表现就是网络看起来正常但数据发不出去。我的做法是在协议栈里加一个定时计数器超过一定时间没有通信动作就强制关闭连接重新打开同时在主循环里喂独立看门狗防止真正死机时系统没有任何恢复手段。5.1 传感器布线对抗干扰的影响我在 HVAC 项目里踩过一次很大的坑传感器线缆沿着强电电缆桥架走了一段结果变频器一启动温度数据就开始乱跳。后来把传感器线缆单独穿管并远离变频器输出电缆干扰症