PIC18F66K40+PJ85718DM:HVAC分布式温度采集系统设计
做HVAC控制器这几年有一类需求碰到过太多次了既要读设备本地的回风温度又要同时盯住好几个远端风管、水管甚至室外的温度点。最近完成的某项目里我就用PJ85718DM温度传感器搭配PIC18F66K40单片机做了一套本地远程温度监测方案。整套方案折腾下来感觉它在成本、功耗和灵活性上确实能打特别适合中小批量的嵌入式产品和暖通系统。这篇博文把架构思路、硬件连接、底层驱动、远程通信以及调试踩坑的过程都整理出来给准备用类似方案的朋友做个参考。1. 系统架构设计先分清“本地”和“远程”到底怎么定义1.1 需求拆解嵌入式侧与HVAC侧的差异很多人一看到“本地与远程温度”第一反应就是“一个温度传感器放在本地远程用网络去读取”。但实际上在HVAC应用里这两个词的含义要更具体一些。本地温度往往指的是控制器附近、设备机箱内部或者回风口附近的温度而远程温度则可能是几米外的送风管道、几十米外的冷热水管道、甚至安装在外墙上的室外温度传感器。从这个角度看要解决的不是“一个传感器”的问题而是“一组分布在不同位置、距离控制器远近不一的传感器”的问题。嵌入式应用则更看重实时性和稳定性传感器必须能在工业电压波动、电机启停干扰的恶劣环境下可靠工作。PIC18F66K40作为一款8位单片机定位就很合适主频够用、外设丰富、价格友好还能兼顾多种通信接口。我做方案时定的原则是单总线或数字接口方式的长距离分布式采集优先能一根线挂多个传感器就绝不一个点拉一根模拟线。这也是后来选了PJ85718DM的直接原因。1.2 器件选型为何选PJ85718DM与PIC18F66K40先说说PJ85718DM这颗传感器。从手册参数看它是一款单总线1-Wire数字温度传感器典型测量精度在±0.5°C以内测量范围覆盖了-55°C125°C这个范围对HVAC系统来说绰绰有余。最关键的是它支持一根数据线上并联多颗每个传感器都有独立的64位序列号主机可以按号寻址这简直是多点温度采集的天然方案。与传统的NTC热敏电阻方案相比数字传感器最大的优势就是不需要做ADC校准不需要在板上焊一堆分压电阻线长一些也不怕信号衰减。单总线还允许传感器从数据线上寄生取电某些布线上确实方便。而PIC18F66K40这颗单片机内部有32KB程序存储器和4KB RAM跑个单总线协议加上Modbus远程通信的活儿毫无压力。它自带丰富的定时器、UART、SPI和I2C模块硬件资源分配起来很灵活。选这颗芯还有一个重要的点它支持外部5V供电的同时内部逻辑电平可以配置和3.3V或者5V的外设都能对接省了电平转换芯片这在工业设备里很实用。1.3 整体链路规划传感器、MCU、上位机之间怎么衔接我的整体链路是这样设计的若干个PJ85718DM传感器分布在各个测温点所有传感器并联在一条单总线上由PIC18F66K40的一个普通GPIO引脚驱动。单片机按照轮询方式依次发出跳过ROM或按序列号匹配的命令读取每一路温度。之后单片机本地处理这些温度数据一边把结果显示在本地LCD屏或者通过按键切换查看另一边通过UART连接的RS485收发器以Modbus RTU从机的方式挂到总线上让上位机组态软件或楼宇自控系统来读。如果需要走无线也很简单把RS485换成WiFi模块或者LoRa模块单片机侧的逻辑基本不用改只是把寄存器里的温度值通过串口转发出去。这样做的好处是“传感层”和“传输层”解耦调试时可以先用串口线看数据确认传感器链路没问题后再接远程通信减少多层问题交叉排查的麻烦。2. 硬件设计要点从传感器接线到MCU外围电路2.1 PJ85718DM的典型接法与关键参数PJ85718DM虽然用起来简单但硬件上有一个地方不能马虎——上拉电阻。单总线协议要求数据线在不通信时处于高电平所以必须在数据线和一个正的电源一般是3.3V或5V之间接一个电阻。这个电阻多大很有讲究我实测下来在供电电压为3.3V、传感器数量不超过10个、线缆长度在10米以内时用4.7kΩ比较稳定。如果线很长或者传感器数量多就要降到2.2kΩ甚至1.5kΩ不然上升沿太慢时序容易出错。传感器的VDD引脚我建议还是单独接电源不要依赖寄生供电。虽然单总线支持寄生供电模式但HVAC设备启动瞬间电流波动大寄生供电模式在上电时序上容易出问题。我在实验室里试过用寄生供电挂了6颗传感器刚上电时偶尔会读到全85℃的假数据改成外部供电之后这个问题基本绝迹。接法上还要考虑保护器件。传感器如果放在室外或者空调风管附近引线很容易受雷电感应或静电冲击。我一般会在接口处加一颗小的TVS二极管把数据线对地钳位在6V左右再串一个100Ω的电阻做限流。别小看这几个元件能省掉不少现场返修。2.2 PIC18F66K40最小系统设计经验PIC18F66K40的最小系统不算复杂但有几个坑要避开。时钟我直接用内部16MHz振荡器通过寄存器配置成16MHz这样可以省掉外部晶振的两个负载电容PCB面积也小。不过要注意内部振荡器的精度大约在±1%左右如果后面要用Modbus RTU跑19200bps以上的波特率就最好改用外部晶振或者把波特率误差算进去。实测9600bps下内部振荡器完全没问题19200也能用但我不建议再往上硬拉。复位电路用最简单的RC复位或者直接接VDD就行PIC18系列内部本身有上电复位和欠压复位模块。我通常只在MCLR引脚接一个10kΩ电阻到VDD再并联一个100nF电容到地基本不会出问题。电源引脚要就近放0.1μF陶瓷电容另外再留一个大一点的电解电容给整个系统缓冲尤其是带LCD屏幕和RS485收发器的时候瞬态电流能到几十毫安没有大电容容易复位。单总线引脚的选择上我建议把它放在一个没有复用下载调试功能的引脚上尤其是使用ICSP编程时不冲突的引脚。另外这条数据线不要和AC220V或者24V交流线走在一起。我有个项目因为布线空间紧张把传感器线和24V电磁阀线捆在一起结果温度数据时不时跳变后来改成独立屏蔽线并且单端接地才恢复正常。2.3 硬件装配与测试注意点板子回来后先不要接传感器用示波器看单总线引脚的静态电平是否被上拉到高电平。然后把一颗PJ85718DM插上发一次复位脉冲看示波器上是否存在传感器应答的低脉冲。这一步能快速确认芯片焊接和引线是否正常。我习惯把传感器接口做成三线端子VCC、GND、DATA不要用那种防水插头一根线又供电又传数据的接口维修时拔插容易搞错。3. 软件实现从单总线时序到温度采集与滤波3.1 单总线驱动层的时序怎么抠PJ85718DM和经典的DS18B20时序非常接近都是通过拉低总线时间的长短来写0和写1。时序这块是软件里最需要抠细节的地方。写0期间主机拉低总线60~120μs写1期间主机拉低约1~15μs后释放总线然后依靠上拉电阻恢复高电平。读时序则是由主机发起一个至少1μs的低脉冲然后释放总线并在15μs内采样数据线电平。这些时序参数如果直接用延时函数去磨会受到主频和编译器优化影响。我的做法是把GPIO操作和几个关键的耗时循环写死用NOP指令凑时间而不依赖通用的delay函数。下面是个简化版例子。#define OW_PIN LATCbits.LATC0 // 单总线引脚 #define OW_DIR TRISCbits.TRISC0 // 方向寄存器 // 复位脉冲返回1表示存在应答 unsigned char ow_reset(void) { unsigned char presence; OW_DIR 0; // 输出模式 OW_PIN 0; __delay_us(480); // 至少480us的低电平 OW_DIR 1; // 释放总线转输入 __delay_us(70); presence OW_PIN; // 采样应答 __delay_us(410); return presence 0 ? 1 : 0; // 读到低表示有器件应答 }这颗单片机的内部振荡器频率我用的是16MHz__delay_us能保证微秒级延时。不过需要特别注意如果后面改主频所有时序函数都要跟着重新调不然全是隐蔽的时序问题。3.2 读取PJ85718DM的温度数据与CRC校验每颗PJ85718DM会返回9字节的暂存器数据前两个字节是温度值第9个字节是CRC校验。温度数据的格式比较直接低字节在前高字节在后分辨率可配置默认12位时每个LSB对应0.0625°C。温度为正数时直接乘0.0625负数时需要把两个字节拼接成16位有符号数再做计算。我在读取时一定会做CRC校验这是单总线上多设备通信可靠的保障。单总线CRC是8位多项式0x31用一个查找表或者在线异或移位生成都行。如果CRC错误这帧数据就扔掉不要拿来算温度宁可下一轮再读。以下是读取并校验的核心片段。#define PJ_READ_SCRATCHPAD 0xBE #define PJ_SKIP_ROM 0xCC unsigned char ow_read_byte(void) { unsigned char val 0; for (int i 0; i 8; i) { val 1; if (ow_read_bit()) val | 0x80; } return val; } // 假设经过复位和跳过ROM后调用此函数读9字节 unsigned char temp_crc 0; temp_crc ow_crc8(scratchpad, 8); // 前8个字节算CRC if (temp_crc scratchpad[8]) { int16_t raw (scratchpad[1] 8) | scratchpad[0]; float temperature raw * 0.0625f; }这里有个细节在连续读取多个传感器时每读完一个传感器后一定再发一次复位否则下一个传感器可能不响应。而且传感器温度转换启动后需要等待转换完成。默认12位转换时间最长是750ms如果温度刷新频率要求高可以把分辨率设为9位转换时间降到93.75ms但精度只有0.5°C这就看需求取舍了。3.3 滤波与防跳变中位值平均才是硬道理HVAC现场的温度信号其实“毛刺”很多特别是风机启动瞬间传感器线上的干扰会被当成有效信号读进来。我试过直接读原始值来显示结果温度在1秒内能跳2°C左右虽然实际环境温度根本没变那么快。我的做法是在温度数据进入业务逻辑前做一次“中位值平均滤波”。具体说就是连续读取5次温度去掉最大值和最小值然后对中间3个值取平均。这样既过滤掉了单个大毛刺又不会像简单滑动平均那样把突然的真实变化也磨平。代码如下float avg_temp(float arr[], int n) { float sum 0; float min arr[0], max arr[0]; for (int i 0; i n; i) { if (arr[i] min) min arr[i]; if (arr[i] max) max arr[i]; sum arr[i]; } return (sum - min - max) / (n - 2); }实测下来5次采样中位值平均之后显示温度稳定在±0.1°C以内波动完全满足HVAC控制要求。如果现场干扰更强可以把连续测量次数加到7次代价是刷新周期变长设置传感器12位分辨率时要考虑这个时间开销。4. 远程通信本地与远程数据汇总上传4.1 远程通信方案选型RS485/Modbus还是LoRa做完传感器采集之后数据放在单片机里还不够HVAC应用必然要接入上位机或者楼宇自控系统。远程通信方案我一般分两种场景来选。第一种是集中式机房或者楼宇内现场传感器分散但总体距离不超过1000米这种用RS485总线加Modbus RTU协议最合适。PIC18F66K40的UART模块接一个隔离的RS485收发器成本低实时性好工业现场积压了大量现成的组态软件都支持Modbus。第二种是园区级室外分布点比如给好几栋楼的冷却塔补水管测温度中间隔着马路拉485线代价太高。这种我倾向于用LoRa无线模块做点对点传输或者用4G DTU把温度数据上传云平台。但要注意无线方案的实时性和供电问题LoRa虽然省电但数据碰撞和蹭网关的机制需要单独设计。这个项目里我先做的是RS485方案因为楼宇自控机房里面已经有现成的Modbus管理平台直接对接即可少写一套上位机协议。4.2 Modbus RTU从机实现要点PIC18F66K40做Modbus RTU从机核心是UART中断接收、CRC16校验和地址匹配。波特率我用9600bps8数据位无校验1停止位。这个配置在绝大多数组态软件里是默认选项兼容性最好。CRC16的算法用的是多项式0xA001每次收发一帧数据都要计算。这个计算本身不复杂但如果在主循环里同步做很容易阻塞其他任务。我的做法是UART接收完一帧后置一个标志位主循环检测到标志后再花几百微秒处理CRC和寄存器读写。以下是CRC16的一个常见实现。unsigned int modbus_crc16(unsigned char *buffer, unsigned int len) { unsigned int crc 0xFFFF; for (unsigned int i 0; i len; i) { crc ^ buffer[i]; for (int bit 0; bit 8; bit) { if (crc 0x0001) { crc 1; crc ^ 0xA001; } else { crc 1; } } } return crc; }从机寄存器表的规划也要提前想好。我习惯把每路温度数据映射成一个保持寄存器单位是0.1°C。比如寄存器地址0x0000存放1号传感器温度0x0001存放2号传感器温度当传感器故障时写入0xFFFF。上位机读取到一个不可能出现在正常范围内的值就能立即判断传感器状态而不是纠结于“温度怎么突然变成了255°C”。4.3 远程数据校验与掉线判断Modbus协议本身有CRC校验可以保证传输层的完整性但应用层的超时判断、重发机制却必须自己实现。我的从机程序里每次收到上位机请求都会先判断地址是否匹配不匹配则直接丢弃匹配则等整帧接收完成后再做CRC校验。这样能避免把其他从机设备的数据误解析。同时我还在数据寄存器里专门做了一个“心跳寄存器”上位机每隔一段时间去读取。正常时这个寄存器的值每秒钟递增上位机如果连续5秒没有读到递增说明通信链路断了界面直接显示“通信故障”而不是显示一个假温度。这个思路对提高系统的可维护性帮助很大。5. 常见问题排查与调试心得实录5.1 传感器读不到或者全部读到85℃这是单总线应用里最经典的问题。PJ85718DM上电后暂存器温度寄存器默认值就是0x0550换算出来恰好是85℃。所以如果程序“成功”读到多个85℃的温度值说明通信链路是通的但传感器根本没有执行温度转换或者主机发的启动转换命令没有被正确执行。我的排查顺序是先用示波器看单总线复位和应答波形确认传感器是否存在再量一下VDD引脚电压看是否满足手册要求最后检查上拉电阻是否焊错或者没焊。遇到过好几次是传感器插反了能读到全0xFF但永远不返回正确数据。换成带防反接的三线插头后这个问题就没再犯。5.2 实际温度正常但显示数值跳变如果滤波做了还是跳变大概率是硬件干扰而不是算法问题。我曾经在一个项目里把传感器线从逆变器的散热风扇旁边穿过结果温度数据每秒钟都能看到规律性毛刺。后来把传感器线换成了带屏蔽层的双绞线屏蔽层在控制器一端单点接地毛刺立刻消失了。记住屏蔽层千万不能在传感器端也接地否则会形成地环路反而引入更大的干扰。5.3 Modbus通信偶尔超时或者数据错乱这个问题要从三个方面查一是RS485总线的终端电阻一般要求总线两端各接一个120Ω匹配电阻。如果监控点位分散、接线不规范总线长度超过几百米后没接终端电阻就会产生反射导致波形畸变。我在项目现场用万用表量过A、B端之间的静态电阻正常应该在60Ω左右如果量到120Ω说明只有一端接了电阻或者根本没接。二是波特率误差内部振荡器跑9600bps问题不大但如果上位机用的是独立时钟源两边偏差过大会造成偶发性的帧错误。三是收发器方向控制引脚切换太慢我遇到过发送完成后立刻把接收使能打开但收发器芯片还在驱动总线导致自己发的一帧数据被自己误接收解决方法是TX完成后等一两毫秒再切换方向。5.4 现场实测记录与稳定性数据在实验室环境里我用这套系统挂了8颗PJ85718DM传感器分成两组一组走10米线一组走30米线。连续跑48小时温度读取成功率在99.8%以上。剩余0.2%的失败主要集中在电磁阀动作的瞬间数据通过CRC校验被滤掉了没有对控温逻辑造成任何影响。作为对比如果不上滤波、不校验读取失败率其实不到1%但对于长期运行的控制设备来说这1%可能就是一次误触发。6. 最后再分享一个调试技巧单总线传感器在更换device之后序列号会变所以如果用“按序列号匹配地址”的方式读取固定点位那么每一颗传感器在出厂安装时都要把ID烧录到EEPROM里。这个流程很繁琐而且换传感器还要重新烧录。我后来改成“跳过ROM遍历读取”的策略每次上电后先广播一次“搜索ROM”命令把所有在线传感器的序列号读出来建立一张动态映射表再根据物理安装位置通过一个本地按键或者Modbus寄存器来绑定“1号温度点对应哪个序列号”。这样换一颗传感器后只需要重新绑定一次不用动代码。这个小技巧在实际运维中非常省心。整个项目做下来我最大的体会是PJ85718DM与PIC18F66K40这套组合单看某一个点都不算最顶尖但组合在一起却是一个难得的低功耗、低成本、多通道且通信扩展性好的方案。尤其是单总线传感器本身的物理特性天然就适合HVAC这种分布测温、现场环境复杂的场景。如果你正在做类似的嵌入式温度采集系统希望这份记录能帮你少踩几个坑。