1. 从应用需求到器件选型,为什么这套组合适合温度监测
接到一个嵌入式温度监测项目时,最先要想清楚的不是代码怎么写,而是“测哪里、怎么回传、可靠性要求有多高”。我这次要讲的这套方案,核心是用一颗自带以太网的8位MCU(PIC18F87J60)搭配一颗双通道数字温度传感器(PJ85718DM),同时抓取本地温度和远程温度,再通过网络把数据送到监控端。它在嵌入式和 HVAC 场景里特别实用,因为这类项目对器件数量、成本、走线长度都很敏感。
先说为什么选 PIC18F87J60。这颗芯片最突出的地方是内置了以太网 MAC 和 PHY,外部只需要一个带隔离变压器的 RJ45 座子,再加几颗阻容就能把网络接口跑起来。相比另外挂一颗以太网控制器的方案,省掉了 SPI 转以太网桥接芯片,BOM 成本能降下来,布板面积也小不少。对于 HVAC 温控面板、风机盘管控制器这种外观尺寸受限的产品来说,少一颗芯片就是实打实的优势。
PJ85718DM 这颗传感器有意思的点在于它本身就有两个测温通道。本地通道测量芯片自身结温,可以贴在控制板上测环境温度;远程通道配合一颗二极管或三极管接法的测温元件,能测到几米外的关键部位温度,比如风管里的回风温度、机柜散热片的表面温度。普通做法是装两颗独立传感器,但汇流排上两颗传感器的校准偏差很难完全一致,用一颗双通道传感器就能从根本上消除通道间偏差,对 HVAC 这种需要相对稳定温差读数的场合尤其友好。
这套组合还有一个很现实的考量:PIC18F87J60 在 8 位机里算得上资源丰富,几 KB 的内存和几十 KB 的程序空间,跑一个精简的 TCP/IP 协议栈再加状态机轮询逻辑绰绰有余。整个系统无操作系统、无文件系统,死锁风险小,断电重启后几秒内就能恢复采集和上报,对设备间长期无人值守的运行场景非常稳。
适用对象主要分两类:一类是嵌入式工程师,想快速搭建一个带网络上报功能的温度采集节点;另一类是暖通、楼宇自控项目的实施人员,需要在不改动现有控制器的前提下,加装一路独立的温度监测点。这套方案不需要昂贵的外设,不需要复杂的实时操作系统,一块开发板、一个网口、一颗传感器就能跑通全流程。
2. 硬件连接与接口设计,把传感器和网络接口接对是关键
2.1 PJ85718DM 的引脚和连接要点
PJ85718DM 的封装不大,常见的是 8 脚小外形封装,引脚功能基本分四组:电源、I2C 接口、远程测温输入、报警输出和地址选择。典型接法如下表所示:
| 引脚功能 | 连接对象 | 注意事项 |
|---|---|---|
| VDD | 3.3V 电源 | 就近放 100nF 去耦电容 |
| GND | 地 | 单点接地,避免与功率地共用回流路径 |
| SDA / SCL | PIC18F87J60 的 I2C 引脚 | 各接 4.7kΩ 上拉电阻到 VDD |
| D+ / D- | 远程测温二极管 | 差分走线,加滤波电容 |
| ALERT | PIC 的输入引脚 | 配置为开漏输出,接上拉电阻 |
| A0 / A1 | 地址选择 | 接地或接 VDD 决定 I2C 地址 |
I2C 上拉电阻的选择值得单独说。标准模式下上拉电阻可以取 10kΩ,但 PJ85718DM 的转换时间不长,实际读取频率如果达到几十赫兹,建议用 4.7kΩ。上拉电阻太大会导致上升沿变缓,在走线稍长的工况下出现数据错乱;太小则会拉低电平裕量,影响输入低电平判断。我在项目中先用 10kΩ,后来发现通信偶尔出现异常,换成了 4.7kΩ 后问题消失。
远程测温通道的接法比很多人想象的要讲究一些。PJ85718DM 的 D+ 和 D- 引脚内部是恒流源驱动,外部接一个二极管结才能形成测温回路。工程上最常见的做法是使用小信号三极管的基极-发射极结来代替单独的二极管,因为三极管的 结压降特性更稳定,温度系数一致性也更好。
接线时要注意 D+ 和 D- 是差分信号,虽然传感器内部有抗干扰设计,但外部走线最好还是相邻并行走,不要跨分割区,不要在两根线之间走其他数字信号。我见过有人在 D+ 和 D- 之间加了 10nF 的电容来滤波,结果温度读数在高温段偏差加大,这是因为过大的电容会改变恒流源的瞬态建立过程,导致阻抗测量不准确。合理的做法是在 D+ 和 D- 到地之间各接一个 100pF 左右的瓷片电容,而不是在两根线之间直接跨接大电容。
远程二极管到传感器之间的线长在 1 米以内通常没问题,超过 2 米就要考虑用屏蔽双绞线,而且传感器的恒流源驱动能力有限,线太长压降也会影响测量精度。HVAC 项目里如果要把测温探头放到风管深处,线缆长度往往有 1 到 3 米,这时候优先选屏蔽线,屏蔽层单端接地。
2.2 PIC18F87J60 以太网接口电路
PIC18F87J60 的网络接口电路主要包括三部分:振荡器、磁隔离变压器和 RJ45 座。芯片内部集成了以太网 PHY,但物理层隔离变压器不能省,它负责隔离直流、匹配阻抗和抑制共模干扰。
振荡器部分建议使用 25MHz 晶振,因为以太网 PHY 需要精确的 25MHz 参考时钟来产生 100BASE-TX 所需的时钟。晶振的两个负载电容要按晶振手册标称值选取,常见是 18pF 到 33pF。这里有个容易踩的坑:有些低成本开发板为了省元件,用内部振荡器跑以太网,结果是链路能通但丢包率很高。PIC18F87J60 虽然也可以启用内部振荡器,但 PHY 对时钟精度要求摆在那里,量产项目还是老老实实用外部晶振。
变压器的接法要留意中心抽头。大多数带变压器的 RJ45 座会引出中心抽头引脚,需要按座子手册接上合适的偏置电压和滤波电容。接错中心抽头会导致信号幅度异常,表现为能识别到 10M 链路但 100M 链路起不来。
网络接口的差分信号线要等长、差分阻抗控制到 100 欧姆。4 层板走线相对好控制,2 层板就只能靠尽量短的走线和合理的铺地来弥补。实际项目里如果布线空间紧张,宁可让 RJ45 座靠近芯片放置,把走线缩短,也不要绕远路去迁就结构位置。
2.3 电源与复位电路设计
PIC18F87J60 的工作电压范围是 2.0V 到 3.6V,PJ85718DM 同样支持 3.3V 供电,所以整个系统单电源 3.3V 即可。每颗芯片的电源引脚都要配一个 100nF 电容,此外在总电源入口放一个 10µF 左右的钽电容或陶瓷电容,用来应对以太网发送数据时的瞬态电流。
HVAC 设备通常在工业环境里运行,电源质量不一定理想。建议在电源输入端加一个简单的 LC 滤波或者至少一个共模电感,防止电机启停产生的干扰通过电源线耦合进 MCU。我做过的一个项目里,压缩机的启停会导致单片机偶发复位,排查下来是电源入口处少了共模电感,加上后就没再出现过。
复位电路用 RC 复位即可,PIC18F87J60 的 MCLR 引脚上拉一个 10kΩ 电阻再接 0.1µF 电容到地,同时在 MCLR 和地之间并联一个 1N4148 二极管到 VDD,可以实现掉电快速放电,防止掉电后电压在复位阈值附近徘徊导致的反复复位。
3. 固件架构与温度采集逻辑,从 I2C 读到网络上报的全链路
3.1 固件的整体结构
这套系统的固件不需要上实时操作系统,一个前后台结构就能搞定。前台是主循环,负责周期性地读取温度、更新网络状态;后台是定时器中断,负责维护 TCP 连接的超时计时和协议栈的定时任务。
固件大致分成四层:
- 驱动层:I2C 读写函数、以太网 PHY 初始化、定时器驱动
- 协议层:轻量级 TCP/IP 协议栈,处理 ARP、IP、ICMP、TCP
- 应用层:温度采集任务、Modbus TCP 服务器、HTTP 响应
- 配置层:保存本地温度告警阈值、远程温度告警阈值、设备 IP 等参数
温度采集建议放到一个固定时间间隔的任务里执行,比如每 500ms 读取一次。以太网协议的收发放到主循环里非阻塞地跑,这样可以避免在发送数据时长时间占用 CPU,影响传感器读取的实时性。
我第一次做这个系统时把温度读取放在以太网接收中断里,结果高负载的 UDP 广播风暴直接把温度采集卡死。后来改成所有传感器读取都只在主循环任务里做,中断只置标志位,才算彻底解决。
3.2 I2C 读取温度的详细时序
PJ85718DM 的 I2C 接口支持标准模式和快速模式,建议使用 100kHz 或 400kHz。芯片内部有本地和远程两个温度寄存器,读取流程如下:
第一步,写入命令指针寄存器,选择要读取的温度通道。命令指针寄存器地址是 0x01 还是 0x02,不同厂家的定义有差异,我这里按常见映射来描述。实际操作时以 PJ85718DM 的数据手册为准。
第二步,等待转换完成。PJ85718DM 持续转换模式下转换周期大约在 100ms 量级,读取寄存器并不需要每次都等待,但如果刚上电就读,寄存器内容可能是未初始化的旧值,所以第一次读取前延时 200ms 比较稳妥。
第三步,读取寄存器的高字节和低字节。读到的 11 位或 12 位数据需要换算成实际温度值。以 12 位分辨率为例,数据位左对齐,取高 8 位作为整数部分,低 4 位为小数,最小刻度是 0.0625°C。
实际代码里有个细节:I2C 读多个字节时,最后一个字节要发送 NACK,否则芯片会认为主机还要继续读,导致总线状态异常。下面给一段示意代码:
uint16_t read_temperature(uint8_t reg_addr) { uint8_t buf[2]; i2c_start(); i2c_write(DEVICE_ADDR << 1); // 写地址 i2c_write(reg_addr); // 选择寄存器 i2c_restart(); i2c_write((DEVICE_ADDR << 1) | 1); // 读地址 buf[0] = i2c_read_byte(ACK); buf[1] = i2c_read_byte(NACK); i2c_stop(); return ((uint16_t)buf[0] << 8) | buf[1]; }读取完成后,判断温度的符号位。PJ85718DM 的温度数据用二进制补码表示,最高位是符号位。如果最高位为 1,说明是负温,需要取补码换算成负数。HVAC 的应用场景虽然多在 0°C 以上,但冷冻水系统在调试阶段或冬季停机时完全可能到零下,所以负温处理逻辑不能省。
3.3 本地通道与远程通道的差异处理
本地通道测的是传感器芯片自身的结温,响应快但受 PCB 上热源影响大。如果传感器贴在控制板上,旁边有大功率电阻或者 MCU 附近,本地读数会比环境温度偏高几度。要测环境温度时,传感器应该尽量远离发热源,或者用一小段导热垫把芯片和机壳连接起来,让热量更均匀。
远程通道测的是外部二极管结温,热阻路径长,响应慢,但它更贴近真实测量目标。在 HVAC 应用里,通常用本地温度做控制回路的快速反馈,用远程温度做风管或进出水管路的温度监测,两者各司其职。
远程通道还有一个特性是它对串联电阻敏感。远程二极管到 D+ / D- 引脚的引线电阻会导致读数偏高,每 100 欧姆的串联电阻大约会引入不到 1°C 的偏差。批量生产时,线缆电阻的一致性如果控制不好,产品间的温度读数一致性也会受影响。
校准的办法是在固件里留一个偏移量配置项。出厂时测量标准温度源下的读数偏差,把修正值写入 EEPROM。这样即使是不同批次的线缆,也能通过校准把误差拉回到允许范围内。
4. 网络协议与远程上报,从 Modbus TCP 到网页数据展示
4.1 用哪个协议上报,怎么选
温度数据要送出去,协议栈一般有几种选择:Modbus TCP、HTTP、MQTT 或者干脆是自定义 TCP 报文。HVAC 和楼宇自控项目里最经典的是 Modbus TCP,因为现有上位机、触摸屏和 BA 系统普遍支持这个协议。嵌入式设备模拟 Modbus 服务器端,上位机作为主站轮询寄存器即可。
Modbus TCP 的报文结构比串口 Modbus 多了一个 MBAP 报文头,一共 6 个字节,包括事务处理标识符、协议标识符、长度和单元标识符。功能码用 0x03 读保持寄存器,把温度值以带符号整数或者扩大 10 倍后的整数形式放进两个连续的寄存器里。
温度值整形是个容易出问题的地方。直接把浮点数扔进 Modbus 寄存器是不可行的,因为 Modbus 寄存器是 16 位整数。常用的做法是温度值乘以 10 或乘以 100 后取整,负温度用补码表示。比如 25.3°C 存为 253,-4.7°C 则存为 -47 的补码形式。上位机解析时需要除以相应的倍数,并正确处理补码转原码。
还要考虑寄存器地址分配的一致性。我习惯把寄存器地址写进文档,标注通道名称、数据类型、缩放系数,避免后期联调时两边对不上。调试时用现成的 Modbus 主站工具读取几个地址验证,比在设备端反复看日志要快得多。
4.2 内置网页的实现思路
Modbus TCP 适合工业系统集成,但现场调试人员更喜欢的其实是网页界面。PIC18F87J60 的资源规模不足以跑完整的 Web 服务,但输出一个纯静态的 HTML 页面完全没有问题,因为页面数据量不大,几十个字节到一个KB 量级。
实现思路是在固件里把 HTML 页面模板以数组形式存储在程序空间,请求到达时把页面中的占位符替换成实际温度值再响应给浏览器。页面上显示两个温度值和一个更新时间戳,再写一个简单的自动刷新 meta 标签,浏览器每隔几秒重新请求一次即可。
这里要注意 TCP 连接资源非常有限。PIC18F87J60 的协议栈最多支持几个并发连接,如果浏览器打开多个标签页同时请求,老连接不释放会导致新请求超时。应对办法是把连接空闲超时设短一些,比如 10 秒,并配合 HTTP 响应头里的 Connection: close 通知浏览器关闭连接。
4.3 裸机环境下实现 TCP/IP 的注意事项
在没有操作系统的情况下,TCP/IP 协议栈要避免占用过多内存。以下是几个实际项目中验证过的小技巧:
第一,ARP 缓存表只需要保留最近通信过的几个 IP 条目即可。每次发送数据前先查 ARP 缓存,没有对应条目就发 ARP 请求等待应答。如果目标设备频繁更换,缓存条目会挤占内存,所以定期清理超过一分钟未使用的条目。
第二,TCP 协议的 ACK 处理要尽量及时。PIC18F87J60 收到 TCP 数据段后,如果长时间不回复 ACK,对端会进入重传等待,加大网络负担。在主循环里把网络处理函数的调用间隔控制在几毫秒内,就能保证 ACK 及时闪现。
第三,CRC 校验必须做。以太网帧本身有 CRC 校验,但 PHY 接收时已经校验过了,通过与否由 FIFO 状态字体现。如果忽略这个状态字,坏帧会被当成有效帧送入协议栈,解析出错误的数据,温度监控上就是偶发的大跳变。我在协议栈的以太网接收入口处加了好几层检查,掉帧率降到了可接受的范围。
5. HVAC 场景下的应用逻辑与联动控制
5.1 本地温度参与风机盘管启停控制
HVAC 项目里最典型的需求是:本地温度作为风机盘管的启停控制依据,远程温度作为回风温度监测、判断过滤网是否堵塞或盘管换热效果是否下降。
比如一个风机盘管温控面板,PJ85718DM 的本地通道安装在面板 PCB 上,直接测量室内空气温度;远程通道通过屏蔽线延伸到盘管回风口,测量实际回风温度。房间设定温度 25°C,回风温度 27°C 时系统判定处于制冷状态且热负荷较大,触发全速通风;回风温度降到 24°C 时切到低速挡。这是典型的“本地设定温度 + 回风温度”双输入控制逻辑。
固件需要处理滞后性问题。直接用单点温度控制继电器切换会产生一个问题:温度在阈值附近波动时,继电器频繁吸合释放,触点寿命很快耗尽,电机也受不了。解决办法是设置回差,比如设定值 25°C,实测 26°C 开机,降到 24°C 才停机,2°C 回差能有效防止震荡。
远程温度还有一个重要的用法是判断盘管是否冻结。北方地区冬季如果循环泵停机,盘管里的水在零下温度会结冰,导致铜管胀裂。用远程通道测盘管表面温度,当温度低于 4°C 时输出一个告警信号,联动循环泵启动或电加热投入,这个功能在实际项目里能省下大笔维修费。
5.2 告警阈值的配置与保存
PJ85718DM 的 ALERT 引脚可以在温度超过设定的上限或低于下限时输出中断信号。MCU 收到中断后读取状态寄存器,判断是哪个通道、哪种类型的告警,然后通过网口发送异常通知。
告警阈值应该做成可配置参数,而不是硬编码在代码里。PIC18F87J60 内部没有 EEPROM,但可以通过外部小容量 EEPROM 或者把配置数据烧录到程序 Flash 的末尾区域来存储。更稳妥的做法是支持上位机通过 Modbus 写寄存器来修改阈值,Modbus 的写保持寄存器功能码处理比读要稍微复杂一点,但能保证现场可调整性。
告警还要避免频繁重复上报。处理逻辑是:温度超过阈值后触发第一级告警,等到温度回落到阈值以下并且保持一段时间(比如 5 分钟)后再复位告警状态。如果温度在阈值附近抖动,最多只发一次告警,不会刷屏。
5.3 VAV 变风量末端联动中的温度汇聚
大型商业建筑的变风量空调系统末端数量动辄几百个,每个末端都需要一块带网络接口的控制板。PIC18F87J60 自带以太网接口,可以组成末端温度采集网络,每个末端把送风温度、回风温度上报到集中控制器,由集中控制器统一调节送风静压。
这时温度传感器读数的同步性就变得重要了。几百个末端逐个轮询一遍需要时间,如果各末端的上报周期一致,且都用本地时钟定时,那么集中控制器在同一个时间窗口内收到的数据基本代表相近时刻的温度状态。如果发现两个相邻末端的温度在相同工况下读数差值大于 0.5°C,优先检查远程通道的线缆连接和校准参数,而不是直接怀疑传感器坏了。
6. 实测数据与故障排查实录
6.1 我遇到过且容易复现的几个坑
问题一:I2C 总线读回的数据全部是 0xFF
这个现象在断电重新上电后最容易出现。原因是传感器上电复位需要时间,MCU 启动速度比传感器快,主机以为设备在线,但传感器还没完成内部初始化,总线无应答。解决方法是 MCU 上电后延时 300ms 再发起第一次 I2C 通信,并且在通信失败后重试三次,每次间隔 100ms。
还有一种情况是 SDA 或 SCL 被拉低后没有释放。检查挂接在总线上的其他设备是否有地址冲突。PJ85718DM 的地址引脚 A0、A1 如果悬空,可能漏极内部没有可靠的上拉或下拉,造成地址浮动。正确做法是全部明确接地或接 VDD,不悬空。
问题二:远程温度读数明显偏高,而且不稳定
远程温度偏高很大程度来自线缆串联电阻。HVAC 应用里远程探头通常用长线连接,如果线阻较大且 D+、D- 走线不对称,正半周期和负半周期的恒流源激励在采样端产生不一样的压降,读数就会偏。
对策有三种:换用更粗的双绞线;在固件里针对已知线阻做补偿;在传感器端增大恒流源电流档位(如果芯片支持)。实测中优先选固件补偿,不用改硬件。
不稳定则多半是干扰问题。远程二极管靠近电机、变频器等干扰源时,感应电压叠加在低频测温信号上会造成读数跳变。可以给 D+、D- 各串一个 1kΩ 电阻再接滤波电容,形成低通滤波,截止频率控制在 1MHz 以上即可,不会影响测温精度。
问题三:以太网插线后链路指示正常,但 Ping 不通
这个现象如果出现在 100M 模式下,大概率是差分走线的阻抗匹配做得不好。用网线测试仪确认线序没问题,再用协议分析工具看 ARP 请求有没有响应。如果 ARP 都通而 ICMP 不通,检查 ICMP 回显功能的代码是否编译进去了。
还有一个容易忽略的地方是 PHY 地址。PIC18F87J60 的 PHY 地址是固定的,但如果代码里不复位 PHY,有些寄存器状态可能与上一次运行的残留值不一致。初始化流程里先对 PHY 做一次软复位,再写控制寄存器,链路识别会更可靠。
6.2 排查工具与技巧
调试这套系统,最基础也最实用的工具是一个 USB 转 I2C 的分析器,用来单独验证传感器部分的好坏。把传感器从系统中断开,模拟器直接读取寄存器,如果读数随温度变化正常,说明问题在 MCU 侧而不在传感器侧。
网络部分调试推荐用 Wireshark 抓包。先抓设备的 ARP 请求,确认设备是否在网络发过请求。再用 modpoll 之类的工具直接读写 Modbus 寄存器,绕开自己的上位机,快速定位是协议栈的问题还是应用层的问题。
温度校准建议用标准水浴或恒温块。把传感器的本地探头和远程探头都放进恒温块,设定 25°C,读取两个通道的读数,记录偏差值;再设定 60°C,读取第二组偏差。两组偏差如果基本一致,说明是增益误差,调整缩放系数即可;如果偏差变化,则要考虑探头位置或传感器内部转换电路是否有问题。
6.3 全流程验收测试清单
一个温度监测产品在出厂前,至少应该跑一遍下面这些测试:
- 常温下两通道读数与标准温度计偏差不超过规格书标称值
- 高低温箱内分别在 0°C、25°C、60°C 三点验证精度
- 远程探头线缆最长和最短两种工况下读数一致
- 以太网接在不同品牌交换机上均能正常通信
- 模拟断电 100 次,每次上电后 5 秒内恢复温度上报
- 告警阈值设为上下限各一次,确认告警上报内容和复位逻辑正常
这套测试都过了,基本可以放心量产。
7. 后续可以扩展的方向
PJ85718DM 加 PIC18F87J60 这套组合还有一个很大的优势是预留了不少性能余量。可靠性要求高的项目里,可以在固件里增加看门狗喂狗逻辑,把温度采集周期和网络任务周期错开,进一步提升系统鲁棒性。网络协议方面,后续也可以继续叠加 SNMP 或 HTTP 的 JSON 接口,让温度数据更容易被其他系统集成。如果项目规模扩大、点数增多,可以把 PIC18F87J60 当作接入层节点,统一上报到中心服务器,形成完整的温度监测网络。