1. 项目缘起与整体设计思路
嵌入式温度监测这个方向,看起来简单,实际上坑特别多。我最早接触这类需求是在一个HVAC控制板的项目里,当时客户要求同时监测机房本地温度和远端风管的温度,还要把数据汇总上报。一开始想得很简单,拿个单片机接个传感器就完事了,结果实际做下来才发现,本地测温、远程测温、通信可靠性、供电噪声、传感器一致性,每一个环节都能让你调半天。
这个项目标题里提到的PJ85718DM和PIC18LF46K22,就是一套非常典型的组合方案。PJ85718DM是一颗远程温度传感器,采用SMBus/I2C兼容接口,可以外接一个或多个远端测温二极管(通常是三极管接成二极管形式),适合测量CPU、功率管或者风管等位置的温度。PIC18LF46K22则是Microchip的一款低功耗8位单片机,自带多个I2C/SPI/UART外设,Flash 64KB,RAM接近4KB,在HVAC和工业控制场景里非常常见。两者搭配,一个负责“感知”,一个负责“处理与上报”,分工明确。
为什么选这套组合而不是用NTC加ADC?核心原因有三个。第一,远程测温用二极管方案,抗干扰能力比长线引出的NTC强很多,NTC引线长了以后线阻和噪声会直接吃掉精度。第二,PJ85718DM这类芯片内部已经做了数字化和线性化处理,单片机拿到的是直接可读的温度寄存器值,省掉大量标定工作。第三,PIC18LF46K22的低功耗特性适合HVAC设备常年通电的场景,而且它的外设资源足够同时跑本地传感器、远程传感器和通信协议栈。
整个系统的设计思路可以概括为:本地温度由单片机附近的传感器或芯片自身通道采集,远程温度由PJ85718DM通过远端二极管采集,两者数据在PIC18LF46K22里做融合、滤波、阈值判断,再通过UART或者I2C上报给上位机或主控板。这个架构的好处是模块化清晰,哪一路出问题都容易定位,而且后续扩展通道也方便。
注意:远程测温二极管一定要选正规的MMBT3904或者专用测温三极管,不同批次的管子Vbe特性有差异,会直接影响测温精度。
2. 核心器件解析与选型考量
2.1 PJ85718DM到底能干什么
PJ85718DM的核心价值在于它把远程温度 sensing 这件事做成了“数字输出”。它内部有一个ADC,把远端二极管上的电压转换成数字量,再通过内部算法换算成温度值。它通常支持一路本地温度和一路远程温度,远程那一路可以接二极管连接方式的三极管。通信接口是SMBus/I2C,地址可以通过引脚配置,这意味着同一条总线上可以挂多颗,实现多点测温。
它的测温范围一般覆盖-40°C到+125°C,远程通道的精度在常温附近可以做到±1°C左右,具体要看二极管的一致性和走线质量。分辨率通常是可配置的,比如0.125°C或者0.25°C,这个在HVAC场景里完全够用,因为HVAC控制一般只关心0.5°C级别的变化。
我在实际使用中比较看重它的一点是:它自带可编程的温度阈值报警。你可以设置过温阈值和欠温阈值,芯片自己会在超限时拉一个报警引脚或者置状态位。这样单片机不用一直轮询,可以省下CPU时间去处理通信和逻辑控制。
2.2 PIC18LF46K22的角色定位
PIC18LF46K22在这套方案里是“大脑”。它要干的事包括:初始化I2C总线、周期性读取PJ85718DM的温度寄存器、读取本地其他传感器(如果需要)、做滑动平均滤波、判断是否超限、控制继电器或者风机、通过UART上报数据。它的低功耗模式(比如Sleep)可以在不需要高频采样时把功耗压下来,适合常年通电的HVAC控制器。
选型时要注意它的供电范围。LF版本支持1.8V到3.6V,如果你的系统是3.3V逻辑,那正好匹配。如果系统是5V,那就得选非LF版本或者加电平转换。另外它的I2C引脚是开漏结构,上拉电阻必须加,典型值4.7kΩ,总线速度快的时候可以降到2.2kΩ。
2.3 为什么不用单片机内部温度传感器
很多单片机自带内部温度传感器,但那个精度通常只有±3°C甚至更差,而且测的是芯片自身温度,不是环境温度。在HVAC应用里,你关心的是风管温度或者房间温度,不是PCB温度。所以内部传感器只能做粗略的过温保护,不能做真正的环境监测。PJ85718DM加远端二极管的方式,可以把测温点延伸到实际需要的位置,这是它最大的优势。
2.4 通信总线的选择与权衡
I2C和SMBus在电气上兼容,但SMBus有超时机制,I2C没有。PJ85718DM通常兼容两者。在HVAC环境里,总线可能走较长距离,这时候要注意总线电容不能超过400pF,否则波形会变差。如果距离确实长,可以考虑降低总线速度到100kHz甚至更低,或者用I2C缓冲器。我在一个项目里因为总线走了将近40cm,最后把速度降到50kHz才稳定下来。
3. 硬件设计与实操要点
3.1 远端二极管的接法与走线
远端测温二极管一般用MMBT3904,把基极和集电极短接,用基极-发射极结作为测温元件。接法上,PJ85718DM的D+和D-分别接到二极管的基极和发射极。走线要用差分对的方式,尽量靠近,远离功率开关节点。如果风管里走线很长,建议用屏蔽线,屏蔽层单点接地。
我踩过的一个坑是:一开始把二极管放在风管里,线走了将近一米,结果读数跳动超过2°C。后来改成双绞线,并且在D+和D-之间加了一个100pF的滤波电容,跳动降到0.3°C以内。这个电容不能太大,否则会影响芯片的采样时序。
3.2 电源去耦与噪声控制
PJ85718DM的供电引脚必须加0.1μF陶瓷电容,位置尽量靠近芯片。如果板上还有继电器或者电机驱动,电源走线要分开,避免大电流路径经过传感器芯片的电源引脚。PIC18LF46K22的VDD和VSS也要加去耦,每个电源引脚一个0.1μF,整体再并一个10μF。
HVAC板子上经常有24VAC或者更高的电压,所以隔离和爬电距离要注意。传感器部分如果和高压部分共板,建议做开槽或者加隔离带。
3.3 上拉电阻的计算
I2C总线的上拉电阻值取决于总线电容和上升时间要求。标准模式100kHz下,上升时间要小于1000ns。假设总线电容200pF,那么上拉电阻最大值大约是:
Rmax = tr / (0.8473 × C) = 1000ns / (0.8473 × 200pF) ≈ 5.9kΩ
所以4.7kΩ是安全的。如果总线电容更大,比如400pF,那Rmax就降到2.9kΩ左右,这时候选2.2kΩ比较稳妥。但电阻越小,静态功耗越大,低功耗场景要权衡。
3.4 地址配置与多芯片挂载
PJ85718DM通常有地址选择引脚,可以配置成不同的I2C地址。如果你要测多个位置,比如回风、送风、室外,那就挂三颗,地址分别设成不同值。PIC18LF46K22的I2C外设支持多主从,读取时依次访问不同地址即可。注意总线上所有设备的上拉电阻只需要一组,不要每个芯片都加。
4. 固件实现与核心代码解析
4.1 I2C底层驱动初始化
PIC18LF46K22的I2C外设配置需要注意几个寄存器:SSPCON1、SSPCON2、SSPADD、SSPSTAT。假设系统时钟16MHz,I2C要跑100kHz,那么SSPADD的值计算如下:
SSPADD = (Fosc / (4 × Fscl)) - 1 = (16MHz / (4 × 100kHz)) - 1 = 39
所以SSPADD写39。初始化时还要把SCL和SDA引脚配置成输入,并且使能I2C模式。
// PIC18LF46K22 I2C初始化示例 TRISCbits.TRISC3 = 1; // SCL TRISCbits.TRISC4 = 1; // SDA SSPCON1 = 0x28; // I2C主模式 SSPCON2 = 0x00; SSPADD = 39; // 100kHz @ 16MHz SSPSTAT = 0x80; // 标准速度模式这段代码是基础,但实际项目里我建议加一个总线恢复函数。因为如果从设备在通信中途掉电或者复位,可能会把SDA拉低不放,导致总线死锁。恢复方法是:把SCL配置成普通IO,手动发9个时钟脉冲,然后发一个Stop条件。
4.2 读取PJ85718DM温度寄存器
PJ85718DM的温度寄存器通常是16位,高字节是整数部分,低字节是小数部分。读取流程是:发Start、发设备地址+写、发寄存器地址、发Repeated Start、发设备地址+读、读两个字节、发Nack、发Stop。
uint16_t read_remote_temp(uint8_t dev_addr) { uint8_t msb, lsb; I2C_Start(); I2C_Write(dev_addr << 1); // 写地址 I2C_Write(0x01); // 远程温度寄存器 I2C_RepeatedStart(); I2C_Write((dev_addr << 1) | 1); // 读地址 msb = I2C_Read(); I2C_Ack(); lsb = I2C_Read(); I2C_Nack(); I2C_Stop(); return ((uint16_t)msb << 8) | lsb; }拿到原始值后,温度换算要看数据手册。常见格式是:高字节的bit7到bit0是整数,低字节的高4位是小数,每单位0.0625°C。所以温度 = (int8_t)msb + (lsb >> 4) * 0.0625。如果是负温度,高字节是补码形式,要先转成有符号数。
4.3 滑动平均滤波的实现
原始读数会有噪声,直接用来控制继电器会导致频繁启停。我一般用8点滑动平均,RAM里开一个环形缓冲区,每次新数据进来替换最老的数据,然后求平均。这样计算量小,效果也够用。
#define FILTER_SIZE 8 int16_t temp_buf[FILTER_SIZE]; uint8_t buf_idx = 0; int16_t filter_temp(int16_t new_temp) { int32_t sum = 0; temp_buf[buf_idx] = new_temp; buf_idx = (buf_idx + 1) % FILTER_SIZE; for (uint8_t i = 0; i < FILTER_SIZE; i++) { sum += temp_buf[i]; } return (int16_t)(sum / FILTER_SIZE); }如果温度变化很慢,比如HVAC环境,8点平均足够。如果要求响应快,可以改成4点,或者用一阶低通滤波:new_filtered = old_filtered + alpha * (new_raw - old_filtered),alpha取0.2左右。
4.4 阈值判断与继电器控制
温度数据滤波后,和设定的阈值比较。比如送风温度超过45°C就开风机,低于40°C就关风机。这里要加迟滞,否则会在阈值附近反复开关。迟滞一般设2°C到5°C,看系统热惯性。
if (filtered_temp > 45 && !fan_on) { fan_on = 1; FAN_PIN = 1; } else if (filtered_temp < 40 && fan_on) { fan_on = 0; FAN_PIN = 0; }这段逻辑简单,但实际调试时要注意:如果传感器故障,读数可能是0xFFFF或者0x0000,这时候不能直接用来判断,要先做有效性检查。比如温度超出-40到125的范围就标记为故障,保持上一次有效值或者进入安全模式。
4.5 UART上报与协议设计
PIC18LF46K22的UART配置要设置波特率。假设16MHz时钟,波特率9600,BRG16=1,那么:
SPBRG = Fosc / (16 × Baud) - 1 = 16MHz / (16 × 9600) - 1 ≈ 103
上报协议我一般用简单的帧格式:帧头0xAA、长度、命令字、数据、校验和。校验和用累加和或者CRC8。这样上位机解析起来方便,也不容易出错。
void uart_send_temp(int16_t local, int16_t remote) { uint8_t frame[8]; frame[0] = 0xAA; frame[1] = 0x05; frame[2] = 0x01; // 温度上报命令 frame[3] = (local >> 8) & 0xFF; frame[4] = local & 0xFF; frame[5] = (remote >> 8) & 0xFF; frame[6] = remote & 0xFF; frame[7] = frame[0] + frame[1] + frame[2] + frame[3] + frame[4] + frame[5] + frame[6]; for (uint8_t i = 0; i < 8; i++) { while (!TXIF); TXREG = frame[i]; } }5. 常见问题与排查技巧实录
5.1 温度读数跳动大
这是最常见的问题。原因通常有三个:电源噪声、远端二极管走线干扰、滤波不足。排查顺序是:先用示波器看电源纹波,如果超过50mV,加LC滤波或者换LDO。然后看D+和D-走线是否远离开关节点,必要时加屏蔽。最后检查固件滤波参数,适当增大平均点数。
我遇到过一次,读数每隔几秒跳一次,最后发现是继电器动作时电源跌落导致传感器复位。解决办法是在传感器电源引脚旁边加一个100μF电解电容,并且继电器电源和传感器电源用磁珠隔离。
5.2 I2C通信失败
如果读不到数据,先确认地址对不对。PJ85718DM的地址引脚配置容易搞错,用示波器看SDA和SCL波形,确认Start和地址字节是否正确发出。如果波形正常但从机不ACK,可能是上拉电阻太大或者总线电容太大。把上拉降到2.2kΩ试试。
还有一种情况是总线死锁,SDA被从机拉低。这时候用前面说的9个时钟脉冲恢复。如果经常死锁,考虑加一个I2C缓冲器或者降低总线速度。
5.3 远程温度偏差大
如果本地温度准,远程温度差好几度,先检查二极管型号和接线。MMBT3904的Vbe特性要匹配芯片的预期。如果换了其他型号,可能需要重新校准。另外,二极管附近如果有发热元件,读数会偏高,这是物理问题,只能改布局。
校准方法:在已知温度下(比如冰水混合物0°C或者沸水100°C,注意海拔影响),读取原始值,计算偏移量,在固件里做补偿。但补偿只对特定温度点有效,宽范围还是靠硬件一致性。
5.4 低功耗模式下读数异常
PIC18LF46K22进入Sleep后,I2C外设会停止工作。如果要在低功耗下周期性测温,需要用定时器唤醒,唤醒后重新初始化I2C,读一次,再睡。注意PJ85718DM本身也有低功耗模式,如果它被配置成关断,唤醒后要等它转换完成再读。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 读数跳动大 | 电源噪声 | 示波器看VDD纹波 | 加去耦电容、LC滤波 |
| 读数跳动大 | 远端走线干扰 | 检查走线是否远离功率线 | 双绞线、屏蔽线、加滤波电容 |
| I2C无响应 | 地址错误 | 示波器看地址字节 | 核对地址引脚配置 |
| I2C无响应 | 上拉电阻过大 | 测量上升时间 | 减小上拉电阻 |
| 远程温度偏差 | 二极管型号不对 | 核对数据手册 | 换推荐型号或校准 |
| 远程温度偏差 | 二极管附近热源 | 红外测温枪确认 | 改布局或加隔热 |
| 低功耗唤醒后无数据 | I2C未重新初始化 | 检查唤醒流程 | 唤醒后重新配置I2C |
| 继电器频繁动作 | 无迟滞 | 观察温度曲线 | 加2-5°C迟滞 |
提示:调试温度问题时,先用已知温度源(比如恒温槽或者冰水)验证单点精度,再上系统看动态表现。不要一上来就怀疑芯片,大部分问题出在电源和走线上。
6. 系统集成与场景延展
6.1 HVAC中的典型部署方式
在HVAC控制板里,这套方案通常这样部署:PIC18LF46K22作为主控,PJ85718DM挂在I2C总线上,远端二极管贴在送风管和回风管上。本地温度可以用另一颗PJ85718DM的本地通道,或者用一颗数字温度芯片比如LM75。数据经过滤波后,一路用于本地风机控制,一路通过UART或者RS-485上报给楼宇自控系统。
如果系统需要多点测温,比如三个房间,那就挂三颗PJ85718DM,地址分别配置。PIC18LF46K22轮询读取,每颗之间间隔10ms,避免总线冲突。整个轮询周期控制在100ms以内,对HVAC来说完全够用。
6.2 工业嵌入式中的扩展用法
除了HVAC,这套组合也适合工业嵌入式场景,比如电机控制器监测功率管温度、电源模块监测散热器温度。远端二极管可以直接贴在功率器件旁边,实时监测结温。PIC18LF46K22的CAN或者SPI接口可以用于和主控通信,把温度数据集成到整个系统里。
如果测温点距离主控超过一米,I2C就不太合适了,这时候可以考虑用PJ85718DM的报警输出引脚做中断,或者换用支持长距离的传感器接口。但一米以内,I2C加屏蔽线还是稳的。
6.3 校准与长期稳定性
温度传感器用久了会有漂移,尤其是远端二极管,受湿度影响可能漏电。建议在固件里加一个自检机制:定期读取芯片内部温度或者参考电压,如果偏差超过阈值就报故障。另外,产品出厂前最好做一次单点或者双点校准,把偏移量写到EEPROM里,运行时补偿。
长期稳定性方面,PJ85718DM本身漂移很小,主要风险在二极管和走线。如果应用环境湿度大,二极管引脚要涂三防漆,避免漏电导致读数偏低。
6.4 成本与替代方案对比
这套方案的成本大概在几块钱到十几块钱人民币,取决于采购量和芯片版本。如果成本压力大,可以用NTC加ADC,但精度和抗干扰会下降。如果要求更高精度,可以用PT100加专用ADC,但成本和电路复杂度都上去了。PJ85718DM加PIC18LF46K22的定位是“性能够用、成本可控、开发简单”,在HVAC和一般工业场景里性价比很高。
我在实际项目中对比过NTC方案和二极管方案,同样走线长度下,NTC的读数波动是二极管方案的3倍以上,而且NTC需要逐个校准,生产工时也上去了。所以只要预算允许,优先选数字传感器方案。
6.5 固件升级与维护考虑
PIC18LF46K22支持自编程,可以通过UART或者I2C做固件升级。建议在固件里预留Bootloader区域,方便现场升级。温度校准参数和阈值配置可以放在EEPROM里,升级固件时不清除,这样现场更换传感器后只需要重新校准,不用重新烧录整个固件。
另外,建议加一个看门狗,防止程序跑飞导致温度失控。HVAC系统如果风机控制失效,可能会造成设备损坏或者能耗浪费。看门狗超时时间设1秒左右,主循环里定期喂狗。
7. 实操心得与避坑清单
做这类项目,我最大的体会是:硬件设计占七成,固件占三成。很多温度问题不是代码能解决的,必须在PCB布局和走线上就处理好。下面是我总结的几条实操心得,都是踩过坑之后记下来的。
第一,远端二极管的走线一定要短。如果实在要长,用双绞线加屏蔽,屏蔽层接模拟地。D+和D-之间加100pF电容,不要加太大,否则影响采样。
第二,电源去耦不能省。PJ85718DM的VDD引脚旁边必须放0.1μF,距离不超过5mm。如果板上有多路电源,传感器电源最好单独一路LDO,不要和继电器共用。
第三,I2C上拉电阻不要照抄参考设计。参考设计通常用4.7kΩ,但那是针对短总线的。如果你的总线长或者挂的设备多,要重新计算。我一般先用4.7kΩ,如果波形不好再降到2.2kΩ。
第四,固件里一定要加传感器故障检测。读数超出范围、I2C连续N次失败、校验和错误,都要标记故障。故障时输出安全值,比如固定25°C或者保持上一次有效值,避免系统误动作。
第五,调试时先用示波器看波形,再看数据。很多问题看波形一目了然,比如时钟占空比不对、上升沿太慢、有毛刺。数据不对的时候,先怀疑硬件,再怀疑代码。
第六,温度校准要在实际工作温度附近做。如果你在25°C校准,但实际工作在60°C,那校准可能不准。有条件的话,在多个温度点做校准,拟合一条曲线。
第七,低功耗设计要算总账。PIC18LF46K22本身功耗低,但如果你让它一直跑I2C轮询,功耗也下不来。合理利用Sleep和定时器唤醒,平均功耗可以做到几百微安。
第八,EMC测试要提前考虑。HVAC设备通常要做CE或者类似认证,温度传感器的走线如果太长,容易成为天线。加共模电感或者磁珠,可以改善辐射发射。
最后再分享一个小技巧:如果你不确定远端二极管接法对不对,可以先拿一个已知良好的MMBT3904,在室温下读一次,然后用手捏住管子,看读数是否上升。如果上升明显,说明测温通道工作正常。这个方法虽然土,但非常有效,能快速判断是硬件问题还是软件问题。
这个方案后续还可以扩展:比如加一个LCD或者OLED做本地显示,加一个EEPROM记录温度历史,或者加一个无线模块做远程上报。核心的测温部分不变,外围按需增减就行。