1. 项目定位与整体设计思路1.1 这套方案到底要解决什么问题做嵌入式和 HVAC暖通空调项目的朋友应该都有同感温度监测这件事看起来简单一旦放到实际工程现场就变成了一堆麻烦。你在设备柜里测环境温度是一回事跑到风管里测送风回风温度又是另一回事本地设备前面想实时看温度是一回事中控室要远程把几十个节点的温度全收上来又是另一回事。我这次搭的方案核心就干两件事一是用 PJ85718DM 温度探头在设备本地做可靠采集二是通过 PIC18F57K42 这颗 8 位 MCU 把数据同时用于本地逻辑处理和远程传输。说白了就是把本地温度和远程温度这两个维度的需求在同一个嵌入式系统里一次做掉。这套方案适合谁参考如果你正在做HVAC控制器的从站节点、机房环境监测板、冷库温度采集终端或者任何需要本地测温 远程上报双能力的设备这篇文章的思路可以直接拿来用。特别是对成本敏感、需要低功耗、希望用一颗成熟8位MCU搞定全部工作的场景这套组合很有参考价值。1.2 系统架构与技术选型逻辑先看整体架构我画一下我实际落地的系统分层。设备侧的组成是三块传感层、主控层、通信层。传感层就是 PJ85718DM通过单总线协议接到主控主控层是 PIC18F57K42负责定时发起温度转换、读取结果、跑协议栈、控制本地输出通信层是 RS485 总线走 Modbus-RTU 协议把温度数据挂到远程监控网络里。为什么这么拆因为HVAC现场的特点非常鲜明节点多、距离远、电磁环境差、工程人员维护水平参差不齐。RS485 加 Modbus-RTU 在这类场景里存活了几十年到现在依然是楼宇自控、暖通监控里最稳的组合。而单总线温度探头则解决了传感器布点灵活的问题——一根线上可以挂多个探头现场需要测风管进风、回风、混风时不用每个探头单独拉一条复杂总线极大降低了布线成本。选 PIC18F57K42 的原因也很直接它自带多路 UART、12位ADC、DMA、可配置逻辑单元这些核心独立外设单芯片就能把传感器驱动、协议处理、本地IO控制全部扛下来。后续调试时不用外挂一堆芯片BOM成本可控长期供货也稳定。1.3 本地温度和远程温度的边界怎么划做方案之前必须把概念弄清楚否则代码写一半才发现理解偏了返工成本很高。我在这个项目里对本地和远程的定义做了明确区分。本地温度指的是物理上直连到本设备、由 PJ85718DM 探头直接采集的温度数据。这些数据实时参与本地的控制逻辑比如超温告警、风机联动、加热器启停。远程温度则是通过 RS485 总线从远端监控主机或者其他节点获取的温度数据。换句话说远程温度不是本设备测出来的而是通过通信网络看到的远端温度。这个划分很重要。因为在实际HVAC系统里某个控制柜不仅需要知道自己这台设备附近的温度还要知道几百米外另一台设备的温度用来做联动判断。所以我在 Modbus 寄存器设计里既放了本机探头温度也留了远程温度映射区让主站可以写入或转发远端温度本机再根据这些温度组合决定执行逻辑。2. 核心器件特性与硬件设计2.1 PIC18F57K42 这颗芯片强在哪PIC18F57K42 属于某厂商 K42 增强型 8 位 MCU 系列最高主频64MHz这个性能在8位机里属于相当能打的水平。Flash 和 RAM 的容量对于这种温度采集加协议处理的应用绰绰有余而且它有多种封装可选从28引脚到48引脚都有我能根据实际PCB面积灵活选择。真正让我觉得它适合这个项目的是它集成了一批核心独立外设CIP也就是不需要CPU介入就能自己干活的外设模块。举几个实际的例子。第一12位ADC带计算功能可以对采集到的模拟量做内置累加、求平均我如果以后要接NTC或者4-20mA变送器不用额外写软件滤波。第二多路UART支持硬件自动波特率检测这对我调试RS485通信非常有帮助。第三它内置CRC和内存扫描配合DMA可以在后台自动校验数据块CPU占用率极低。第四可配置逻辑单元CLC可以把几个IO信号在硬件层面直接组合成控制信号比如把两个温度比较器的输出直接触发告警完全不用软件参与。当然K42 系列也有它的学习门槛最典型的就是外设初始化需要配置大量寄存器不像某些精简单片机那样几行代码就能跑起来。不过一旦熟悉了它的寄存器框架后面用起来非常顺手。2.2 PJ85718DM 温度传感器的实际表现PJ85718DM 是一款常见的数字温度传感器采用单总线1-Wire接口指令集和引脚定义跟市面上主流单总线温度芯片兼容。这意味着什么意味着我现有的大部分驱动代码可以直接适配不用从零写协议而且现场如果急用可以用同类型的探头临时替换。它的测温范围对HVAC场景来说完全够用覆盖-50℃到125℃。分辨率可以配置默认12位模式下温度分辨率达到0.0625℃。对风管温度监测来说0.1℃级别的精度已经远远满足要求毕竟HVAC控制精度的目标也就是±0.5℃。PJ85718DM 的供电比较灵活可以在3.0V到5.5V之间工作。我在项目里直接用了3.3V给传感器供电这样和MCU的IO电平一致不需要额外的电平转换。有一点大家要注意单总线器件靠一个寄生供电还是外部供电会影响接线方式。我建议室外或工业现场优先用外部供电因为寄生供电在长距离布线和电磁干扰大的环境下容易出现供电跌落导致转换结果不稳。这个我后面在常见问题里还会细说。2.3 硬件电路连接与关键参数下面是我实际画板时的电路连接方式直接给出可抄的配置。MCU 使用内部高频振荡器作为时钟源经PLL倍频到64MHz。PJ85718DM 的数据线接到 MCU 的一个普通双向IO口数据线上拉一个4.7kΩ电阻到3.3V。这个上拉电阻的值比较关键不能太小也不能太大。太小会导致低电平灌电流过大太大则会让单总线的上升沿变缓、通信距离缩短。4.7kΩ是数据手册和工程实践之间比较平衡的选择。RS485 部分MCU 的 UART 发送、接收、方向控制脚接了一颗常见的 RS485 收发器。收发器的A、B总线输出端各串联一个10Ω电阻做限流保护A、B之间并一个120Ω终端电阻。这个终端电阻在很多设计里容易漏掉如果总线末端不加长距离传输时信号反射会非常明显具体表现就是偶发乱码和帧错误。电源方面系统输入是24V直流HVAC现场最常见的就是24V经过一级降压到5V再经LDO降到3.3V给MCU和传感器供电。PIC18F57K42 的AVDD和DVDD我分开走线ADC参考电压单独滤波这样后续扩展模拟量输入时底噪会小很多。2.4 PCB布局与布线注意事项做这种带通信接口的板子布局布线直接影响现场稳定性。我的做法是数字电路和RS485接口区域物理隔离RS485收发器尽量靠近接线端子放置A、B差分对走线要平行且等长不要跨分割区。晶振如果用外部晶振要靠近MCU周围不要走高频数字信号线。电源入口加TVS管和共模电感RS485的A、B线对地加TVS这个在雷雨多的地区实测非常有效。另外PJ85718DM 如果采用外部供电电源线和数据线应该采用双绞线或者屏蔽线。屏蔽层单端接地不要两端都接否则屏蔽层会成为地环路反而引入更大的干扰。3. 本地温度采集单总线驱动详解3.1 单总线协议到底是怎么回事单总线协议名气大原因就是它能在一条数据线上同时完成供电寄生模式和通信。不过大多数工程实践还是用外部供电模式数据线只负责通信。它的时序核心有三个复位脉冲、写时序、读时序。所有通信由主机发起从机温度探头只能被动响应。主机把数据线拉低一段时间然后释放从机检测到这个复位脉冲后会拉低数据线一段时间作为存在脉冲。这个握手过程就像打电话前的喂在吗。读写时序更讲究。单总线要求严格的时隙time slot宽度主机写0时要把数据线拉低整个时隙的绝大部分时间写1时只拉低很短时间。读时序则是主机拉低一小段时间后释放然后采样从机是否拉低数据线来判定是0还是1。整个过程对延时精度要求很高只要时序不对读回来的数据全是乱的。好在PIC18F57K42的主频足够高软件延时配合精确时钟可以非常稳定地实现时序。3.2 固件驱动实现与代码拆解先看最基本的总线操作函数。我用的是GPIO模拟方式没有用硬件单总线外设这样代码可移植性最好。#define OW_PIN PORTBbits.RB0 #define OW_TRIS TRISBbits.TRISB0 void ow_reset(void) { OW_TRIS 0; // 输出模式 OW_PIN 0; __delay_us(480); // 拉低480us以上 OW_TRIS 1; // 释放总线改为输入 __delay_us(70); // 等待从机响应 // 此时读取OW_PIN低电平表示存在脉冲 __delay_us(410); // 等待复位时隙结束 } static void ow_write_bit(uint8_t bit) { OW_TRIS 0; OW_PIN 0; __delay_us(2); // 起始低电平 if (bit) { OW_PIN 1; // 写1提前释放 __delay_us(60); } else { __delay_us(60); // 写0保持低电平 OW_PIN 1; } OW_TRIS 1; __delay_us(2); } static uint8_t ow_read_bit(void) { uint8_t bit 0; OW_TRIS 0; OW_PIN 0; __delay_us(2); OW_TRIS 1; // 释放总线准备读取 __delay_us(1); if (OW_PIN) { bit 1; } __delay_us(60); // 读时隙剩余时间 return bit; }这几个函数是整个驱动的地基。写字节和读字节就是循环调用上面的位操作这里不赘述了。接着是读温度的主流程。每次读取要先发复位然后发跳过ROM命令如果总线上只有一个探头再发温度转换命令0x44等待转换完成。转换时间跟分辨率相关12位分辨率下最大要750ms这个时间对HVAC控制来说完全可接受不需要做硬实时。转换完成后再发复位、跳过ROM、发读暂存器命令0xBE连续读取9个字节其中前两个字节是温度低字节和温度高字节后面是告警阈值和配置寄存器以及CRC校验字节。int16_t read_pj85718dm_temp(void) { uint8_t buf[9]; int16_t raw; ow_reset(); ow_write_byte(0xCC); // 跳过ROM ow_write_byte(0x44); // 启动温度转换 __delay_ms(750); // 等待转换完成 ow_reset(); ow_write_byte(0xCC); ow_write_byte(0xBE); // 读暂存器 for (int i 0; i 9; i) { buf[i] ow_read_byte(); } // 检查CRC if (crc8_check(buf, 8, buf[8]) false) { return TEMP_READ_ERROR; } raw (buf[1] 8) | buf[0]; return raw; // 实际温度 raw * 0.0625 }3.3 多探头挂载与CRC校验的应用技巧单总线最舒服的地方就是挂多个探头但也是踩坑最多的点。每个 PJ85718DM 都有一个64位唯一ROM序列号主机可以通过搜索ROM指令枚举总线上所有设备的序列号。我建议在初始化阶段做一次完整的ROM搜索把每个探头的序列号和对应物理位置比如风管1回风风管1送风绑定起来。之后正常运行时可以发匹配ROM指令指定读某个探头也可以用跳过ROM一次性启动所有探头转换再逐个匹配ROM读取数据。前者更灵活后者更省时。CRC校验必须重视。单总线协议里每个读出的暂存器数据后面都有一个8位CRC它校验的是前面9个字节中的8个数据字节。如果CRC不对说明通信过程中数据被干扰了最直接的处理是丢弃本次结果重新读取。HVAC现场电磁干扰多如果没有这一层校验偶尔会出现温度值跳变几千比特的错误控制逻辑会做出荒谬的决策。我实测过加了CRC校验后错误数据的接收概率几乎为零。4. 远程温度监测Modbus-RTU 从站实现4.1 为什么远程通信选了 RS485 加 Modbus现在物联网方案很多动不动就是MQTT、Wi-Fi、4G。但HVAC工程现场的实际情况是设备多部署在楼宇机房、建筑夹层、管道井里网络条件差维护人员更习惯用传统的PLC和组态软件。Modbus-RTU在这种环境下是事实标准几乎所有楼宇自控系统、触摸屏、PLC都原生支持。对PIC18F57K42来说一个UART加一颗RS485收发器就搞定了远程通信硬件成本极低稳定性极高。选Modbus-RTU还有两个隐性好处。第一协议非常轻量8位MCU的Flash和RAM完全吃得下。第二调试工具多电脑上随便一个串口调试助手加一个Modbus调试工具就能模拟主站现场排查问题非常方便。波特率我选了9600bps。可能有人觉得慢但对温度数据这种变化缓慢的信号9600bps完全够用而且低波特率在长距离双绞线上抗干扰能力更强。一帧温度数据不过8个字节左右1秒轮询一次几十个节点毫无压力。4.2 Modbus-RTU 从站协议栈的状态机实现从站协议栈的核心是状态机按字节接收驱动。我用串口接收中断加一个环形缓冲区每收到一个字节就压入缓冲区。主循环里从缓冲区取字节喂给协议状态机。状态机的设计要考虑帧间隔判定。Modbus-RTU规定一帧数据之间的间隔不能超过3.5个字符时间超过这个间隔就认为上一帧结束、下一帧开始。在9600波特率下一个字符大约1ms3.5个字符时间差不多4ms左右。我在代码里用一个毫秒递减计数每次收到新字节就清零计数倒计时到0就认为一帧结束了开始解析帧数据。typedef enum { FRAME_IDLE, FRAME_RECEIVING, FRAME_COMPLETE, } frame_state_t; void process_serial_byte(uint8_t byte) { static uint8_t buf[MODBUS_MAX_FRAME]; static uint8_t len 0; // 帧间隔超时判断 if (frame_timer 0) { buf[len] byte; } else { len 0; buf[len] byte; } frame_timer 4; // 4ms间隔 if (len MODBUS_MAX_FRAME) { // 超长丢弃 len 0; } }帧解析时第一件事是检查CRC16。Modbus的CRC16用查表法或者直接计算多项式是0x8005初始值0xFFFF结果低字节在前。CRC不对直接丢弃。CRC正确再看从站地址地址匹配才响应否则忽略。4.3 功能码与寄存器映射设计我在项目里实现了两个最常用的功能码03读保持寄存器06写单个寄存器。这两个功能码覆盖了大部分HVAC应用场景。温度值在寄存器里我不能直接存整数摄氏度因为那样会丢掉小数点精度。我的做法是放整数单位为0.01℃。比如温度23.45℃寄存器存2345。这样既避免了浮点数在Modbus传输中的麻烦也保证了0.01℃的分辨率足够HVAC用了。寄存器映射我列个表方便直接对照寄存器地址名称读写属性说明0x0001本机温度只读本机PJ85718DM探头温度0.01℃0x0002本地告警状态只读bit0超温告警bit1低温告警0x0003远程温度读写主站写入或转发的远端温度0.01℃0x0004超温告警阈值读写默认550055.00℃0x0005低温告警阈值读写默认-1000-10.00℃0x0006温度回差读写默认2002.00℃有了远程温度这个寄存器主站就可以把其他节点的温度数据转发过来本机就能实现比较本地和远端温差决定是否联动风机这类高级逻辑。比如检测到本地回风温度和远端送风温度温差超过设定值说明风量不足就给出告警信号。Modbus从站的异常响应也要做好比如收到不支持的功能码要返回异常码01非法功能码收到越界地址返回异常码02非法数据地址收到非法的数据值返回异常码03非法数据值。这些细节虽然繁琐但主站调试人员对异常码非常依赖响应规范能省大量沟通成本。5. 常见问题与排查技巧实录5.1 温度读数跳变、偏差大的排查思路这是温度采集项目最常遇到的问题没有之一。现象通常是温度值偶尔跳到几十度甚至几百度以上然后过一会又恢复正常。第一检查点永远是CRC。如果CRC错误率高说明总线上信号质量差。常见原因有三个上拉电阻太小、数据线太长导致分布电容过大、供电电压不稳。我用示波器测过异常设备的单总线波形发现上升沿明显弯曲这说明上拉能力不足或者线太长。解决办法是把上拉电阻改小到2.2kΩ同时让供电在传感器附近加一个0.1μF陶瓷电容再并联10μF电解电容。第二检查点是时序。PIC18F57K42的延时函数在不同的编译器优化等级下实际延时会有差异。尤其是在 -O2 优化下软件延时的空循环可能被优化掉导致时序严重异常。我建议时序相关的延时用编译器内置的__delay_us或者__delay_ms函数不要自己写空循环延时。第三检查点是接线顺序。单总线探头分三根线VDD、GND、DQ。现场施工时经常有工人把红黄黑接错或者是端子排接触不良。这时读数会表现为一直显示85℃——这是传感器内部上电复位后的默认值强烈提示通信根本没建立起来。遇到这个值先查接线别急着改代码。5.2 RS485 通信丢帧、乱码的完整排查路径RS485的丢帧乱码90%出在物理层10%出在协议层但大家往往先查协议层走了不少弯路。我按顺序排查先看终端电阻。总线两端必须各有一个120Ω电阻。只有一个节点时收发器旁边也要按终端电阻否则信号反射会让波形畸变。再看A、B线是不是接反了。A接A、B接B现场经常搞混。A、B接反最典型的故障是能收到部分数据但CRC总是错。继续看地线。RS485虽然叫差分传输但收发器需要一个公共地参考。长距离传输时最好在总线末端把各个节点的信号地连起来或者选用带隔离的RS485模块。最后再排查波特率偏差。如果主站和从站的波特率偏差超过一定范围长时间帧同步会漂移。PIC18F57K42内部振荡器在温度变化时会有偏差如果对稳定性要求高可以考虑使用带外部晶振的设计。5.3 HVAC 现场电磁干扰的实战对抗经验HVAC现场最常见的干扰源是变频器、接触器和继电器。这些设备在动作时会产生强烈的电磁脉冲通过电源线或者空间辐射耦合到传感器和通信线上。我的经验是分三级防护。第一级是电源入口处理使用隔离DCDC模块给设备供电把变频器侧的干扰隔断。第二级是RS485接口隔离用数字隔离器加隔离电源彻底切断地环路。这个对雷击、地电位差引起的损坏也有效。第三级是软件容错比如温度读取失败时使用上一次的有效值连续多次失败才置告警标志Modbus通信超时后不立刻改变输出状态而是保持上一状态。我曾经在一个现场发现温度数据每天固定时间跳变排查了很久才发现是同一个配电柜里有一台定时启停的排风扇继电器吸合瞬间干扰到了传感器线。后来把传感器线改成屏蔽线并且单独走线槽问题彻底消失。这类问题用仪器很难复现最好的方法就是在硬件设计阶段把防护做足。6. 三个版本的演进与最终实测效果6.1 从原型到稳定版的迭代记录这个项目我前后迭代了三版。第一版是原型验证板用开发板飞线连接传感器和RS485收发器重点验证PJ85718DM的单总线时序在PIC18F57K42上的表现以及Modbus协议栈能不能稳定跑通。这一版暴露的主要问题是在开发板上用杜邦线连接传感器线一长就出现CRC错误。第二版画了正式PCB引入了更规范的上拉、终端和ESD保护。这一版整体稳定性大幅提升但在环境试验中发现了高温下的通信问题排查后确定是LDO在高环境温度下热阻不足导致MCU供电电压低于工作下限。第三版优化了电源设计和PCB布局把发热器件分散同时在MCU的供电引脚加了更完善的去耦电容。这一版在不同负载和温度条件下做了连续72小时的测试表现稳定。6.2 实测数据汇总在环境试验箱里做了从-20℃到60℃的温度校准测试选几个有代表性的数据点标准温度传感器读数误差-20.00℃-19.94℃0.06℃0.00℃0.02℃0.02℃25.00℃24.98℃-0.02℃40.00℃40.03℃0.03℃60.00℃60.11℃0.11℃从数据看整体误差在±0.15℃以内完全满足HVAC温控需求。在连续72小时运行中Modbus通信丢帧率小于万分之二单总线CRC错误率几乎为零。功耗方面设备在3.3V供电下正常运行电流约18mA主要功耗来自RS485收发器空闲状态和MCU运行。如果开启休眠模式每次温度转换间隔唤醒一次平均电流可以降到2mA以下能满足电池供电的场合。6.3 后续扩展的几个方向这次用的硬件平台其实还留了不少扩展空间。PIC18F57K42的12位ADC可以接一路4-20mA模拟量输入比如外接压力变送器或者湿度变送器。可配置逻辑单元可以把本地温度告警直接映射到一个硬件输出脚就算MCU跑飞了温度告警依然能用独立逻辑输出这对工业安全场景很有价值。另外Modbus协议栈目前只做了03和06两个功能码需要的话可以加04功能码读输入寄存器或者16功能码批量写寄存器方便主站一次性下发多组设定值。还可以考虑增加一个串口透传模式让调试人员不用拆机就能通过远程通信口连接本地调试口。7. 最后说两句自己的心得做这个项目最大的体会是嵌入式方案里真正难的不是单个功能实现而是把好几个功能可靠地组合在一起并且能在恶劣现场长期运行。单总线温度采集和Modbus-RTU从站任何一个单拎出来都是文档里讲得清清楚楚的东西但组合在一起后遇到的各种问题很多时候要靠实际跑现场才能积累经验。我个人强烈建议如果你也要做类似设计第一步先把硬件上的保护电路做齐包括ESD、终端电阻、电源滤波和隔离这些成本不高却能避免日后大量的现场奔波。第二步是固件里一定把CRC校验做强无论单总线还是Modbus校验不过直接丢弃这套思路能挡住九成的偶发故障。第三步是给自己留一个可靠的调试接口哪怕只是一个串口调试口在联调时就能省下大把时间。这套方案从我自己的项目经验来看稳定性、成本、可维护性三者平衡得很好。如果你正在纠结几个方案之间怎么选希望这篇文章能帮你少走一些弯路。