
做 HVAC 控制器的朋友应该都遇到过这种纠结一套设备既要管自己板卡上的温度又要盯着风管、回风井或者循环水管里的介质温度。本地温度代表的是板子自身健康状态远程温度代表的才是被控对象的真实状态两个信号串在一起才是完整的温控闭环。我最近在做的这套嵌入式温度监测模块主控选了 STM32F215ZG前端测温用的是 PJ85718DM 这颗 I2C 接口的数字温度传感器最终目标是在一个系统里同时、稳定、可诊断地采到本地温度和远程温度。这篇文章把从选型、硬件设计、固件实现到现场排障的完整过程做个记录适合正在做 HVAC 控制器、嵌入式采集模块或者工业温控节点的同行参考也适合刚接触数字温度传感器、想把读个温度做成工程级功能的同学当作实战案例。1. 为什么 HVAC 场景里要把本地温度和远程温度分开采集1.1 本地温度管板卡死活远程温度管空调冷暖在暖通空调的控制器里温度这个词经常被混淆。我做了这么多年项目最怕的就是需求文档里只写一句采集温度等联调的时候才发现对方要的是两个完全不同的物理量。本地温度指的是控制板本身的温度我们一般把传感器放在 PCB 上靠近电源模块、继电器区或者主控芯片附近。它的作用是保护一旦机箱内通风不良、散热片失效、或者环境温度过高主控要先知道自己快热死了然后降频、报警、或者启动散热风扇。这块测的是电子设备的工作环境响应可以慢一点但是稳定性要求极高最好不能有一颗螺丝松动就导致读数莫名其妙地飘。远程温度指的是被空调系统控制的那个介质的实际温度比如送风管里的风温、回风口附近的空气温度、冷冻水管表面的水温。这个温度直接参与 PID 调节、压缩机启停判断、风阀开度计算。它的特点是探头和主控之间一定有物理距离短则一两米长则七八米而且周围往往有风机、压缩机的电磁干扰。把这两个温度放在同一个系统里采集不是因为多测一个更划算而是因为缺失任何一个都会出事。只监控远程温度控制板闷在密闭机箱里烧到 85°C 你都不知道只监控本地温度风管温度已经波动 5°C 了控制策略却还在按上一轮数据跑。1.2 PJ85718DM 这颗传感器解决了什么痛点我最早做 HVAC 温度采集的时候用的还是 NTC 热敏电阻加 ADC 采样的方案。NTC 的好处是便宜、响应快、采购买方便但工程麻烦是一大堆每路探头都要做单独的线性化校准长线传输时线阻分压会直接影响精度而且模拟信号抗干扰能力弱信号线稍微靠近一下变频器的输出电缆读数就会开始跳舞。每次现场隔三差五报温度不准我们都要拿万用表、电烙铁去逐个排查。后来改用了 PJ85718DM 这类数字温度传感器相当于把模拟量采集线性化抗干扰这些脏活累活都塞进一颗芯片里。这颗传感器是 I2C 接口的内部集成了信号调理、Delta-Sigma ADC 和数字滤波逻辑温度数据直接通过 I2C 总线以数字形式送进主控。它有几项特性很适合 HVAC 场景典型精度在 ±0.3°C 左右现场不做标定也能满足大多数空调控制的精度要求测温范围覆盖 -40℃125℃不管是北方冬季的冷风管道还是机柜内部的发热环境都够用分辨率可以配置我在项目里使用了 12 位省去了一部分转换时间温度跳动幅度也能控制在 ±0.1℃ 以内内置报警比较器超过上下限阈值可以直接拉中断主控不需要一直轮询。选 STM32F215ZG 作为主控则是看重它的资源平衡。这颗芯片是 Cortex-M3 内核主频 120MHz512KB Flash、64KB RAM做温度采集有点大马拉小车的嫌疑但好处是剩余资源非常充裕。系统里除了两个温度通道还要跑 Modbus 协议栈、显示屏刷新、继电器控制逻辑、PID 调节算法和本地参数存储选一颗资源余量大的 MCU 可以避免开发到一半发现 Flash 不够用的尴尬。另外它的硬件 I2C 外设、独立看门狗、备份寄存器和 RTC 也都是 HVAC 控制器刚需的外设。2. 硬件链路从传感器引脚到 MCU 引脚每一步都要算清楚2.1 PJ85718DM 的接线方式与 I2C 上拉电阻计算PJ85718DM 我这边用的是标准 8 引脚封装典型接法是 VDD、GND、SCL、SDA 四根线剩下几个引脚接报警输出和地址选择。项目里一共用了两片传感器一片焊在主控板上作为本地通道一片做成远端防水探头组件作为远程通道。两片挂在同一条 I2C 总线上通过地址引脚A0/A1区分总线上最多可以挂 8 片同类传感器这个余量也给后面扩展多路测点留了后路。I2C 总线的上拉电阻是首先要算清楚的参数很多人直接抄参考设计用 4.7kΩ结果在远程探头线长超过 3 米之后就开始出问题。上拉电阻的选择要服从总线电容计算公式很简单Rp(max) tr / (0.8473 × Cbus)其中tr是允许的上升时间标准模式100kHz下要求 1μs 以内快速模式400kHz下要求 300ns 以内。Cbus是总线总电容包括 MCU 引脚电容、传感器引脚电容、PCB 走线电容和线缆电容常见估算值是每米双绞线约 50pF80pFPCB 上每厘米走线约 1pF2pF。以 100kHz 标准模式、远程线缆 5 米、总线电容大约 400pF 计算Rp(max) 1μs / (0.8473 × 0.4nF) ≈ 2.95kΩ。也就是说4.7kΩ 的上拉电阻在长线场景下已经超出了理论上限上升沿会变得很慢I2C 时序容易出错。我最终选了 2.2kΩ 上拉电阻并且把总线速率固定在 100kHz而不是跑 400kHz。温度数据本身就不是高吞吐量信号用速度换可靠性完全值得。这里还有一个小细节如果板上挂了两片传感器上拉电阻最好只保留一组不要每片都各留一组否则并联之后总等效阻值会变小驱动能力是够了但功耗和边沿过冲会变得难看。2.2 远程探头怎么做屏蔽双绞线、缓冲与接口保护远程温度探头在设计上要比本地通道多考虑一层。探头放在风管里线缆要穿墙、走线槽旁边往往就是 220V 动力线和变频器输出线。我踩过最大的坑就是直接用普通排线把传感器引到远端结果不仅读数跳动连 I2C 总线上原本正常的本地传感器都开始偶尔丢应答。远程通道的走线必须用屏蔽双绞线。屏蔽层单端接地接在主控板这一侧的 GND远程探头那一侧悬空。双绞结构让两根信号线上的共模干扰基本一致靠差分接收能力抵消屏蔽层负责把电场干扰导走单端接地避免了多点接地形成地环路。线径上用 0.5 平方毫米或者更粗一点的双绞线长线压降问题基本可以忽略因为 I2C 信号是电流驱动而非电压源。如果远程距离超过 5 米我建议在总线上加一个 I2C 总线缓冲器。这类芯片的作用是把你这一侧的 SCL/SDA 和远程那侧的 SCL/SDA 隔离开来两侧都有自己的上拉电阻缓冲器内部做了方向检测和电平整形可以显著改善长线上的信号边沿。加了缓冲之后远程线缆的长度可以扩展到 10 米甚至更长实际项目中我把远程探头的线缆放到了 8 米示波器上看上升沿依然干净通信不丢字节。接口保护也要做好。传感器放在工业现场最怕的是调试的时候线序接反或者线缆破损后把 24V 电源串到信号线上。我在远程接线端子那里加了瞬态抑制管和限流电阻虽然不能保证雷击情况下芯片完好但至少能扛住常见的误接和静电损伤。这些保护器件的引脚电容要尽量小不然会给高速信号增加负担好在我们跑 100kHz这个问题不大。2.3 PCB 布局本地传感器不要挨着发热源本地通道的传感器在 PCB 上放哪里直接影响读数的可信度。我第一版设计图为了走线方便把本地传感器放在 LDO 旁边结果一上电就看到温度读数比实际机箱环境温度高了 8°C。这倒不是传感器坏了而是 LDO 的散热把局部环境加热了测到的本地温度根本代表不了板卡整体的热状态。正确的做法是让本地传感器避开明显的发热源比如电源芯片、主控芯片、继电器驱动管距离至少 2 厘米以上。如果板卡有通风路径传感器要放在进风口一侧这样读到的是冷却空气流过之后的混合温度更接近机箱内部平均温度。另外传感器正下方的 PCB 要避免走大电流铜皮我曾经碰到过一块板上正好在传感器下面走了 2A 的电源线局部温升把精度吃掉近 1°C。焊盘散热也要留意很多数字温度传感器的封装底部有大面积焊盘设计时要注意过孔和铜皮的导热。如果寒地暖系统中传感器是焊接在 PCB 上的那么 PCB 的温度会通过焊盘和引线直接传导到传感器内部测试的时候最好用热成像仪看一眼温度梯度别只盯着数据手册上那个±0.3°C。3. 固件实现从寄存器初始化到稳定可靠的数据流3.1 传感器驱动初始化、连续读取与错误重试PJ85718DM 的驱动逻辑并不复杂但实现成可以在项目里稳定运行的代码需要把错误处理想在前头。I2C 总线在工程现场并不是永远清净的偶尔会有来自电源的强干扰导致总线卡死这一点后面会专门说但驱动层必须在设计时就考虑到。传感器驱动我拆成了几个函数初始化配置、单次温度读取、连续转换模式切换、寄存器写入校验。初始化配置里最核心的几项是把传感器配置为非默认的转换分辨率、设置报警比较器的阈值、使能或禁止连续转换。我实际使用的配置是单次转换模式每次读温度之前先发一次触发命令等待内部转换完成后再读温度寄存器。这个方式比连续转换模式多花一点时间但每次读到的都是刚刚新鲜出炉的温度切换探头地址时的冲突少调试也直观。下面这段是我从项目里抽出来的驱动核心代码去掉了公司私有库的宏定义换成了可读性更好的写法改一改就能用到自己的板子上#define PJ85718DM_I2C_ADDR 0x48 // 本地通道地址A00, A10 #define PJ85718DM_I2C_ADDR_R 0x49 // 远程通道地址A01, A10 #define PJ85718DM_REG_TEMP 0x00 // 温度数据寄存器 #define PJ85718DM_REG_CONF 0x01 // 配置寄存器 #define PJ85718DM_REG_ALERT 0x03 // 报警阈值寄存器 static I2C_HandleTypeDef hi2c1; uint8_t pj85718dm_read_temp16(uint8_t dev_addr, int16_t *raw_value) { uint8_t reg PJ85718DM_REG_TEMP; uint8_t buf[2]; HAL_StatusTypeDef ret; // 第一步指向温度寄存器 ret HAL_I2C_Master_Transmit(hi2c1, dev_addr 1, reg, 1, 100); if (ret ! HAL_OK) { return 0; // 总线异常调用方决定是否重试 } // 第二步读取16位转换结果 ret HAL_I2C_Master_Receive(hi2c1, dev_addr 1, buf, 2, 100); if (ret ! HAL_OK) { return 0; } // 将两个字节拼成16位有符号数 *raw_value ((int16_t)(buf[0] 8)) | buf[1]; return 1; }这段代码我在实际项目里做了一处重要改动就是把它包在一个带重试机制和总线恢复逻辑的壳里。HAL 库的 I2C 处理在检测到总线挂死时会返回超时但单纯的超时返回并不能保证总线恢复。我在重试之前先做一次总线的复位脉冲把 SCL 连续翻转 9 到 16 次同时监测 SDA 的电平状态模拟出一个标准的复位序列让从设备重新进入空闲状态。这个手法在 I2C 通信被外部干扰搞乱之后特别有效算是嵌入式工程师的小技巧。3.2 滤波算法移动平均、中值滤波和斜坡限幅到底用哪个温度传感器原始数据里偶尔会出现毛刺尤其是远程探头线缆较长、又靠近变频设备的时候即使加了屏蔽和缓冲也无法百分百避免偶发的跳变。这些毛刺不是传感器真实测量的温度而是瞬间的电磁干扰被当成有效数据采进来了。针对温度信号常见的滤波手段有移动平均、中值滤波和斜坡限幅。我发现很多初学者直接套移动平均发现响应速度变慢之后又不断调整窗口大小结果越调越难受。实际项目中我采用了两级组合方案第一级是中值滤波窗口取 5 个连续样本排序后取中间值。中值滤波对孤立大毛刺几乎是免疫的一个异常高的干扰值会被排序算法扔到边上不会影响输出。代价是每次要搬 5 个数在中端 MCU 上完全不是负担。第二级是移动平均窗口取 8 个样本。这一步是为了平滑温度的短时波动把信号均匀度提升到控制模块能直接使用的程度。移动平均窗口的选择有一个权衡窗口太短平滑效果不明显窗口太长响应延迟增加。HVAC 场景里温度变化本来就慢风管温度几秒钟内变化 1°C 已经算剧烈所以 8 点窗口既保证了平滑又没有明显的滞后问题。斜坡限幅我放在了最后作为兜底保护如果新样本和当前输出值之差超过上限比如 3°C/400ms就先按 3°C/400ms 的斜率逼近而不是直接跳到新值。这个逻辑看起来保守但实际效果是即使前两级滤波被极端干扰打破输出值也不会出现让控制策略瞬间误判的突变。比如压缩机启动瞬间的电磁冲击导致传感器读到一个跳变 7°C 的伪数据斜坡限幅会把它限制成一个渐进变化顶多让系统以为温度在缓慢上升而不会触发紧急关停逻辑。3.3 双通道调度本地通道和远程通道的读取节奏不一样两个 PJ85718DM 挂在同一条 I2C 总线上调度策略看起来简单实际有一种很容易踩的坑。我在第一版程序里是主循环中轮流读取两个传感器靠 I2C 的地址区分来避免冲突结果发现远程通道读着读着本地通道偶尔会出现超时。排查下来的根因是库函数本身的超时设计。默认超时时间设得比较长而单个传感器读一次温度需要执行一次写寄存器命令加一次读数据命令串行执行时如果某次的 I2C 总线处于 hang 状态HAL 库会阻塞整个调用把另一个通道的读请求也拖进去超时。最后我把两个通道的读取放到了两个不同的调用点本地通道在 100ms 定时中断里读远程通道在主循环的 500ms 周期里读两者通过信号量协调对 I2C 总线的访问确保同一时刻只有一个通道发起传输互不阻塞。这里还有个数据语义的问题PJ85718DM 的分辨率不同时内部转换时间不一样。12 位分辨率下典型转换时间大约在 100ms 级别这意味着你发出读取命令之后不能立刻读数据寄存器必须等待内部 ADC 完成转换。驱动里我用了一个简单的延时实现但要注意这个延时不要用HAL_Delay硬等否则会卡住其他任务好久。我的做法是把转换等待放到调度器里先把任务让出去时间到了之后再去读数据。4. HVAC 项目特有的可靠性设计这些坑别等现场才踩4.1 探头断线、短路的检测策略数字温度传感器有一个经常被忽略的问题线缆断掉之后I2C 总线上的表现可能不是读不到而是读到个 0或者读到个满量程。如果不加检测固件会把这种失效值当成真实温度送进控制闭环轻则显示偏值重则触发误保护。我在设计里做了三层防护。第一层是总线层面的检查每次读取如果 HAL 返回错误就计数加一连续失败超过 3 次就判定通道总线异常立即上报并切换到备份逻辑。第二层是数据合理性检查PJ85718DM 测的是 -40°C 到 125°C 范围的温度如果读到的原始值换算出来超过这个范围直接视为无效数据。第三层是跳变检测把当前读数和最近一次正常读数的差值做比较超过设定斜率上限多次时判定为探头物理状态异常。实际的失效表现会有很多种。远程探头里如果 VDD 和 GND 短了传感器可能直接不工作了I2C 总线被拉低这两个通道全挂。这时候上拉电阻会成为一个电流通路I2C 的电平就变成了半高不高状态主控怎么发命令都是定时器超时。所以我把两条 I2C 总线上都加了多路复用器平时两个通道在一组总线上运行检测到总线锁死的时候可以把某一条通道从总线中隔离出来保全另一条通道这个设计在现场救过一次项目远程探头线被割草机切断了系统依然能正常测本地温度然后通过报警把远程探头异常报给了监控室。4.2 压缩机、继电器启停瞬间的 EMI 干扰对策HVAC 系统里最恐怖的不是持续干扰而是继电器吸合、压缩机启动那个瞬间。大电流的突变会在空间里激发一个宽频带的电磁波传感器线缆就像一根天线把这个脉冲完整地接收下来。这个干扰的特点是时间短、幅度大会直接让 I2C 总线时序乱套。软件上我能做的是重试和复位但硬件的抗干扰才是治本。除了前面说的屏蔽双绞线还有几个经验值得分享传感器线缆的屏蔽层选择在靠近主控板的一侧单端接地。两边接地会形成地环流回路反而让干扰电流顺着屏蔽层流进主板地平面更容易影响 I2C 信号。继电器线圈和压缩机的控制线要与传感器线缆分槽走线保持 10 厘米以上的距离。如果条件不允许跨越多线槽至少要让传感器线缆不与动力线平行超过 20 厘米。如果设备里走的是 AC 接触器启动瞬间会有巨大的瞬态电流合适的位置加一个磁环会立竿见影。我把磁环套在远程探头的两端线缆上压降和温升都没有明显影响但现场观察到的误码率直接下降了很大一截。I2C 总线上拉的电阻不要并排放在一起形成环路要遵循每个信号一对回流地的布局原则把上拉电阻靠近主控的引脚放置。4.3 参数掉电保存与异常恢复HVAC 控制器不仅要读温度还要把报警阈值、校准偏移、滤波窗口大小这些参数保存下来掉电之后不能丢。STM32F215ZG 内部没有真正的 EEPROM但这颗芯片支持把 Flash 中的一个扇区模拟成 EEPROM 来用结合备份寄存器可以做出稳定的参数存储方案。我在项目里用的策略是把核心参数温度阈值、报警使能、滤波配置放在备份寄存器里断电速度快、写次数不怕磨损把稍大数据量的配置放 Flash 模拟 EEPROM 区域写之前擦扇区写完之后回读校验。参数存储最怕的是写了一半掉电一旦掉电老的参数和新的参数都不完整了。解决方案是每次存储时写两套副本加版本号和 CRC 校验启动时优先读取版本号较高且 CRC 正确的那一套如果两套都坏了就用默认值同时置一个参数异常标志。这套机制在一次现场断电测试中起了作用设备写到配置数据的中途被强制断电重启以后读到了上一轮完整的配置报警阈值和探头地址都没丢系统的可用性看起来就不像一个纯消费级产品。5. 实测过程中的三次典型故障与完整排查链路5.1 远程探头线缆延长 8 米后温度读数整体偏移第一个实测问题是远程探头线从 3 米延长到 8 米之后温度读数比已知参考值低了约 1.5°C。刚开始我怀疑是传感芯片本身精度问题换了一片还是一样于是开始逐段排查。一开始排查的方向是线缆电阻。PJ85718DM 是数字总线传输测温发生在传感器内部线缆电阻不应对温度数据本身产生影响。所以排除这个思路后我转向怀疑是否总线时序恶化导致内部 ADC 转换的中途出错。用示波器看远程端的 SCL 上升沿大概在 1.4μs 左右已经明显高于 100kHz 模式下 1μs 的要求这个问题足够引起通信位翻转。加了 I2C 总线缓冲器之后上升沿缩短到 400ns 左右通信恢复正常读数和参考值之差缩小到 0.1℃ 以内。这个故障的教训是I2C 长线场景不能只看能通信还要关注边沿质量。能通信可能只是恰好还在规格边缘环境温度变化或者干扰强一点就会宕机信号完整性要留出足够的余量。5.2 本地通道在继电器吸合瞬间出现脉冲跳变第二个故障更隐蔽。系统运行几天后发现本地温度每隔一段时间就会跳一次 8°C 左右的毛刺持续约 0.5 秒后恢复正常。毛刺的周期和继电器的动作周期居然完全吻合。我开始怀疑是继电器线圈的通断影响了传感器供电。把示波器探头夹到传感器 VDD 引脚上一看继电器吸合瞬间 VDD 纹波从正常的 50mV 飙升到 400mV。传感器的内部电路在电压剧烈波动时ADC 基准会随之抖动直接反映为温度读数跳变。我做了两处改动一是在传感器 VDD 引脚旁边加了一颗 4.7μF 的陶瓷电容做局部去耦二是把继电器的控制引脚加上 RC 吸收网络降低线圈反电动势对电源平面的冲击。改完以后再用示波器看同一位置纹波降到 80mV温度读数也不再出现同步跳变。这个问题的关键是表面看起来是传感器错误实质上是电源完整性。很多温度传感器的奇怪表现最后追根溯源都是供电不给力。5.3 更换传感器批次后报警功能失效第三次故障发生在一次批量采购换批之后。新批次的传感器在待机模式下的功耗特性没有明显差异但报警输出引脚的内部结构改了之前用的推挽配置触发不了外部中断。现象就是传感器读回来的温度已经超过阈值报警中断却一直不触发系统只能靠主控轮询才能感知超温。排查过程从报警输出引脚电平开始用示波器看到超温时可以拉低但恢复后不会自动释放。细读新批次的数据手册发现报警引脚的行为模式改成了锁存模式必须通过 I2C 读一次状态寄存器才会清零。固件里在报警处理函数中加了一步中断触发后立即读取传感器状态寄存器再重新设置报警阈值使报警引脚退出锁存状态。这个问题不算复杂但它提醒了我——换批次尤其是传感器这种看似功能完全相同的芯片还是要重新读一遍变更日志。6. 实测数据与后续可扩展方向6.1 稳定度与响应速度的现场记录把系统挂在一个带风机的风管温度调节台上跑了两天记录了几组典型数据测试条件本地通道稳态波动远程通道稳态波动异常毛刺出现次数室温、正常供电±0.05°C±0.08°C0临近继电器每 10 秒动作一次±0.08°C±0.12°C1触发滤波拦截远程探头线长 8 米、有变频电源±0.06°C±0.15°C2触发滤波拦截本地通道波动明显小于远程通道是因为板卡走线短、干扰弱。远程通道在 8 米线长、变频干扰的环境下仍然能保持 ±0.15°C 的稳态波动用于 HVAC 控制精度已经足够。毛刺出现的次数不算多但说明全链路中仍然存在残余的高频干扰继续优化屏蔽层接法和高频去耦还能进一步降低。响应速度方面PJ85718DM 在 12 位分辨率下从发出转换命令到稳定输出一个新温度值大约需要 120ms 左右。配合 5 点中值滤波加 8 点移动平均的二级滤波系统对阶跃温度的响应时间约 1.2 秒。对于 HVAC 这种时间常数在几十秒到几分钟级别的被控对象这个响应速度完全足够但在一些需要快响应的场合比如过温保护触发依然要直接用未滤波的原始数据做判断不能等滤波输出。6.2 扩展方向多节点组网与监控联动这次项目只做了两个温度通道但整个架构留下来可以继续扩展。PJ85718DM 的地址引脚可以挂 8 片同类传感器加上 I2C 多路复用开关和总线缓冲器单套系统接 1624 路温度测点不是问题。接下来我想把系统升级成多节点组网用 STM32F215ZG 自带的 RS-485 接口把温度数据做 Modbus 上报让中央监控平台通过标准协议定期拉取每个节点的温度替换掉现在只能看屏幕的本地显示方式。本地保存的报警阈值和滤波参数已经做了参数化设计后续扩展时只需要把控制参数在 EEPROM 里增加索引再补一条配置初始化函数管理端就可以通过通信协议远程调整每个测点的报警阈值不需要每个探头装完还要拆机改参数。另外我还在计划把温度数据曲线存进 SD 卡通过 FAT 文件系统生成 CSV 日志方便后期做能耗分析和故障追溯。这个方案的好处是数据链路完全独立于显示和控制路径即便是上位机挂了现场也能照常运行日志文件也能在事后完整复盘传感器的工作状态。做了这个项目之后我的一个体会是温度采集看起来是嵌入式开发里最简单的一环但真正要做到长期稳定可信需要处理的是电源、信号完整性、固件状态机、参数存储和现场故障诊断这一整套工程问题。读数字温度传感器本身不难难的是你的系统在各种奇怪的现场环境下始终能拿到高质量的数据。把传感器当做一个数字器件去接、去读很多坑是避免不了的把它当做一个会受到供电、电场、温度传导影响的实际组件去认真对待系统的稳定性才能真正提上去。