做嵌入式系统或者暖通空调(HVAC)控制的同行,应该都有过类似的经历:设备明明显示“温度正常”,现场却已经热保护停机了。问题往往不是测错了,而是测的温度根本不是你想关心的那个温度。这个项目里我用了一颗本地/远程温度前端PJ85718DM,配合一颗PIC18F85J50单片机,把板级本地温度和风道、房间里的远程温度放进同一套链路统一监测,既解决了设备过热预警的问题,也满足了空调机组远程温控的数据需求。对做楼宇自控、HVAC机组、储能柜、通信基站监控的朋友来说,这套思路可以直接参考复用。
1. 方案定位与整体设计
1.1 嵌入式与HVAC场景下,本地和远程温度分别指什么
先把概念补齐。嵌入式设备里说本地温度,通常指控制电路板本身、MCU附近、功率器件或电源模块表面那一路温度。它反映的是“设备自身有没有过热风险”,比如处理器降频、接触器线圈过热、IGBT散热片温度超标,这类数据直接决定了设备能不能继续运行。
远程温度在HVAC场景里指的是真正的环境测点:风管内的送风温度、房间回风口的温度、室外机附近的空气温度,或者是被冷却机柜内部的温度。它反映的是“系统提供给人或设备的温湿效果”,这路数据往往距离主控板有几米到几十米,线缆要穿过金属桥架、和动力线走同一个线槽。
实际项目里这两路必须分开测,原因很简单。我之前处理过一台组合式空调箱的控制面板报警:控制器报“机柜温度过高”,但打开柜门很快降温,到底该不该停机大家都拿不准。后来把板级温度和风道温度分开采集才发现,主控板靠近接触器和变压器,夏天柜内温度本来就接近55°C,而真正要保证的送风温度其实正常。如果只测一路,要么频繁误报警,要么漏掉真正的故障。
两路温度一起用,还能做联动判断。比如HVAC的防冻保护:盘管附近温度低于设定值,同时回风温度也在往下走,基本可以判断水路或者风阀出了问题;如果只有一路异常,可能是探头位置不对或者传感器本身坏了。本地和远程形成交叉验证,整套监测的可靠性会高很多。
1.2 为什么用PJ85718DM加PIC18F85J50这对组合
先讲传感器这边。早期做温度采集,最常用的是热敏电阻加ADC,但HVAC柜内环境干扰很强,热敏电阻输出的模拟电压长线传输后很容易被接触器吸合干扰影响,而且每颗热敏电阻的B值曲线不完全一致,批量生产时要逐个标定,很麻烦。
PJ85718DM这类的温度前端芯片,把本地测温单元和外部远程测温通道做进了同一颗器件里,直接输出数字量。本地通道测板温,远程通道接一个外置探头,放到风管或者房间远端,不需要额外的恒流源、放大器和ADC校准。更关键的是它还有一个ALERT告警脚,温度越限可以直接拉低,接到主控的中断上比软件里轮询阈值更及时,也省主控时间去扫寄存器。
主控这边选PIC18F85J50,主要看中三点。第一,Flash和RAM在PIC18系列里属于中上水平,存一套Modbus从站协议、温度滤波算法和配置参数完全够用;第二,这个型号有多个硬件UART和硬件SPI/I2C模块,一路UART做RS485远程上报,一路UART做调试输出,I2C对接传感器,外设能同时工作,不用软件模拟吃CPU;第三,外部中断和定时器资源够,接传感器告警脚、做采集周期调度都不需要抠门。
对比过用普通8位MCU加运算放大器搭模拟采样电路,成本表面上低一点,但模拟链路在强电柜里的抗干扰、批量标定、故障排查全是隐性成本。数字传感器加数字总线的方案,算总账反而更省。
1.3 整体数据链路规划
这套系统的数据流可以分成四条链路,各管各的:
- 温度采集链路:外置远端探头和板载本地测温单元接入PJ85718DM,芯片通过I2C与PIC18F85J50通信,读取两路温度值。
- 远程上报链路:PIC的UART接RS485收发器,走Modbus RTU协议,把本地温度、远程温度和状态字发给上位机或者楼宇BA系统。
- 本地显示链路:PIC通过SPI接一块LCD或者数码管,现场人员不开上位机也能直接看温度,方便设备调试和维护。
- 告警动作链路:PJ85718DM的ALERT脚接PIC外部中断,温度超过高温阈值或者低于低温阈值时,传感器硬件直接打断主控,主控立刻执行保护逻辑。
为什么要这样拆?HVAC现场最麻烦的就是强电干扰和施工布线。数据在传感器端就转成数字量,I2C线短距离走板内,不会像模拟信号那样把几米外的干扰直接引进来。远程上报用RS485和Modbus,是目前HVAC、楼宇自控和工业现场兼容性最好的一套组合,上位机、触摸屏、PLC基本都能直接对接。本地显示是用来看现场状态的,设备调试时不需要抱着电脑跑上跑下。
这套链路设计的关键是“数据分层”:底层负责采集,中间层负责处理,上层负责呈现和上报。每一层之间通过寄存器表、协议帧解耦,后期换传感器型号或者换上位机平台,都只改局部,不用推翻重来。
2. 硬件设计与引脚级细节
2.1 PJ85718DM的寄存器模型与工作模式
PJ85718DM这类本地加远程一体的数字温度传感器,内部通常包含配置寄存器、本地温度寄存器、远程温度寄存器、状态寄存器和高温/低温阈值寄存器。具体地址和位域不同批次会有一点差异,但设计逻辑是通用的:配置寄存器决定工作模式、转换分辨率、报警极性等行为;温度寄存器存放转换结果;状态寄存器标记有没有越限、有没有转换完成;阈值寄存器存储用户设定的报警上下限。
它可以看成一支数字体温计,但同时它自己也在测环境温度,还拖着一根外置探头测远端温度。芯片内部完成远端探头信号的处理,把微小的电压信号转换成数字量,这对抗干扰很有帮助,因为远端探头出来的小信号在芯片内部就被就地数字化了,不会在板子上绕一大圈再被干扰。
工作模式方面,HVAC这类持续监测场景建议用连续转换模式,温度变化能被及时捕捉;嵌入式低功耗场景则可以用单次转换模式,想省电的时候按需发起一次测量,然后进入睡眠。报警阈值寄存器配置好后,ALERT脚就能工作在比较独立的硬件告警状态,即使主控在忙别的,传感器也能自己盯住温度。
配置这类芯片有个容易忽略的细节:不少型号在刚上电时会有一段启动时间,有些芯片的寄存器需要写入“软复位”命令再等待转换就绪,否则读取的第一帧数据可能是空值。我的做法是在初始化流程里固定加一步延迟和状态寄存器等待,确保所有外设准备就绪后再进入正常运行。
2.2 PIC18F85J50的外设分配与接口设计
PIC18F85J50的引脚资源比较多,但也不能随手乱接。引脚分配需要先把复用外设固定下来,再安排普通I/O,否则后面画PCB时会发现走线绕成蜘蛛网。
我在这套系统里的分配思路是这样的:
- MSSP模块做I2C主模式,速率定在400kHz,对接PJ85718DM。SCL和SDA在板内走线,距离很短,不需要额外驱动。
- UART1接RS485收发器,作为Modbus RTU从站接口,默认波特率9600,HVAC上位机轮询周期一般1到5秒,这个速率完全够用。
- UART2接调试串口,用来打印系统运行状态、转换原始值和滤波结果,开发阶段非常有用,量产时可以不接或者禁用。
- 外部中断引脚接传感器ALERT脚,配置为下降沿触发,温度越限时PIC立刻进入中断处理。
- SPI模块接LCD显示屏,刷新频率不高,占用资源小。
有个需要注意的地方:PIC18F85J50同一组引脚往往由多个外设复用,比如某几个引脚既能做I2C又能做SPI或者ADC。项目里必须先读清楚复用表,把I2C的外设信号和RS485的UART信号放到不会冲突的引脚组上。我建议先用表格列一遍所有要用的外设功能,再对照引脚复用关系做一次分配,确认没有信号交叠再画原理图。
此外,ALERT脚最好加上拉电阻,因为这类开漏告警输出需要外部上拉到高电平,越限时拉低。如果省掉上拉,中断脚的电平会一直飘,进不了下降沿中断,或者误触发不停。
2.3 硬件布线、供电与去耦注意事项
硬件设计里翻车最多的不是原理图,而是供电和布线。先看供电:PJ85718DM这类数字传感器如果工作在低电压,而PIC18F85J50的外围电路电压不一致,I2C电平就可能不匹配。稳妥做法是全部统一到同一逻辑电平面,如果实在统一不了,必须在SCL、SDA上增加电平转换电路,而不是用一个电阻分压草草了事,否则高速通信时波形边沿会变得很难看,偶发数据错误。
再看I2C上拉电阻。板内I2C线路不长时,典型值选4.7k到10k都行。但如果传感器通过延长座或者排线连接,线路电容变大,上拉电阻太小会拖慢上升沿,太大会导致信号边沿不够。实测下来,85厘米以内的排线用2.2k比较稳,20厘米以内的板内走线用4.7k没有任何问题。设计时最好预留贴不同阻值电阻的位置,方便现场调整。
远程探头线缆是另一处重点。HVAC现场尤其要注意,探头线必须走屏蔽双绞线,屏蔽层单端接地,不要和接触器线圈线、变频器输出线平行走同一桥架。远端探头接口位置还要加ESD保护器件和简单的RC滤波,因为柜内空间有限,施工时经常有人把探头线跟强电线绑在一起,不会有明显pp,但干扰会体现在温度读数上。
本地测温位置同样要慎重。要测“板温”,传感器就离大功率发热元器件保持一点距离;要测“芯片壳温”,可以把探头或者传感器本体靠近芯片表面,但不要直接压在散热片上。还有一个常被忽略的点:机柜内空气不流动时,本地温度寄存器反映的是局部热点的温度,不是柜内平均温度,所以本地测温点应该放在最容易超过设计阈值的那些元器件附近,而不是放在PCB正中间图好看。
3. 固件实现与温度数据处理
3.1 初始化与寄存器配置
固件部分要从初始化开始。核心思路是先让PIC的I2C外设就绪,再让PJ85718DM进入已知状态,最后配置转换模式、分辨率和报警阈值。
初始化代码大致是这样的结构:
#define PJ_I2C_ADDR 0x9A // 7位地址左移后得到,实际以手册为准 #define REG_CONFIG 0x01 #define REG_LOCAL_TEMP 0x05 #define REG_REMOTE_TEMP 0x07 #define REG_STATUS 0x02 #define REG_THIGH 0x03 #define REG_TLOW 0x04 void pj_init(void) { uint8_t cfg, status; I2C_Init(400000); // 主模式400kHz // 写配置:连续转换,12位分辨率,告警极性低有效 cfg = 0x60; I2C_WriteReg(PJ_I2C_ADDR, REG_CONFIG, cfg); // 写报警阈值,先屏蔽做最保守的上下限 I2C_WriteReg(PJ_I2C_ADDR, REG_THIGH, 0x7F); I2C_WriteReg(PJ_I2C_ADDR, REG_TLOW, 0x80); // 等芯片完成一次转换 do { status = I2C_ReadReg(PJ_I2C_ADDR, REG_STATUS); } while ((status & 0x80) == 0); }写配置时最忌讳的是把所有寄存器一次性写死。不同芯片的配置位含义有差异,如果直接把手册里的示例值照搬,很可能在量产批次的芯片上出现行为不一致。我习惯把配置值定义成宏,并注释每个位域的作用,后续换料或者调试时改起来很方便。
初始化完成后,可以先读一次本地温度和远程温度,打印出来确认传感器的确在工作,再进行后续的采集调度。
3.2 温度读取、符号扩展与线性换算
PJ85718DM这类芯片通常把温度值放在两个字节的寄存器里,温度数据以二进制补码表示。读取之后不能直接当无符号数使用,要先做符号扩展。对于12位分辨率、LSB对应0.0625°C的常见格式来说,代码可以这样写:
int16_t read_temp(uint8_t reg_addr) { uint8_t msb, lsb; int16_t raw; msb = I2C_ReadReg(PJ_I2C_ADDR, reg_addr); lsb = I2C_ReadReg(PJ_I2C_ADDR, reg_addr + 1); raw = ((int16_t)msb << 8) | lsb; if (raw & 0x8000) { raw |= 0xF000; // 符号扩展,保留负温度 } // 右移4位,得到12位有效数据对应0.0625°C的整数值 return (raw >> 4) * 100; // 返回放大100倍的温度,单位0.01°C }为什么不直接返回浮点数?因为在PIC18这类8位MCU上,浮点运算虽然能用,但会占用大量时间。HVAC项目的温度采集周期一般是100毫秒到1秒,其实并不缺这点性能,但少用浮点可以降低代码体积,也方便后续移植到更小的芯片。
读取远程温度时要特别留意,远程通道的探头类型不同,数据格式可能和本地通道不完全一致。有的芯片本地通道和远程通道的分辨率一样,但有的远程通道因为外部探头特性非理想,可能需要套用官方提供的一组校准系数。不要拿着本地通道的换算公式直接套远程通道,要仔细看手册里的换算说明。
3.3 软件滤波与远端探头线损补偿
温度传感器原始读数在HVAC现场会出现随机波动,主要来自风机振动、环境气流变化和电磁干扰。完全相信单次采样结果的代价是误报警,所以我加了一阶低通滤波:
int16_t filter_temp(int16_t new_value, int16_t *filtered, uint8_t alpha) { *filtered += ((int32_t)(new_value - *filtered) * alpha + 128) >> 8; return *filtered; }alpha取255表示完全相信新值,取1表示滤波非常重。HVAC系统本身热惯性很大,温度不会突变,一般把alpha放在32到64之间,对应几秒的时间常数,兼顾稳定性和响应速度。嵌入式过热保护场景则要区分对待:如果监测的是芯片结温附近的温度,滤波太重会导致保护滞后,alpha可以放到200左右甚至不滤波,直接把原始值用于判断。
远端探头线损是另一个实际问题。远程探头线很长时,线缆内阻和接触电阻会被温度传感器前端感知成额外的信号衰减,导致测出来的温度偏高或偏低。硬件上最好选带屏蔽层的双绞线,软件上做一段补偿表。具体做法是拿一段已知长度的短参考探头和长线探头同时测量同一热源,记录差值,在固件里查表修正。这种修正不严谨但很实用,尤其现场不允许频繁断电接线的时候。
3.4 温度数据的本地显示与远程上报
数据上屏相对简单,把滤波后的本地温度、远程温度和报警状态按固定格式显示在LCD上。调试阶段我会增加一屏显示原始ADC值或者寄存器值,方便现场判断传感器有没有异常。这个调试界面在量产版本里可以隐藏,但开发时很有用。
远程上报走Modbus RTU从站,我维护了一张数据映射表,比如保持寄存器0x0000放本地温度,0x0001放远程温度,0x0002放状态字,状态字里分别用bit表示传感器是否在线、是否越限。上位机只需要周期读取这三个寄存器就能完成监控。
Modbus RTU报文本身不复杂。比如上位机下发:
01 03 00 00 00 03 05 CB含义是站号1,功能码03读保持寄存器,起始地址0x0000,读3个寄存器,后面是CRC校验。从机返回的帧里按顺序填充六个字节数据,最后追加CRC16。CRC16算法是Modbus标准多项式0xA001,代码逻辑固定,网上有大量成熟实现,不需要重复发明轮子。
这里有个经验:当从机响应时间超过了上位机期望的应答时间,上位机往往会报超时。所以从机主循环里温度采集和Modbus处理要分优先级,RS485接收中断里只收帧不处理业务,等一帧收完整了再处理,避免在上位机轮询时去做耗时的I2C读取和滤波计算。
4. 调试实录与常见问题排查
4.1 远程温度狂跳:先怀疑机械连接
这套系统调试时最头疼的是远程通道温度突然从正常值跳到-50°C或者85°C。第一反应不要查固件,先去查硬件连接。
我遇到过一次典型的现场问题:远程探头线中间用端子排转接,端子螺钉没压紧,线缆在风机振动下接触电阻随机波动,传感器把波动直接反映成温度跳变。用示波器抓探头信号线能看到低频毛刺,电笔一碰就恢复。处理方式是重新压接端子,线缆固定好,屏蔽层单端接地,问题立刻消失。
这个案例说明一个规律:数字传感器的数字输出不代表前端信号是数字的,探头信号依然是微弱的模拟量。只要模拟量在物理上被干扰,后面怎么滤波都没用。排查顺序应该是机械连接在前,软件滤波在后。
4.2 I2C偶发超时:电平与上拉电阻两处暗坑
另一个常见问题是I2C总线偶尔卡死,读上来的温度突然全是0xFF或者总线挂住不响应。最初我怀疑是芯片时序问题,反复检查代码也无果,最后发现是主控和传感器两者供电电压不一致,加上I2C上拉电阻值偏大,导致高电平没有被可靠拉起来。
把供电统一、上拉电阻换成2.2k之后,连续运行三天没有出现一次卡死。为了保险,固件侧也加了超时重试机制:每次I2C通信前检查总线空闲状态,发现SCL被拉低超过一定时间,就执行总线恢复序列,把SCL切换成GPIO手动翻转几帧,释放总线。这套软件容错虽然不能解决硬件缺陷,但能显著降低偶发故障造成的影响。
4.3 实测温度偏离:标定流程和两点校正
设备装到现场后,用户投诉送风温度和实测风温差2°C。这种情况先别急着改代码,先做标定。标准做法是把探头和标准温度计同时放进冰水混合物和恒温槽,分别稳定后记录原始寄存器值。
标定的数学处理很简单,当作线性校龄:
// T_cal = k * raw + b // T1, T2 为标准温度,D1, D2 为对应原始读数 // k = (T2 - T1) / (D2 - D1) // b = T1 - k * D1两点标定之后还有微小的非线性,但如果温度范围控制在0到70°C,线性近似的误差基本可以接受。真正影响标定质量的是操作细节:探头放进恒温槽必须等读数完全稳定再记录,一般要等5分钟以上,不能看数字差不多就抄;记录标准温度计时不要让手接触温度计球泡,否则又是一次人为误差。
4.4 常见问题速查表
| 现象 | 大概率原因 | 排查与处理 |
|---|---|---|
| 远程温度跳变 | 探头线端子接触不良、屏蔽层未接地、ESD击穿 | 重新压接、固定线缆、单端接地、加保护器件 |
| I2C读回0xFF或总线卡死 | 电平不匹配、上拉电阻过大、总线被拉低 | 统一供电、换小阻值上拉、软件总线恢复 |
| 温度整体偏高或偏低 | 探头位置错误、未标定、线损未补偿 | 检查探头安装位置、做两点标定、查表修正 |
| 报警频繁误触发 | 阈值设置过于贴近正常值、滤波太弱 | 调宽阈值范围、增加滤波时间常数 |
| Modbus偶发超时 | 从机处理耗时、波特率不匹配、地址错误 | 优化主循环调度、核对CRC和站号 |
| 本地温度异常高 | 传感器靠近散热片、柜内气流不畅 | 调整板级测温点位置、改善通风 |
这套速查表是我实际调试中反复用到的,省去了很多翻手册的时间。硬件和软件问题的比例大约七比三,所以遇到异常先动手检查接线,再动代码,顺序别反过来。
最后再分享一点个人体会。如果让我重新做一遍这个项目,我会先把远端探头线的走向、固定方式和接地原则定死,再开始标定。温度监测最难的不是把寄存器里的数字读出来,而是让这个数字在真实环境里始终反映物理实际。软件滤波只能改善表象,硬件和结构上的确定性才是稳定的基础。希望这套基于PJ85718DM和PIC18F85J50的实路方案,能帮你少走几步弯路。