
做嵌入式和 HVAC 的朋友大概率都经历过这种场景现场机柜里装了一堆温度传感器本地读数正常可到了要远程看数据、做告警、对接上位机的时候就发现总线上不稳定、数据飘、驱动写不顺。我这段时间刚好把一个基于PJ85718DM单总线数字温度传感器和PIC32MZ2048EFH144144 引脚的 32 位 MCU的本地 远程温度监测系统完整落地了一遍从硬件选型、电路连接、固件驱动到远程上报都跑通了。这篇文章就把这套方案的踩坑过程和最终可复现的细节写出来给正在做嵌入式温度采集、风机盘管控制、机房环境监控、HVAC 系统改造的朋友做个参考。这套系统的核心并不复杂用PJ85718DM挂接多路温度探头解决本地多点采集用PIC32MZ2048EFH144做控制和协议转换解决远程上报和对 HVAC 控制器/上位机的数据对接。难的是把时序、抗干扰、设备寻址、通信稳定性这些“隐性成本”处理好。下面按我从选型到落地实跑的顺序拆开讲。1. 项目整体设计与方案选型1.1 为什么选 PIC32MZ2048EFH144 而不是一颗 8 位单片机先说结论如果只是采两三路温度、用数码管显示一颗 8 位单片机完全够用。但一涉及到“本地 远程”两个维度需求就变成多个传感器挂在一条总线上、要跑协议栈、要留出 PID 或者告警逻辑、要能升级固件、要同时驱动 LCD 和通信接口。这时候 8 位机的资源就开始吃紧代码里到处是优化和压缩后期的维护成本反而高。我选PIC32MZ2048EFH144的主要原因有几个这颗芯片是 32 位 MIPS 内核最高跑到 200MHz带硬件浮点单元。做 HVAC 控制时如果要在本地跑 PID 或滤波算法浮点指令能省不少事。片上有 2MB Flash 和 512KB RAM。这个资源量放在温度采集项目里非常宽裕能直接跑轻量级 RTOS也能把通信协议栈、LCD 驱动、传感器驱动全部装下不用抠抠搜搜。外设齐全集成 10/100M 以太网 MAC、USB、CAN、多个 UART/SPI/I2C。144 引脚封装给了足够多的 GPIO后续挂按键、蜂鸣器、继电器、扩展 IO 都不用烦恼引脚不够。工业级温度范围适合 HVAC 现场那种常在设备间、室外机旁、配电柜里工作的环境。我拿它和两类常见方案做过对比对比维度8 位单片机方案主流 Cortex-M 方案PIC32MZ2048EFH144 方案主频8~72MHz偏弱80~480MHz可选范围多200MHz够用RAM/Flash几 KB 到几十 KB紧张几十 KB 到 2MB弹性大512KB RAM 2MB Flash宽裕以太网基本都要外挂芯片部分型号集成 MAC自带 MAC外挂一颗 PHY 即可浮点运算大多数没有多数有 FPU硬件浮点跑滤波和 PID 顺手工业环境适配一般看具体型号工业级设计抗性较好这套项目里还存在一个很实际的问题客户现场既有空调风柜又有小型冷水机组控制接口五花八门有些要走 RS485 的 Modbus有些要走以太网 TCP有些只给一个干接点。PIC32MZ 这种外设丰富的芯片可以让我在固件里同时保留多种远程通道而不是硬件方案一上来就被引脚数量和外设数量限制死。1.2 PJ85718DM 传感器与单总线方案逻辑PJ85718DM 是一类单总线数字温度传感器引脚和通讯方式都很接近大家熟悉的 DS18B20 风格一根数据线既能供电又能传数据多个传感器可以并联在同一条总线上主机通过总线上的时序来识别和读取每一个设备的温度。我手上用的这颗 PJ85718DM 具体参数要以实际批次数据手册为准但常见规格通常包含测温范围约 -55°C 到 125°C典型精度 ±0.5°C出厂带有 64 位唯一序列号支持 9~12 位可配置分辨率。这样的特性放在 HVAC 场景里非常合适空调风管温度、回风温度、室内温度、水管温度精度需求一般也就 ±0.3~0.5°C单总线传感器在成本上比 PT100变送器低一大截而且不用校准变送器、不用外接 ADC。很多人会问为什么不用 NTC 热敏电阻便宜又简单NTC 确实便宜但有两个痛点第一非线性严重需要查表或者用 Steinhart-Hart 公式算精度上还要靠匹配电阻和校准第二模拟量信号在长线传输时抗干扰能力差稍微靠近变频器、接触器电压就跳动。PJ85718DM 这类数字传感器直接输出数字量主机读到的是完整的温度寄存器值线缆干扰主要影响通讯时序可以通过软件重试和 CRC 校验来兜底整体可靠度高很多。单总线还有一个隐藏优势寻址方式灵活。每个传感器都有唯一的 ROM 序列号主机可以精确指定读某个探头也可以跳过 ROM 广播读取所有探头。现场安装时不需要每个探头单独拉一根模拟线一条双绞线串下来就能带十几个点这对 HVAC 这种传感器分布散的场合非常友好。1.3 本地监测与远程监测的系统架构整个系统一开始我就分成两部分来设计本地监测PJ85718DM 探头分布在几个核心测点比如风柜的送风管、回风管、冷冻水供水管、回水管、机房环境温度。这些探头挂接到 PIC32MZ2048EFH144 的某个双向 IO 口上由单片机负责轮询、解析、显示在本地 LCD/OLED 屏幕并根据设定阈值联动蜂鸣器或继电器。远程监测单片机把处理后的温度数据、状态信息通过 RS485 Modbus 或以太网接口转发给监控主机、楼宇自控系统或云网关。远程端不需要知道单总线的时序细节只需要按照 modbus 协议或自定义帧格式来读寄存器。这样就做到“传感层和通信层解耦”以后现场要换传感器、加探头只要改固件里的总线配置不用动整个通信框架。这种架构的好处是即使远程链路断了本地监测和控制依然能独立运行反过来远程端即使离线也不会反过来拖垮单总线采集。我建议所有做类似项目的朋友都坚持这个“本地优先、远程解耦”的思路否则很容易在调试远程通信时把整个采集流程搞崩。2. 硬件搭建与电气连接的关键细节2.1 单总线网络的接线与上拉电阻选择单总线最容易被忽略的就是上拉电阻。PJ85718DM 和大多数单总线设备一样数据线是漏极开路结构必须在外部把数据线接到电源正极。常见做法是用一颗 4.7kΩ 上拉电阻。但 4.7kΩ 不是万能的。我在测试中发现传感器数量少、线缆短比如 1~2 米时4.7kΩ 很稳定一旦现场线缆拉长到 10 米以上或者并联探头超过 5~8 个总线寄生电容增大上拉电阻太大时信号边沿变缓MCU 读取时序就出问题表现就是偶尔读到 0x85 或者温度值为 0。我的实际处理经验现场情况推荐上拉电阻备注单探头短线缆2m4.7kΩ最通用多探头并联线缆 5~10m2.2kΩ~3.3kΩ降低上升沿时间长线缆或电磁环境较差1kΩ~2.2kΩ加 TVS 管注意总线驱动能力超过 20 米探头多不建议直接挂裸总线加总线缓冲或分路信号完整性优先需要注意的是上拉电阻不是越小越好。电阻太小总线漏极开路驱动电路会被灌入过大电流传感器输出低电平的电压会抬升反而可能导致逻辑判断混乱。所以要先估算总线等效电容再从小到大试阻值。我自己习惯先在板子上画一个 0603 封装的上拉电阻位置同时预留 0Ω 并联位方便调试时快速换阻值。接线另外有两个细节单总线一定要共地。很多新手只接数据线和电源线导致传感器地线电位与 MCU 地不一致通讯失败的排查会极其痛苦。电源与数据线尽量靠近。传感器供电直接从总线取电还是外接电源决定了寄生供电模式还是外部供电模式。做 HVAC 项目时我强烈建议外部供电至少三根线VCC、GND、DQ不要用寄生供电。寄生供电对时序要求更苛刻现场稍微有干扰就容易复位。2.2 MCU 引脚规划与电平匹配PIC32MZ2048EFH144 引脚多但分配时不要随手定。我的建议是先列需求清单再逐个功能块分配单总线 GPIO选择支持中断且抗干扰设置灵活的引脚并把数据线尽量引到板边靠近接线端子减少板内走线绕行。LCD/OLED如果是并行接口引脚数量占用大建议优先选 SPI 接口屏幕节省 IO。留出背光控制、复位引脚。RS485用一路 UART 方向控制引脚方向控制引脚要靠近 UART 发送引脚避免布线过长导致控制时序错位。以太网RMII 接口有固定信号组包括 TXD、RXD、时钟、MDIO 等这类引脚在 144 封装上通常是专用复用引脚分配时要看数据手册别把时钟信号安排到离 MCU 核心很远的地方。电平匹配方面要注意PIC32MZ 的 IO 是 3.3V 电平。如果传感器模块用的是 5V 供电但数据线是开漏结构配合上拉电阻上拉到 3.3V 就可以直接互连如果传感器模块内部做了 5V 电平和推挽输出就不能直连需要加电平转换或者串联限流电阻。我在样板阶段就因为这个原因烧过一个引脚后来全部统一成 3.3V 上拉数据线电平就干净了。2.3 电源、隔离与长期可靠运行HVAC 现场供电环境并不干净压缩机启动、接触器吸合、变频器运行时电源线上会有很明显的浪涌和尖峰。给这套系统供电我直接采用了两级方案24V AC/DC 输入现场很多 HVAC 控制柜都有 24V 交流电源第一级用 AC-DC 模块或整流滤波降到稳定的直流中间母线。板级稳压中间母线经过 DC-DC 隔离模块后再通过 LDO 降到 3.3V 给 MCU 和传感器供电。隔离模块的作用是切断来自动力侧的地环路干扰对远程通信稳定性帮助很大。有条件的话建议把数字电源和模拟电源分开至少用磁珠做单点连接。单总线传感器虽然输出的是数字信号但传感器信号线如果和继电器驱动线走在同一束线槽里耦合干扰雷打不动会出现。在远程通信侧RS485 接口两端我加了 TVS 管和共模电感总线 AB 线之间加 120Ω 终端电阻。很多人以为终端电阻只有长距离通信才需要其实哪怕 20 米内的两站通讯加上终端电阻也能明显减少反射尤其是一个主站带多个从站的拓扑下稳定性提升非常显著。3. 固件实现把温度读数稳定拿到手3.1 单总线时序驱动与初始化单总线时序是整个固件里最容易翻车的地方。PJ85718DM 这类单总线协议在初始化阶段是这样工作的主机把总线拉低至少 480μs然后释放总线等待一段时间后采样。传感器在检测到主机释放后会等待 15~60μs 再主动拉低总线 60~240μs形成一个“存在脉冲”。主机在释放总线后的 60μs 左右采样数据线如果读到低电平说明总线上有设备在线。这个过程看着简单但用 MCU 实现时有个经典坑延时函数本身精度不够。PIC32MZ 跑在 200MHz我用系统定时器做微秒延时和用循环空转做延时行为完全不同。循环空转会受中断影响一旦有串口中断或定时器中断插入延时时间就被拉长单总线时序就乱了。所以我在整个单总线驱动里强制关中断来保证时序微时的准确性只有读取和写位结束之后才重新开中断。核心代码片段如下static void ow_delay_us(uint32_t us) { // 基于核心定时器的精确微秒延时 uint32_t start CORE_TMR_GetCount(); uint32_t ticks (us * (CORE_TMR_FREQ / 1000000UL)); while ((CORE_TMR_GetCount() - start) ticks); } uint8_t ow_reset(void) { uint8_t presence 0; OW_DIR_OUT(); OW_WRITE_LOW(); ow_delay_us(500); OW_WRITE_HIGH(); ow_delay_us(30); OW_DIR_IN(); ow_delay_us(70); presence OW_READ(); ow_delay_us(410); return presence; // 0 表示存在 }写位和读位同样考察时序void ow_write_bit(uint8_t bit) { OW_DIR_OUT(); OW_WRITE_LOW(); if (bit) { ow_delay_us(6); OW_WRITE_HIGH(); ow_delay_us(64); } else { ow_delay_us(60); OW_WRITE_HIGH(); ow_delay_us(10); } } uint8_t ow_read_bit(void) { uint8_t bit; OW_DIR_OUT(); OW_WRITE_LOW(); ow_delay_us(6); OW_WRITE_HIGH(); ow_delay_us(4); OW_DIR_IN(); ow_delay_us(8); bit OW_READ(); ow_delay_us(50); return bit; }从这段代码能看出来读位的过程里采样点非常关键主机拉低总线后必须及时释放然后在一个很窄的时间窗内读取电平。采样点偏早或偏晚都会读到错误的电平。我在调试时喜欢用逻辑分析仪抓实际的写位、读位波形然后把延时参数微调到波形符合协议要求为止靠“眼睛看现象”是不行的。3.2 温度转换、分辨率与 CRC 校验PJ85718DM 这类传感器的读温流程一般是主机向总线上所有设备发送跳过 ROM0xCC命令再发送温度转换命令0x44等待转换完成然后重新初始化发送跳过 ROM 命令发送读取暂存器命令0xBE连续读取 9 个字节。9 个字节里包含温度低位、温度高位、高低告警寄存器、配置寄存器、CRC 字节等。转换时间跟配置的分辨率直接相关。常见的 12 位分辨率下转换时间约 750ms9 位分辨率下大约 90ms。HVAC 场景不需要毫秒级刷新12 位分辨率更合适温度分辨率最小约 0.0625°C足够平滑。我在固件里做了这样的处理上电后先读取每个传感器的配置寄存器统一设置为 12 位分辨率避免不同探头批次默认值不一致。有符号温度值的高 5 位是符号扩展位。判断正负时不要只看最高位而是把整个 16 位当成有符号数处理不然负温度会被错误换算。强烈建议做 CRC 校验读取到 9 字节之后先校验最后一个字节CRC 不过就丢弃本次数据保留上一次温度值。这个处理比盲目相信寄存器值要稳妥得多。CRC 校验函数如下uint8_t ow_crc8(uint8_t *data, uint8_t len) { uint8_t crc 0; while (len--) { crc ^ *data; for (uint8_t i 0; i 8; i) { crc (crc 0x01) ? (crc 1) ^ 0x8C : crc 1; } } return crc; }这里多项式用的就是单总线传感器常见的 CRC-8 多项式。上位机如果也实现了同样的校验可以进一步排查到底是读取错误还是数据本身异常。3.3 多点传感器搜索与通道管理多探头并联时主机不能一直用跳过 ROM 来广播操作必须知道每个传感器唯一的 64 位 ROM 码才能针对某个通道单独读取。最稳妥的做法是在系统初始化和安装阶段执行一次 ROM 搜索把所有探头的序列号枚举出来然后保存到一个设备表中。单总线搜索算法的核心是逐位比较每个设备的 ROM 码主机每一位的读时间片会先读到“该位值”的补码然后依据两个 bit 的组合确定分支方向对歧义位做标记用回溯的方式遍历整棵搜索树。固件里最怕的是没有对搜索次数做上限限制导致死循环。我建议加上“最多重复搜索 8 轮”的保护机制并在每轮搜索后检测是否回到了同一个 ROM 码避免设备表里出现重复项。搜索到设备表后还需要给每个传感器分配逻辑通道号。这是工程化设计和 Demo 的最大区别。Demo 可以直接用 ROM 码当通道名工程上最好定义一张通道配置表通道号ROM 序列号示例安装位置作用CH028 0E 12 34 00 00 00 5A风柜送风管送风温度CH128 0E 45 67 00 00 00 9C风柜回风管回风温度CH228 0E 78 9A 00 00 00 21冷冻水供水管冷水供水温度CH328 0E BC DE 00 00 00 77冷冻水回水管冷水回水温度映射表存在 Flash 或外部 EEPROM 里现场换传感器后可以通过维护串口重新绑定通道。这样即使 ROM 码原本毫无规律逻辑通道始终稳定远程端读到的寄存器地址不会随便变化。4. 远程上报从单片机到上位机的通路4.1 远程链路的方案选择远程上报在这套系统里有两个方向一个是走 RS485 总线进楼宇自控另一个是走以太网进监控平台。RS485 Modbus 在 HVAC 行业极其常见主要是因为绝大部分冷源群控、风机盘管控制器、能源管理网关都支持 Modbus RTU兼容性最好。以太网则更灵活可以直接对接私有上位机也可以转化成 MQTT 或 HTTP 上报云平台。PIC32MZ2048EFH144 自带 MAC外接一颗百兆 PHY 芯片就能跑 TCP/IP 协议栈。不过我这次现场部署优先用了 RS485原因很现实现场原有链路就是 RS485换以太网布线成本太高RS485 一对双绞线手拉手串下来最省事。我的建议是如果从零开始且允许布线以太网优先如果现场已有楼宇自控系统直接用 RS485 Modbus 是风险最低的方案。在这套系统里我两种通道都做了固件里用一个配置项切换当前主用通道。4.2 Modbus 寄存器与数据帧设计Modbus RTU 的帧格式不再赘述重点是你要怎么分配寄存器地址。我的常用习惯是把温度值放大 10 倍或 100 倍传给上位机用整数寄存器保存避免浮点数端序和内存对齐带来的坑。温度 12.34°C 就传 1234上位机拿到后除以 100 再显示。寄存器分配表可以这样组织寄存器地址读写属性含义数据类型0x0000只读CH0 温度放大 100 倍有符号 16 位0x0001只读CH1 温度放大 100 倍有符号 16 位0x0002只读CH2 温度放大 100 倍有符号 16 位0x0003只读CH3 温度放大 100 倍有符号 16 位0x0100读写CH0 告警上限放大 100 倍有符号 16 位0x0101读写CH0 告警下限放大 100 倍有符号 16 位0x0200只读运行状态字16 位位映射对于单个从站设备功能码用 0x03 读保持寄存器或 0x04 读输入寄存器都可以。我习惯先读通道温度再读状态字这样上位机一次轮询就能拿到最新数据和设备自检状态。一个典型请求帧示例字段长度示例数据说明从站地址1 字节0x05本设备地址功能码1 字节0x03读寄存器起始地址高1 字节0x00起始地址 0x0000起始地址低1 字节0x00起始地址低位寄存器数量高1 字节0x00数量高位寄存器数量低1 字节0x04一次读 4 个通道CRC 低1 字节0xXX循环冗余校验低字节CRC 高1 字节0xXX循环冗余校验高字节从站收到后依次返回每个寄存器的 16 位数据最后附 CRC。这里的字节序是 Modbus 的“低字节在前”MCU 端做数组拼接时要特别注意否则上位机读出来温度永远不对。4.3 与 HVAC 控制器对接的流程HVAC 控制器通常以固定周期轮询温度采集器。如果采集器响应慢或者总线上挂着多个 Modbus 从站主站轮询周期会被拉长。所以温度采集侧不要等远程请求到了才发起单总线转换而应该在本地始终以固定周期比如 2 秒轮询 PJ85718DM把最新温度缓存到寄存器数组里。远程主站来读取时直接返回缓存值不阻塞、不等待。这种“缓存 即时响应”的思路可以避免远程轮询速度和传感器转换速度之间的耦合。我经常遇到有人把温度转换放在 Modbus 中断服务里做结果一个请求卡掉其他响应整个总线的响应超时接踵而至。如果对接的是云网关用 MQTT 时也要注意本地上行主题可以只发温度值和状态量下发主题则包含阈值配置和校时命令。网关端最好做“离线缓存”设备断网重连后能补上数据不然现场变成监控盲区告警逻辑就形同虚设。5. 常见问题与排查技巧实录5.1 单总线通讯失败读回全 1 和总线挂死这可能是单总线项目里最常见的故障。读回全 1基本可以断定主机没有把总线拉低或者传感器没上电或者存在检测时读取点偏早。排查顺序我固定如下先用万用表量传感器引脚确认 DQ 和 GND 之间有电压确认数据线上拉到 3.3V。用示波器或逻辑分析仪抓 ow_reset 波形看主机低电平是否超过 480μs释放后是否存在脉冲是否出现。如果存在脉冲正常问题多半在 ROM 命令或者时序微调上调整读位/写位过程中的延时。总线挂死则是另一类问题传感器与 MCU 在总线上互相拉低一直处于忙状态后续所有操作都无响应。解决办法是给每次单总线操作设置超时。我在固件里设置了一个“总线恢复流程”连续 3 次操作无响应时把数据线拉低 720μs 再释放然后再重新复位很多时候能把卡死的器件救回来。5.2 温度跳变与电磁干扰HVAC 现场最大的干扰源是接触器吸合和变频器输出。温度寄存器跳变、读数周期性异常十有八九是干扰耦合进数据线或者供电线。解决顺序是先把传感器供电改成独立稳压供电不要和继电器驱动共用一个 3.3V。数据线用双绞屏蔽线屏蔽层单点接地别两端都接。总线上加 TVS 管和 RC 滤波。软件层加连续读取多次取中位数的滤波算法。干扰问题最好在硬件层解决软件滤波只能掩盖症状。我实测过一组数据不加任何滤波时读回来的温度偶尔从 23.4°C 跳到 78.5°C加屏蔽和 TVS 后完全消失相反单纯加软件滤波后显示平滑了但偶发的错误值还是会在历史记录里留痕后期分析很麻烦。5.3 看门狗、低功耗与异常恢复MCU 跑起来后掉电、死机、卡在某个 while 循环里都会让系统丧失温控能力。我给这套系统加了独立硬件看门狗并且把喂狗位置放在主循环的相对固定点而不是放在定时器中断里。如果放在定时器中断里喂狗哪怕主循环已经卡死看门狗依然不会复位系统就一直带病运行这是做工程最忌讳的。看门狗的设计还有个小技巧喂狗前先检查单总线模块的上一次采集时间戳。如果超过 5 秒没有取到新温度就先执行一次总线恢复再喂狗。这样可以让异常在上位机告警之前就自动恢复减少现场维护频率。低功耗不是这个项目的核心但嵌入式设备总有备用电池供电的场景。PIC32 在休眠模式下可以保留 RAM 数据单总线传感器在休眠期间不供电检测到定时唤醒后再上电读取。这套逻辑在 HVAC 场景里不常用但如果你后期做无线节点建议一开始就把“按需上电”的电源设计考虑进去不然后期改板子成本很高。6. 给同行和初学者的经验补充6.1 开发环境与调试工具选择这套系统我是在 MPLAB X 环境下用 C 完成的调试时搭配了模拟器工具和逻辑分析仪。对初学者我建议不要省掉逻辑分析仪的钱单总线时序这东西看再多的文档都不如抓一次波形学得快。调试时把采样点、时序延时都标出来波形和协议一对比问题就清楚了。另外PIC32MZ 的编译优化等级对时序影响很大。尤其是用空循环做延时的代码高优化等级可能把循环优化掉导致延时时间完全不对。我最后把所有延时函数都改成基于核心定时器的精确延时再开启优化时序依然稳定这才解决了互斥问题。6.2 传感器校准与一致性处理PJ85718DM 这类数字传感器虽然声明有精度但实际出厂一致性是会波动的。要求高的时候批量安装后建议做一次两点校准冰水浴0°C 附近和局部恒温水浴比如 40°C 或 60°C把每个传感器的偏差记录到设备表里固件读取后自动补偿。不要小看这个步骤。HVAC 里如果各区域的温度传感器之间有 ±0.8°C 的偏差同一个房间有的探头显示 22.1°C有的显示 22.9°C明明系统没问题空调机组却会频繁启停能耗反而上升。校完后把偏差写入EEPROM以后换探头时重新校准数据一致性就有保障。我在实际项目里还做了一种低成本一致性校验把所有探头绑在一根金属棒上放入同一恒温环境等待平衡后读取每条通道的温度计算各通道偏差然后生成一张补偿表。这个方法不需要精密仪器但能把系统的整体一致性控制在 ±0.2°C 以内性价比极高。这个项目做完之后我最大的感受是温度监测系统的功能本身不难难的是把现场条件考虑周全。单总线的时序、RS485 的抗干扰、传感器的一致性、固件的异常恢复每一个环节都直接决定整套系统能不能在机房、风柜旁稳定跑一年。如果你正在规划类似的项目建议从这套“本地采集 远程转发”的架构入手先把硬件方案定稳再逐步加功能会比一上来就追求“全功能大而全”顺畅得多。