做HVAC控制板这几年我试过不少测温方案最费时间的往往不是PID怎么调而是现场温度数据到底准不准、稳不稳。控制器机箱里的本地温度和风道末端、室外机里的远程温度两种测温点的安装环境、线缆长度、干扰来源都不一样如果都靠NTC加ADC去读线一拉长误差能到两三度标定还得一台一台来。这个项目里我改用PJ85718DM数字温度传感器和PIC18F66K40单片机配合通过I2C总线在一条链路上挂多个传感器把本地与远程温度一起管起来。正在做嵌入式测温、暖通控制或者环境监测的朋友可以重点参考多点组网、远程布线和干扰排查这几部分。1. 项目定位与整体设计思路1.1 本地温度与远程温度两种需求不能混着做在HVAC这类场景里“本地”和“远程”这两个词不是随便叫的。本地温度特指MCU控制板附近的温度比如变频驱动器散热器、机箱内部空气温度、电源模块旁边。这个测点的任务是保护控制器本身要求采样响应快能及时反映板卡是否过热避免降额或者损坏。远程温度则是指布设在设备之外、通过线缆延伸出去的测点比如空调送回风管道、冷水机组进出水管表面、多联机各房间的室内机温度。这些点离MCU少则几十厘米多则十几米线缆带来的压降和空间电磁噪声是绕不开的问题。两种需求如果硬塞进同一个方案通常会顾此失彼。NTC本身是非线性的走线一长读取误差会被放大而且每根线的阻抗不一样换一根线读数就变。数字传感器把模拟量在探头端就转换成数字数据抗线损能力明显好很多。所以这个项目的第一个决定就是所有测温点全部采用数字传感器MCU只负责通过I2C总线去读不再用ADC逐个换算。1.2 为什么要用PJ85718DM PIC18F66K40选型时我列过几个候选NTC加MCU内置ADC、一线制数字温度传感器、PJ85718DM这类带I2C接口的数字传感器。NTC加ADC方案在短距离、单点测温时很好用成本低但HVAC现场线缆长度不固定同样的电阻值在不同线长下误差不一致还得做查表换算多路时更麻烦适合对精度没什么要求的场景。一线制传感器支持远距离和挂载多点不过时序比较敏感CPU容易被时序卡住而且每个点地址不同一个点损坏可能导致总线被拖累。PJ85718DM则是标准I2C接口多个设备可以靠地址位区分我在这块板子上挂了8个总线上只需要SDA、SCL、VCC和GND四根线。传感器内置温度寄存器精度和一致性出厂调好不需要逐颗标定探头省下的测试工时很可观。至于MCU选PIC18F66K40而不是随便拿一颗Arduino或者STM32顶上主要原因有三个这颗MCU带硬件I2C模块MSSPCPU不需要自己模拟时序工作电压范围宽适合HVAC控制板常见的5V供电环境内部有可配置逻辑单元CLC可以把传感器的报警信号直接硬件联动到其他外设不占用CPU轮询。另外它还有Data EEPROM可以把校准参数和节点ID存进去重启不丢。注意这里说的“本地”与“远程”并不是网络意义上的远程控制而是指传感器相对MCU的物理位置。后文里的“远程读取”全部指通过总线读取远端测点的温度这一点先统一口径。2. 硬件设计与接线要点2.1 PJ85718DM最小系统与I2C总线搭建PJ85718DM是标准I2C接口数字温度传感器供电范围大约在2.7V到5.5V之间封装上有SDA、SCL、地址配置引脚ADDR和报警输出引脚ALERT。最小系统看起来很简单但有几个细节容易踩坑。去耦电容每个传感器的VCC和GND之间要放0.1uF陶瓷电容尽量靠近芯片引脚。有些朋友觉得数字传感器功耗低省掉电容结果远程线缆一长电源波动直接导致读数跳动。上拉电阻I2C总线的SDA和SCL必须有上拉电阻。阻值不是随便放一个4.7k就完事需要考虑总线上挂的设备数量和线长。总线电容越大上拉电阻就要越小否则上升沿变慢但太小又会增加低电平灌电流。按100kHz通信速度、总线电容100pF到300pF估算4.7k到10k都在可用范围。如果线缆超过2米我通常用4.7k同时把通信速率降到100kHz而不是继续用400kHz去赌运气。ADDR引脚PJ85718DM的I2C地址可以通过ADDR引脚接高、接低或悬空来配置。多路测温时每个传感器要预先设置不同地址避免总线上冲突。我的习惯是远程传感器用拨码开关或者跳线帽选地址产线上不用烧录就能改地址调试效率高很多。布局方面本地传感器放在MCU同一块PCB上离MCU至少2厘米远离电源电感和散热器远程传感器则通过带锁扣的接线端子引出线缆走屏蔽双绞线。2.2 远程传感器的线缆布局与抗干扰远程测温最怕的不是传感器本身不准而是线缆把干扰带进来。HVAC现场变频器多、继电器多启动和关断瞬间的电磁脉冲很容易耦合到长线缆上。我的做法是SDA、SCL、VCC、GND四根线用屏蔽双绞线双绞线负责抗共模干扰屏蔽层只在一端接地避免形成接地环路传感器端外壳不要直接接屏蔽层否则容易出现地电位差带来的电流。线缆长度超过5米时还需要在传感器端加一个小阻容滤波器比如串联33欧姆电阻再对地并联100pF电容。不过要注意这个滤波电容会直接影响I2C的上升沿加了之后通信速率适当降到100kHz实测下来读数稳定很多。电源线的载流量按总线上所有传感器数量估算每个PJ85718DM的功耗很小但多颗挂在一起时别用太细的线。如果传感器节点分散在不同位置建议在远端单独布一组电源线和地线不要在总线上串成菊花链减小远端压降。2.3 硬件自检与上电确认硬件焊完先别急着写代码。我的习惯是用万用表量一下每颗传感器VCC和GND之间有没有短路尤其是远程接线端子焊接完成后。只接一颗传感器拿逻辑分析仪或示波器抓一下SDA和SCL的波形确认上拉正常。然后用单片机扫描I2C地址看能否发现传感器。给PIC18F66K40写一个简单的地址扫描程序把所有可能的I2C地址遍历一遍能读到的地址逐一打印出来。这个步骤看起来笨但能省下后面排查问题的半天时间。如果地址扫描发现少了一颗多半是ADDR焊错了或者走线断了如果总线上完全没响应先查上拉电阻和电源再看是不是SDA和SCL接反了。3. 固件实现I2C读取温度与多点轮询3.1 初始化PIC18F66K40的MSSP模块PIC18F66K40的MSSP模块可以工作在I2C主机模式。初始化时主要做三件事设置引脚功能、配置波特率、使能模块。引脚方面SDA和SCL需要通过PPS外设引脚选择映射到对应引脚。波特率寄存器SSPADD的计算公式是FOSC / (4 * (SSPADD 1))如果系统时钟用16MHz想要100kHz的I2C速率那么SSPADD1 16000000 / (4 * 100000) 40SSPADD 390x27。void i2c1_init(void) { // 将SCL对应引脚的PPS映射为MSSP1时钟 SCLPINPPS 0x1C; // 示例映射实际引脚需要按芯片封装调整 // 将SDA对应引脚的PPS映射为MSSP1数据 SDAPINPPS 0x1D; // 配置MSSP1为I2C主机模式100kHz SSP1CON1 0x28; // 0b00101000: I2C主机模式, 使能模块 SSP1ADD 0x27; // 16000000 / (4 * 100000) - 1 39 }这里有个细节PPS映射的数值要查芯片数据手册的引脚功能表不同封装同一功能对应的值不同。我写代码时一般从厂商例程库复制映射值再改成自己实际用的引脚。3.2 读取PJ85718DM温度数据的完整流程PJ85718DM内部有指针寄存器和配置寄存器读取温度的基本流程是先把指针寄存器指向温度数据寄存器然后连续读取两个字节得到16位温度数据再按分辨率换算成实际温度。如果传感器配置为12位分辨率数据寄存器的高5位是符号位低12位是温度数据每LSB对应0.0625°C。温度值换算成浮点static float read_temperature(uint8_t dev_addr) { uint8_t raw[2] {0}; int16_t value; i2c1_start(); // 写入指针寄存器指向温度寄存器 i2c1_write(dev_addr 1); i2c1_write(0x00); i2c1_stop(); i2c1_start(); // 读两个字节数据 i2c1_write((dev_addr 1) | 0x01); raw[0] i2c1_read(1); raw[1] i2c1_read(0); i2c1_stop(); value ((int16_t)raw[0] 8) | raw[1]; value 4; // 去掉低4位得到12位有符号数据 return (float)value * 0.0625f; }注意不同厂商的温度传感器在数据对齐上可能有差异有的把12位数据左对齐有的右对齐。我用的是右对齐方式如果你的传感器原始数据分布不同需要调整移位方向。这类问题在低温读数上尤其明显比如0°C以下读出来乱跳多半就是符号位处理错了。3.3 报警阈值与多点轮询逻辑PJ85718DM的ALERT引脚可以配置为过温报警也可以做成窗口比较器。HVAC场景里我通常把两个阈值分开用本地传感器用比较模式温度超过设定的散热上限就触发ALERT远程传感器用窗口模式超出回风温度上下限就报警。因为有多颗传感器而ALERT输出通常是漏极开路多个传感器可以共享一根报警线只要有一个报警就会把线拉低。PIC18F66K40这边可以用外部中断或者CLC来接这根线走硬件联动不占用CPU轮询。多点轮询的逻辑比较简单#define TEMP_NODE_MAX 8 uint8_t node_addr[TEMP_NODE_MAX] { 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F }; float temp_result[TEMP_NODE_MAX]; void poll_all_temperature(void) { uint8_t i; for (i 0; i TEMP_NODE_MAX; i) { temp_result[i] read_temperature(node_addr[i]); // 简单滑动滤波连续读三次取平均 temp_result[i] read_temperature(node_addr[i]); temp_result[i] read_temperature(node_addr[i]); temp_result[i] / 3.0f; } }远程节点比较多时每次轮询之间加一点延时给传感器足够的转换时间。数据手册上给出的默认转换时间在几十毫秒量级我实际轮询8个节点时把循环周期设计成200ms左右既能满足HVAC的控制需求又不会把总线占得太满。4. 实测校准、问题排查与避坑实录4.1 现场实测数据与软件校准装好之后我在模拟HVAC环境里做了几组测试本地传感器放在控制板附近远程传感器分别接1米、3米、8米线缆贴在风道外壁和浸入水温参考槽。用了一根精度0.1°C的铂电阻温度计做参照。实测结果大体在±0.5°C以内这与PJ85718DM本身的标称精度相符。但有两个现象值得注意第一距离越远读数受到环境电磁干扰影响越大。同样的传感器8米线缆在变频器附近启动时瞬时读数偶尔会比稳定值跳0.3°C左右。加软件滤波之后基本消除。第二传感器紧贴金属表面测温时读数会略高于空气温度这是因为传感器自身功耗产生轻微自热加上金属导热带来的接触温差。如果追求绝对准确需要把传感器轻贴在表面再覆盖一层隔热泡棉减少对流传热。软件校准可以用最简单的两点法把传感器放到0°C冰水混合物和50°C水浴里记录两次读数然后按公式校准T_cal a * T_raw b其中a是斜率修正b是零点偏移。这个标定过程只用做一次把a和b存到PIC18F66K40的Data EEPROM里产线每台设备最多多花两分钟但现场一致性会好很多。4.2 高频故障与排查速查表这个项目从样机到量产我整理过一张排查表列一下碰到频率比较高的几类问题现象可能原因排查方法总线上完全扫描不到设备上拉电阻漏焊或SDA/SCL接反示波器抓波形测量上拉电阻两端电压只有部分传感器能读到地址冲突或ADDR引脚没接对单独焊一颗做地址扫描确认地址位读出来的温度明显偏高传感器贴住了发热源或自热影响检查传感器安装位置加隔热垫远距离读数偶尔跳变线缆干扰或滤波电容过大拉慢上升沿换屏蔽双绞线加软件滤波适当降速ALERT一直触发或一直不触发阈值寄存器写入顺序不对确认先配置阈值再使能报警查中断标志位上电后总线忙SDA被拉低某颗传感器损坏或外部器件占住总线逐颗断开排查用万用表量SDA电平这一类问题其实有个共性如果能确认硬件电源和接线都没问题先把通信速率降下来、把滤波打开、把轮询间隔给足很多“幽灵问题”都是时序和干扰叠加造成的。4.3 几个只有做过才知道的细节第一PJ85718DM的ALERT脚在配置完成之前可能会默认触发。如果上电后立即去读阈值寄存器读到的是不确定值所以我的固件启动流程固定为初始化I2C - 配置报警模式 - 写阈值 - 延时10ms以上 - 再读取状态寄存器确认报警条件清零。第二远程传感器千万不要在带电状态下插拔。虽然传感器本身容错性还可以但总线上的电缆电感非常容易在插拔瞬间产生尖峰少则造成数据错误多则打坏相邻节点。我在设计里用了防反接连接器现场维护必须断电操作。第三关于NVM保存节点ID和校准参数。PIC18F66K40的Data EEPROM写寿命有限虽然正常写入足够用很多年但不要在主循环里频繁改写只在收到参数更新命令时才写避免磨损。5. 扩展空间与个人实操心得5.1 把校准参数和节点配置存进Data EEPROM我用Data EEPROM保存的内容有三类节点地址表、每颗传感器的校准系数a和b、ALERT阈值配置。这样做的直接好处是固件升级时不会把标定数据冲掉。写EEPROM时要注意PIC18F66K40写入一个字节需要等待内部写完成写之前还要关闭中断防止写入过程中被打断。我通常把参数结构体打包成一串固定长度的字节写入后回读校验不对就重写。// 示例保存校准系数 void eeprom_write_cal(uint8_t node, float a, float b) { // 等待上次写入完成 while (WR 1); // 设置地址和数据 // 关闭全局中断 INTCONbits.GIE 0; // 按芯片数据手册执行写入序列 // 重新使能中断 INTCONbits.GIE 1; }现场维护时一个节点坏了换上新的传感器后只要把该节点的校准系数重新写一次就行不用整机重校准维护工时能省不少。5.2 用CLC把ALERT直接联动到执行机构PIC18F66K40的CLC可配置逻辑单元在这个项目里也是一个关键点。如果HVAC系统里过温时需要直接切断继电器或者打开旁通风门不需要等CPU轮询到报警位再处理。把ALERT引脚配置成CLC的输入CLC输出接外部中断或者直接驱动一个信号输出脚传感器一报警硬件层面几百微秒内就能把动作发出去可靠性比纯软件轮询高一个量级。这里有一个坑多个传感器的ALERT是共享一条线的一旦触发后无法区分是哪一颗传感器报的警。所以我的做法是CLC只负责快速响应具体是哪个节点温度异常还是由MCU在中断里读取各个节点的温度值去定位。互不冲突效果很好。5.3 最后说点个人体会这个项目做完再回头看最值钱的经验其实不是学会了怎么读写I2C而是理解了测温系统的误差从哪来。传感器自身的精度只是一部分布线和安装方式往往是误差的大头。数字传感器把模拟量数字化只是解决了传输损耗问题并没有解决探头与被测对象之间的热阻问题。所以做嵌入式测温别迷信芯片标称精度多想想探头装在哪儿、线缆怎么走、数据怎么滤这几件事搞定了系统的真实精度才能立得住。如果后续还要扩展我会考虑把远程节点换成带湿度的一体化传感器总线架构不用变代码也就多几个寄存器组整体方案的可复用性就在这里。