做 HVAC 控制器这些年我接触过的温度采集方案不少。从最早的热敏电阻加 ADC到后来用单总线传感器再到现在用数字 I2C 温度传感器每种方案都有它的适用场景。这个项目比较有代表性用一颗 dsPIC33FJ256GP710A 做主控挂上 PJ85718DM 温度传感器同一套硬件既测控制板附近的本地温度也通过一条 I2C 总线去读远端风管、回风口的远程温度。整个过程做下来踩过不少坑也积累了一些在 HVAC 现场真正能用的经验整理出来分享给正在做类似嵌入式和暖通温度监测的朋友。PJ85718DM 是带可编程寄存器的数字温度传感器dsPIC33FJ256GP710A 是 16 位数字信号控制器这个组合在嵌入式系统里不算多新奇但在 HVAC 这种环境复杂、线缆长、干扰多的场景里想把本地和远程温度都读得稳、读得准还是有不少细节要处理的。这篇文章会从方案选型、硬件连接、固件实现到现场调试把整个思路和实操过程完整讲一遍。1. 项目到底在做什么一个 DSC 怎么同时盯住“近处”和“远处”的温度1.1 拆开标题看需求先别急着看芯片型号把标题里几个关键词拆开理解项目的轮廓就出来了。PJ85718DM 和 dsPIC33FJ256GP710A 是两个硬件主角前者负责把物理温度变成数字量后者负责接收、处理这些数字量并且结合 HVAC 系统的其他控制逻辑做决策。“监测嵌入式和 HVAC 应用中的本地与远程温度”这句话的信息量很大。“本地温度”在 HVAC 设备里一般指控制柜内部环境温度也就是主控板附近的温度。这个温度用来监控控制器自身的工作环境防止柜内过热导致芯片工作异常。“远程温度”则指分布在系统各处的测点比如送风管道温度、回风温度、冷冻水进出水温度、室外环境温度等等。这些测点离主控板视现场布局有近有远布线从几十厘米到十几米都有可能。所以这个项目的核心需求可以概括成一台控制器一条总线多个测温节点既能照顾近处的本地监测又能覆盖远处的远程监测并且所有温度数据都要稳定、可靠、实时地送到主控里参与控制逻辑。1.2 为什么选 PJ85718DM 和 dsPIC33FJ256GP710A 这个组合先说温度传感器。PJ85718DM 这类带 I2C 接口的数字温度传感器和传统的 NTC 热敏电阻加 ADC 采样方案相比优势非常明显。热敏电阻虽然便宜但有三个让人头疼的地方非线性严重需要做校准表或者拟合曲线信号是模拟量长线传输时抗干扰能力差线缆电阻也会引入误差而且每一路的校准工作量大批次差异也会带来额外偏差。数字温度传感器直接把温度值通过 I2C 总线读出来不需要校准精度也更有保障。至于为什么不用单总线传感器如 DS18B20我在实际项目里也对比过。单总线省引脚但时序要求非常苛刻在 HV AC 现场那种长线、强电干扰多的环境里单总线协议的时序很容易被打乱读回数据偶发出错。I2C 总线有标准的时钟同步机制只要速率配置合理稳定性比单总线好不少。PJ85718DM 还支持通过引脚配置地址一条总线上能挂多个节点这正好匹配“本地 远程多点”的结构。主控选 dsPIC33FJ256GP710A 也不是随便定的。HVAC 控制器不是什么纯测温仪表它通常还要管压缩机、风机、电动阀这些执行机构。dsPIC33FJ 系列是 DSC数值运算能力强PWM、ADC、UART 等外设齐全够用也留有余量。温度监测只是它工作的一部分选这颗芯片等于给系统留出了后续扩展空间。2. 硬件方案一条 I2C 总线上的本地节点与远程节点2.1 传感器侧PJ85718DM 的引脚、I2C 地址和告警功能PJ85718DM 这类可编程温度传感器典型的引脚包括电源 VDD、地 GND、时钟线 SCL、数据线 SDA还有地址选择引脚和告警输出引脚。地址选择引脚一般叫 A0、A1通过对这两个引脚接高电平或低电平可以组合出不同的 I2C 从机地址。这样设计的目的很明确允许设计者在同一条总线上挂多个传感器每个传感器用不同的地址区分。在实际项目中我把主控板附近的本地传感器地址设为基地址加 0远程传感器地址设为基地址加 1这样固件里通过地址就能直接判断数据来自哪个位置逻辑清晰。接线时要注意地址引脚不能悬空悬空的电平状态不确定可能导致地址漂移两个设备地址冲突时总线数据就会乱。我一般默认全部用电阻拉到 GND需要改地址时再上拉到 VDD。PJ85718DM 的告警功能在 HVAC 场景里非常实用。温度上限和下限值可以预先写入寄存器当测量温度超出范围时ALERT 引脚会自动拉低主控只需要检测一个中断引脚就能知道温度超限而不必反复轮询传感器。比如远程温度测的是压缩机排气温度把上限设为 90℃一旦超限立即触发保护逻辑这对设备安全很重要。2.2 主控侧dsPIC33FJ256GP710A 的 I2C 资源与电路连接dsPIC33FJ256GP710A 本身带硬件 I2C 外设有独立的 I2C1 和 I2C2 模块支持标准速率 100kHz 和高速 400kHz。做温度采集这类应用用硬件 I2C 而不是用 GPIO 模拟原因有两点一是硬件外设的时序是自动生成的稳定可靠不容易被中断打扰二是代码更简洁主控可以把精力放在数据处理和系统控制逻辑上。I2C 总线是开漏结构所以 SCL 和 SDA 线上必须有上拉电阻才能产生高电平。这个电阻值的选择很多人不重视其实在远程测温场景里非常关键。上拉电阻太大总线边沿变缓信号容易被干扰上拉电阻太小总线电流偏大在 3.3V 系统下功耗增加而且遇到从机驱动能力弱时可能拉不高电平。常见的做法是在 2.2kΩ 到 10kΩ 之间选。我这个项目里总线上挂了两个传感器线缆总长约两米实测下来用 4.7kΩ 效果不错。远程节点的连接除了 SCL 和 SDA还要单独布电源线和地线。传感器供电和主控供电建议共地但不要在远程末端再来一个大回路地否则现场强电设备的干扰会顺着地线串进来。如果线缆超过三米我的做法是SCL、SDA 用双绞线传输屏蔽层只在主控端单点接地。这样能有效减少电磁干扰耦合又不至于形成地环路电流。3. 固件实现从 I2C 初始化到稳定的远程温度读取3.1 I2C 初始化BRG 寄存器配置到底怎么写dsPIC33FJ 的 I2C 波特率寄存器是 I2CxBRG计算公式是I2CxBRG (Fcy / (2 × Fsck)) - 1其中 Fcy 是系统时钟频率Fsck 是目标 I2C 时钟频率。假设 Fcy 为 40MHz目标是标准速率 100kHz那么I2CxBRG 40000000 / (2 × 100000) - 1 199如果目标速率是 400kHz算出来约等于 49。但我要提醒一句如果你的现场总线比较长比如超过两米不要用 400kHz老老实实跑 100kHz。温度数据本身变化不快100kHz 的轮询速度完全够用换来的是稳定。我实测过同样的硬件400kHz 下偶尔会读到跳变的尖峰降到 100kHz 后问题基本消失。初始化代码的核心部分大致是这样void I2C1_Init(void) { // 关闭 I2C1 I2C1CON 0x0000; // 配置波特率寄存器Fcy40MHz目标 100kHz I2C1BRG 199; // 设置 SCL 和 SDA 引脚为开漏模式具体引脚在 I2C 对应的复用引脚上 // 启用 I2C1 模块 I2C1CON 0x8000; }这一步看起来简单但有个容易忽略的地方引脚必须配置为开漏输出否则总线上的上拉电阻不起作用通信直接失败。另外初始化前要确保总线上没有设备把 SDA 拉低否则一开启模块就会报总线忙。这种情况在远程传感器异常复位时比较常见后面排查部分我详细讲。3.2 读取温度的完整流程写指针、重复起始、误差来源I2C 温度传感器读取温度值流程一般是先向传感器写入寄存器地址指针然后再发起读操作从传感器读回温度数据。读数据的时候一般要用“重复起始”而不是“停止后重新开始”这样可以保证操作的原子性避免其他主机插入。虽然我们这条总线上基本不会有多主机但养成用重复起始的习惯是好的。下面是一段简化的读取函数展示了关键流程unsigned char TempSensor_ReadByte(unsigned char devAddr, unsigned char regAddr) { unsigned char value 0; // 第一步起始信号 设备地址写命令 I2C1CONbits.SEN 1; while (I2C1CONbits.SEN); I2C1TRN (devAddr 1) | 0x00; while (I2C1STATbits.TRSTAT); // 检查 ACK 状态如果无 ACK 则返回错误 // 第二步写寄存器指针 I2C1TRN regAddr; while (I2C1STATbits.TRSTAT); // 检查 ACK // 第三步重复起始 设备地址读命令 I2C1CONbits.RSEN 1; while (I2C1CONbits.RSEN); I2C1TRN (devAddr 1) | 0x01; while (I2C1STATbits.TRSTAT); // 检查 ACK // 第四步读取数据字节读完发送 NACK I2C1CONbits.RCEN 1; while (I2C1STATbits.RBF 0); value I2C1RCV; I2C1CONbits.ACKDT 1; I2C1CONbits.ACKEN 1; while (I2C1CONbits.ACKEN); // 第五步停止信号 I2C1CONbits.PEN 1; while (I2C1CONbits.PEN); return value; }读取温度数据后要做的工作是把原始两字节数据换算成摄氏度。以典型的 12 位分辨率温度传感器为例数据格式是高字节的符号位和整数位低字节的高 4 位是小数位。温度的 LSB 对应 0.0625℃。也就是说把两个字节拼成一个 16 位有符号整数后乘以 0.0625 就是实际温度。实际换算的 C 代码可以这样写int16_t raw (highByte 8) | lowByte; // 对于 12 位分辨率低 4 位是无效位算术右移 4 位 int16_t realRaw raw 4; float temperature realRaw * 0.0625;这里有个容易踩的坑低字节中的无效位如果不处理直接参与数值计算会引入错误的小数噪声。尤其是远程传感器偶尔受干扰时低位的无效位可能变成非 0不屏蔽掉的话读数值就会在真实值附近抖。3.3 本地/远程轮询调度与软件滤波HVAC 系统的温度对象热惯性很大温度变化速率远没有电机电流那么快所以没必要每毫秒读一次温度。我的做法是本地温度 1 秒读一次远程温度 2 秒读一次。这个周期既能满足控制需求也给了 I2C 总线充足的时间去完成别的通信任务。软件滤波方面针对远程温度最容易出现的偶发毛刺我试过滑动平均也试过中值滤波。当干扰导致个别采样值明显异常时滑动平均会把异常值的影响保留几次中值滤波则更果断。实际做法是连续读三次取中间值作为本次有效温度。等数据进入稳定状态后再用滑动平均做短时平滑这样既抵抗了脉冲干扰又不会让系统响应变得迟钝。滤波处理时还要注意一点不要把超限告警也一起滤掉。如果传感器的 ALERT 引脚已经因为温度超限被触发了温度值本身一般不会再突变这时候读取到的超限温度是真实的软件滤波只处理通信毛刺不要对超限数据做过度的平滑。4. HVAC 现场实战长线干扰、总线挂死的排查与处理4.1 HVAC 中每种“温度”该怎么测HVAC 系统的温度测点表面看都是“测温”但每一个测点的实际安装要求都不一样。控制柜内的本地温度探头要尽量远离变压器、继电器和驱动模块这些发热大户否则测出来的柜内温度就失真了。很多人把本地温度传感器放在主控板正中央结果测的是芯片自身发热和柜内实际环境温度差了十几度这是典型的安装错误。远程测点里回风温度探头要装在气流均匀的位置不能太靠近回风口边缘否则受墙壁和管道传热影响。送风温度探头要插到风管中央并且做好保温不然风管壁的温度会影响探头读数。冷冻水管水温测点要注意探头与被测管道的接触面积必要时填充导热硅脂。室外温度探头要加防辐射罩避免阳光直射造成假的高温读数。这些安装层面的细节和芯片本身的设计一样重要。4.2 现场调试笔记这个项目里真正踩过的坑第一坑是速率。最初我认为硬件 I2C 很可靠直接按 400kHz 跑。现场一接上远程传感器温度读数立刻“跳舞”时不时跳出一个不可能出现的高温值。后来用示波器抓 SCL 波形发现上升沿明显变缓线缆电容把边沿拖得很长。把速率改成 100kHz 后波形恢复正常读数也稳定了。第二坑是屏蔽层接地。远程探头线我用的是屏蔽双绞线最初的接法是屏蔽层在传感器端也接了一下地。结果现场一启动旁边的变频器温度读数就开始波动。后来把传感器端的屏蔽层断开只在主控端单点接地干扰问题立刻解决。屏蔽层两端都接地的地环路比不接还糟糕。第三坑是地址冲突。做第二版样机的时候我在同一块传感器板上预留了两个传感器位但 I2C 地址选择引脚默认都是 GND结果上电后发现两个传感器用同一个地址总线上一通从机应答数据全乱。后来规范了硬件设计本地传感器保持地址 0远程传感器强制跳到地址 1才彻底解决。第四坑是自热问题。PJ85718DM 的测量精度在实际中受芯片自身功耗影响不大但如果把它紧挨着电源芯片或功率电阻PCB 上的热量传导会让读数偏高。我最后把本地传感器放到板子边缘距离发热器件留出至少 10 毫米的距离读数才恢复正常。4.3 常见问题排查速查表现象可能原因解决方案SCL 完全没有波形I2C 模块未使能或 BRG 寄存器配置错误检查 I2C1CON 的 I2CEN 位核对波特率计算SDA 一直为低电平某个从机锁死总线断开所有从机逐一点亮对锁死的传感器单独断电复位温度读数偶发跳变总线速率过高、线缆电容过大或现场干扰降速到 100kHz增加中值滤波改善线缆屏蔽读回数据全为 0xFFSDA 信号在长线上衰减严重减小上拉电阻值降低速率检查线缆质量温度值偏高且稳定传感器靠近热源或自热影响调整传感器安装位置增加散热或隔热措施两个节点数据互相串扰I2C 地址冲突检查地址引脚配置确保每个从机地址唯一这张表看着简单但每一条都是现场真金白银换来的。做嵌入式温度监测的人尤其是面向 HVAC 这种工业环境的朋友我建议把这个表打印出来贴在调试台旁边。5. 加深一点这套测温方案还能怎么扩展5.1 从“监测温度”到“控制温度”与 PID 控制结合温度监测本身不会直接产生价值真正有价值的是基于温度数据的控制逻辑。dsPIC33FJ 这类 DSC 的算力足以在读取温度的同时运行一个完整的 PID 控制环。比如在 HVAC 中通过远程回风温度作为反馈量控制电动水阀的开度或压缩机的转速把室内温度稳定在设定值附近。我在这个项目里做了类似的事情温度采样周期 1 秒PID 控制周期 1 秒温度滤波后的数据作为反馈输入输出叠加到风机的 PWM 占空比上。因为 dsPIC33FJ256GP710A 主频足够整套控制跑下来 CPU 占用率也不高剩余资源还能处理通信和其他保护逻辑。5.2 告警唤醒与远程上报PJ85718DM 的 ALERT 引脚还可以接到主控的外部中断输入。正常工作时主控可以进入低功耗模式等到温度超限或传感器告警时ALERT 引脚拉低唤醒主控。这个做法特别适合对功耗敏感、或者希望减少主控巡检频率的场合。我在扩展版本里把这个告警信号同时接到了主控的 IO 口并且通过 RS485 接口把告警信息上报到楼宇监控平台实现了远程温度监测和异常告警的完整闭环。最后再分享一个小技巧I2C 总线上的传感器如果现场维护时发现数值总是“卡住”不动大概率不是传感器坏了而是 I2C 命令序列卡在了中途。这时候最简单有效的办法不是反复重试而是巡检时给传感器增加一个专用的复位 IO 口需要时硬件拉低重启比起软件复位可靠得多。我后来在每一路的远程传感器板上都加了复位按键和复位 MOS 管实测维修效率提升明显。