做嵌入式温度采集这么多年,我试过热敏电阻、热电偶、I2C温湿度传感器,也折腾过不少远程测温方案。前阵子做一套 HVAC 控制器的多区温度监测,用的就是标题里这个组合——PJ85718DM 数字温度传感器搭配 PIC18LF45K80 单片机。整个项目跑下来,本地温度读取稳得很,远程多个节点的数据汇聚也基本一次调通。这篇博文把这个方案的选型逻辑、电路接线、底层驱动、远程组网方式,以及我踩过的坑都完整整理一遍,给正在做嵌入式温控或暖通项目的朋友作个参考。
1. 项目全貌与实际需求拆解
1.1 “本地”和“远程”温度监测的区别
很多人刚拿到这个标题会困惑:同一套系统,为什么要把“本地温度”和“远程温度”分开说?其实这两者在硬件架构和软件逻辑上完全是两码事。
本地温度监测,指的是温度传感器直接挂在控制器周边,传感器和主控之间的距离通常只有几十厘米到几米。典型场景是嵌入式设备内部的板级测温,比如电源模块温度保护、散热风扇联动、设备外壳温度检测。这种应用最关心的是读数准、响应快、接线简单。
远程温度监测则完全不同。传感器可能会分布在几十米甚至上百米的范围内,比如HVAC系统里每个楼层的回风温度、每个房间的进风温度、冷水机组的供回水温度。此时传感器和主控之间不是简单的一根线连起来,而是需要组网、需要寻址、需要处理长线传输时的信号衰减和干扰。
PJ85718DM 这种单总线数字温度传感器,恰好可以用一根数据线同时解决多节点的串联部署问题;而 PIC18LF45K80 这颗单片机自带丰富的外设,又能把本地读数和远程上报整合在同一个处理器里。这个组合从架构上讲是很顺的。
1.2 这个方案到底解决了HVAC里的哪些痛点
我在HVAC控制器的实际项目里,遇到过几个很典型的问题:
第一,温度精度和一致性。以前用NTC热敏电阻,每颗电阻的B值都有离散性,批量生产时必须逐台校准,不然不同机器之间同一个温度点会差出1到2度。温度有偏差,制冷制热切换就会出问题,夏天吹冷风冬天吹热风是用户最不能接受的故障之一。
第二,多点测温的连线和抗干扰。一台中央空调外机可能要同时采集室外环境温度、回气温度、排气温度、盘管温度,用模拟传感器的话每个点都要接两条线到主控板ADC引脚,线束又多又乱,而且电机启停的干扰会导致ADC读数跳变。换成单总线数字传感器,一条线上就能挂多个传感器,数据本身就是数字信号,抗干扰能力明显强很多。
第三,远程集中监控。HVAC系统往往有多台设备分布在建筑的不同区域,传统做法是各设备独立运行、现场看灯板,维护人员要一台台跑。现在我让每台设备的控制器作为远程节点,通过RS485或者CAN总线把温度数据统一汇总到上位机,就能在一个屏幕上看到所有温度点。PIC18LF45K80 芯片内部自带CAN控制器,做这种总线汇聚非常顺手。
2. 硬件选型解析:为什么是这两颗芯片
2.1 PJ85718DM 传感器的核心特性
PJ85718DM 是一颗单总线数字温度传感器,封装不大,外围电路极其简单。它的核心特性我大概梳理了一下:
- 测温范围覆盖了HVAC和常规嵌入式场景,工业级温度范围-40℃到+125℃绰绰有余。
- 分辨率可编程,最高支持12位,对应温度精度0.0625℃,做恒温控制和低温报警都够用。
- 采用单总线协议,主机只需要占用一个GPIO口,就能在一条总线上挂多个传感器。
- 每一颗传感器出厂时都带有唯一的64位序列号,总线上的节点识别完全靠这个ID来区分。
- 供电方式支持外部供电和寄生供电两种,灵活性比较高。
实际使用下来,我最看重的就是它的一根线多节点能力。比如我需要在机组上测三个温度点,以前要占用三路ADC通道,现在一个IO口搞定,PCB布线和结构走线都轻松很多。
2.2 PIC18LF45K80 的资源盘点
PIC18LF45K80 属于8位低功耗单片机,它吸引我的地方是外设特别齐全,尤其是通信接口方面:
- 支持XLP超低功耗技术,待机电流能做到很低,适合电池供电的嵌入式记录仪。
- 集成CAN控制器,对于需要走CAN总线的HVAC远程采集节点,省掉一颗外部独立CAN芯片。
- 多个UART模块,可以一路接传感器调试串口,一路接RS485总线。
- 内部振荡器可以工作在32MHz,跑单总线时序要求的高精度延时完全没有压力。
很多人会觉得8位机在现在这个时代是不是太弱了。其实对于温度采集这种任务,8位机是完全够用的。我反而不太建议在这个场景里硬上Cortex-M处理器,资源过剩、功耗更大、成本也高。PIC18LF45K80 的定位非常精准:我只需要在一个稳定的MCU上完成GPIO时序模拟、协议解析和总线转发,它的算力刚好满足,还有余量处理一些简单逻辑。
2.3 这套组合的选型逻辑与替代方案对比
选这套方案是综合考虑出来的。我把几个常见方案放在一起比过:
| 方案 | 测温方式 | 多节点能力 | 远程组网 | 成本 | 使用感受 |
|---|---|---|---|---|---|
| NTC + ADC | 模拟量 | 差,一节点一路ADC | 难 | 低 | 需要校准,一致性差 |
| I2C温湿度传感器 | 数字量 | 一般,I2C地址有限 | 中 | 中 | 线多,长线布线麻烦 |
| PJ85718DM + MCU UART | 数字量 | 强,一条线多节点 | 强 | 中低 | 外围少,可靠性稳定 |
| 独立温度变送器 | 模拟量/数字量 | 一般 | 一般 | 高 | 成本高,适合工业现场 |
单总线方案在HVAC这种“需要多个温度点又想低成本组网”的场景里,优势是很明显的。PIC18LF45K80 的自带CAN控制器更是锦上添花,如果是多机联调的HVAC集中控制系统,可以直接省掉一个CAN接口扩展芯片。
3. 电路连接与驱动实现
3.1 最小系统接线与供电方式
PJ85718DM 最小系统只有三个引脚需要连接:电源VDD、数据DQ、地GND。实际项目中我优先选择外部供电方式,而不是省事的寄生供电,原因有三点:
一是外部供电下传感器的测温电流更大,转换更稳定;二是寄生供电在长线传输时容易因为电流不足导致启动失败;三是外部供电可以单独加去耦电容,降低总线上的电源噪声。
具体接线是这样的:VDD直接接3.3V,DQ引脚上接一个4.7kΩ上拉电阻到3.3V,再连接到PIC18LF45K80的一个GPIO,GND共地。如果DQ线要拉出去到设备外部较远的位置,我习惯在传感器端额外加一个0.1μF的陶瓷电容跨接在VDD和GND之间,给温度转换时的电流尖峰一个缓冲。
PIC18LF45K80 这端只需要配置GPIO方向、保持默认输出高电平,然后把上拉电阻选合适,硬件上基本就完成了。
3.2 上拉电阻阻值到底怎么算
很多人抄电路图时习惯性就用4.7kΩ,从来不管为什么,直到某天总线上挂了8个传感器就异常了,才开始怀疑电阻。上拉电阻的选型,本质是要保证两个矛盾指标的平衡:上升沿要够快,同时静态电流不能太大。
单总线通信时,主机和传感器都是开漏输出,高电平完全靠外部上拉电阻去拉。电阻越大,总线RC常数越大,上升沿越慢,超过时序要求就容易读到错误的位。电阻越小,信号边沿越陡,但功耗和信号反射会变大。
我用一个实用估算方法:总线等效电容包括导线电容和每个器件的输入电容,按经验值导线约50~100pF/m、每个传感器约50pF。短距离单节点用4.7kΩ完全没问题;如果总线总电容超过500pF,就把上拉降到2.2kΩ;超过1000pF,我会考虑改用1kΩ或者加总线驱动器。
总线上挂多个传感器时,我不推荐一味减小电阻解决所有问题,因为每个器件的输入漏电流不一样,阻值太小反而影响低电平的判定。正确做法是控制总线长度、减少分支线,而不是靠电阻硬撑。
3.3 单总线底层时序:初始化、读位、写位
单总线协议看起来简单,其实所有时序都以微秒为单位,写驱动时对延时的准确性要求还是比较高的。我最先在别的MCU上移植时吃过亏,后来在PIC18LF45K80上老老实实按数据手册的时序来写,用内部振荡器配合空循环加延时函数。
初始化时序最严格:主机先把总线拉低至少480μs,然后释放并等待60μs,传感器收到复位脉冲后会拉低总线60-240μs作为存在脉冲,主机在这段时间内读总线电平就能确认设备在线。
写时序分写0和写1:写0时主机拉低总线60-120μs,然后释放;写1时主机拉低总线1-15μs,然后释放并保持高电平至少60μs。
读时序统一由主机发起:主机先把总线拉低1-15μs,然后释放,传感器如果发0就继续拉低总线,如果发1就把总线放开,主机必须在拉低后的第15μs内采样一次。
下面是PIC18LF45K80上的基础驱动代码,用两个宏控制IO方向:
#define DQ_OUT_LOW() do { TRISB &= 0xFE; LATB &= 0xFE; } while(0) #define DQ_OUT_HIGH() do { TRISB &= 0xFE; LATB |= 0x01; } while(0) #define DQ_IN() do { TRISB |= 0x01; } while(0) #define DQ_READ() (PORTB & 0x01)复位和读位函数如下:
uint8_t onewire_reset(void) { uint8_t presence = 1; DQ_OUT_LOW(); delay_us(500); DQ_IN(); delay_us(60); presence = DQ_READ(); delay_us(500); return presence == 0; } uint8_t onewire_read_bit(void) { uint8_t bit = 0; DQ_OUT_LOW(); delay_us(6); DQ_IN(); delay_us(9); bit = DQ_READ(); delay_us(55); return bit; }这一层是整个传感链路的地基,时序对了后面一切顺利,时序差一点传感器偶尔会幽灵性掉线。
3.4 温度转换命令与数据处理
驱动层跑通后,读取一次温度的标准流程是:
- 发送复位脉冲,确认传感器在线。
- 发送跳过ROM命令0xCC,如果总线上只有一个传感器就不需要做ROM匹配。
- 发送温度转换启动命令0x44,12位模式下需要等待约750ms。
- 再次复位,发送跳过ROM命令后发送读暂存器命令0xBE。
- 连续读取两个字节,低字节在前,高字节在后。
原始数据是12位有符号数,左对齐存储在16位寄存器里,实际分辨率是0.0625℃。转换公式非常直接:将原始值右移4位得到有符号整数,再乘以0.0625就是实际温度。
我还做过一个查表简化版本,把-40℃到+125℃的整数温度与原始高八位对应关系直接放在Flash里,省去每次浮点运算。在8位机上浮点运算虽然能跑,但没必要。
读取代码核心部分大概是这个样子:
int16_t read_temperature(void) { int16_t raw = 0; uint8_t lsb, msb; onewire_reset(); onewire_write_byte(0xCC); onewire_write_byte(0x44); delay_ms(750); onewire_reset(); onewire_write_byte(0xCC); onewire_write_byte(0xBE); lsb = onewire_read_byte(); msb = onewire_read_byte(); raw = (msb << 8) | lsb; return raw >> 4; } float temp = (float)raw_int * 0.0625f;需要注意负数温度处理,如果原始数据高字节最高位为1,说明温度在0度以下,右移时必须做算术右移保持符号位,再用浮点计算时结果就是负数,误删符号位会直接读出一个很大的正数。
3.5 远程组网:从点对点到总线通讯
本地读温度只是第一步,要做到标题里说的“远程温度监测”,还需要把数据送到更远的控制端。我在项目里尝试了两种方式,分别适配不同场景。
第一种是UART转RS485。PIC18LF45K80 的UART引脚外接一个RS485收发芯片,所有温度采集节点挂在同一对差分总线上,主机轮询每个节点,节点收到读温命令后返回数据。RS485抗干扰能力强,传输距离能到几百米,楼宇HVAC这种环境非常适用。
第二种是直接用芯片自带的CAN控制器。PIC18LF45K80 有模块,不需要外接独立CAN控制器,只需要一颗CAN收发器芯片就能组网。CAN总线在多个节点同时请求、优先级仲裁方面天然有优势,节点可以主动上报温度,不像RS485那样必须严格轮询。
我最终采用RS485做远程主链路,因为HVAC现场很多已有的控制器都带RS485接口,兼容性更好。CAN作为设备内部高速总线,用于多个数据采集板之间的互联。
控温协议我用的是简化主从模式,帧格式这样定义:
- 帧头:0xAA 0x55
- 设备ID:1字节,1~247
- 功能码:1字节,0x01表示读温度
- 数据长度:1字节
- 数据域:N字节
- CRC16校验:2字节,低字节在前
主机发出查询帧时广播所有节点都收,但只有设备ID匹配的从机才回复温度数据。从机不主动上报,从根源上避免总线冲突。CRC16校验非常关键,没有它光靠帧头无法判断数据是否在传输中被污染。
4. HVAC与嵌入式场景的落地细节
4.1 嵌入式本地测温的常见部署方式
本地温度监测,我一般会在设备主控板上预留一到两路单总线端口。一路专门测板内关键器件温度,另一路引到机箱外面测环境温度。PIC18LF45K80 的GPIO足够充裕,这样分配完全不影响其他功能。
做嵌入式设备时,温度传感器摆放位置往往比选型更重要。散热片旁边和风口正下方的温度可能差好几度,传感器旁边如果有开关电源的磁性元件,工作时发热也会直接影响读数。我建议在结构设计阶段就确定传感器位置,不要等PCB回来再想怎么飞线。
板级测温的另外一个细节是采样周期。嵌入式设备不像HVAC那样对温度控制实时性要求极高,一般1到5秒读一次就够。PIC18LF45K80 在每次温度转换的750ms期间可以进入休眠状态,用定时器唤醒再取数,能明显压低平均功耗。
4.2 HVAC设备端温度采集的三个关键测量点
HVAC设备里需要测温的地方不少,我一般在三个方面优先部署:
- 回风温度:用于判断室内实际工况,决定制冷制热模式切换。
- 送风温度:用于控制风机转速和辅助电加热启停。
- 盘管进出口温度:判断换热效率,防止盘管冻结和冷媒异常。
这三个温度点如果都用NTC,光是标定三组温度曲线就能占掉不少生产工时。换成PJ85718DM以后,生产线上只要确认每个传感器ID和装机的对应关系就行,直接省掉温度校准环节,这个效率提升在生产端非常明显。
HVAC机组里的电磁干扰比较复杂,压缩机启动瞬间电流很大,总线走线要尽量远离动力线。如果不得不贴近动力线,我用屏蔽双绞线传输单总线信号,屏蔽层单端接大地,实测下来读数保持稳定。
4.3 远程温度采集节点的低功耗设计
远程节点不一定一直有电,有些位置是电池供电的,功耗就是硬指标了。PIC18LF45K80 的XLP特性在此时很关键。
我把整个采集流程设计成:默认处于休眠状态,每30秒自动唤醒一次,唤醒后启动一次温度转换,等750ms转换完成后读取并暂存温度,再打开RS485接收使能监听主机查询。如果主机有查询命令就立刻应答,没有查询就继续休眠。
实测低功耗配置下,休眠电流在微安级别,主要的功耗来自温度转换的那750ms和通信时的收发电流。这样算下来,两节AA电池撑大半年是可行的,具体取决于主机轮询的频率。
4.4 主机端数据汇聚与工程部署
远程监测不能只把数据发出去就完事,主机端还要有一套完整的协议处理来保证数据不乱。我在主控PIC端写了设备状态机,每个从机节点都有在线状态标志和超时计数器,连续三次无响应就标记离线,不阻塞其他节点的正常轮询。
工程部署时,我给每台设备编号,并把ID烧录进控制器的Flash参数区。现场维护时只要按图纸找设备,不用再到总线上一台台猜地址。这个习惯帮我省了无数次现场调试时间。
5. 常见问题排查与调试感受
5.1 传感器一直返回损坏值或者CRC错误
我遇到最多的问题是:明明接线看起来没问题,数据手册上的标准代码也照抄了,读到的温度却永远是那几个固定值,或者CRC校验一直不过。
第一检查复位时序的480μs延时是否准确。很多人在8位机上用标准库延时函数,实际延时因为编译器优化和主频配置不同会缩水,至少用示波器抓一次波形验证实际低电平宽度。第二检查上拉电阻是否焊接或者是否有接触不良。第三看DQ线是否过长或干扰太大,HVAC现场尤其要注意传感器线不能和压缩机动力线走同一个线槽。
我用逻辑分析仪抓波形排查这类问题最快,重点看复位脉冲之后的传感器回拉脉冲是否存在,如果回拉脉冲的宽度不足60μs,大概率是供电问题。
5.2 同一总线上部分传感器能读、部分不能读
单总线多节点部署时,经常出现“前面的三四个都能读,后面两个就掉线”的情况。这种问题多半不是传感器坏了,而是总线信号质量到后面已经衰减到临界状态。
排查方向是:先量总线静态电平是否正常,再缩短总线长度或换更粗的导线,然后考虑增大上拉能力。还有一种可能是传感器在出厂时ID写异常,这种概率很小但存在,逐个传感器单独连接测试就能发现伪节点。
多节点场景下我建议每增加一个传感器就做一次全量扫描,确认新加的设备ID不会和已有设备重复。
5.3 RS485远程通信偶发丢包
RS485远程通信进入HVAC现场后,出现偶发丢包是常见韧象。先检查A/B线是不是接反,这能从设备上一眼看出来。再用示波器看总线波形,A和B之间的差分电平是否正常。
终端电阻是另一个高频问题。RS485总线规范要求在物理两端各加一个120Ω电阻,很多人只在一端加了甚至完全没加,长线传输时波形反射就会导致误码。距离短的可以不严格,距离超过50米就必须重视。
波特率误差也要检查。如果主控用的内部振荡器标称32MHz但实际偏差较大,长时间连续传输时累计误差会超出从机容限。我的做法是让所有节点使用同一频率源配置,并且预留频率校准测试项。
5.4 排查速查表
| 现象 | 可能原因 | 关键检查点 |
|---|---|---|
| 读到的温度固定在错误值 | 时序延时不准、上拉电阻过大 | 模拟量复位脉宽,确认上拉 |
| CRC频繁失败 | 干扰、时序边缘余量不足 | 示波器看上升沿,查走线 |
| 部分节点掉线 | 总线过长、驱动能力不足 | 量波形,缩短走线或降阻值 |
| 全总线无法通信 | 供电异常、IO配置错误 | 量VDD/GND,排查MCU引脚 |
| RS485丢帧 | A/B反接、缺终端电阻 | 查接线、补120Ω电阻 |
| 远程数据明显偏大 | 传感器与被测点距离太远 | 调整传感器位置,避免热源直射 |
我个人在实际项目里还有一个习惯:所有单总线传感器在焊接到主板之前,先用一个简易测试座全部读一遍ID并记录下来。这样主板下线后如果出现某个点位异常,我直接对照ID就能判断是传感器问题还是电路问题,对排障效率的提升非常明显。
整套方案从器件选型到远程组网跑通,前后大约花了不到两周时间。PJ85718DM 和 PIC18LF45K80 的组合在HVAC多温度监测场景下属于性价比很高的方案,传感器外围元件少、单片机外设覆盖全、组网路径清晰,非常适合中小型嵌入式产品和暖通控制项目直接参考。代码部分如果后续需要,我也可以把移除了项目依赖的完整驱动和协议栈单独分享出来。