做HVAC控制项目的朋友应该都有同感温度采集看起来是最不起眼的环节可真正到了现场调试你才会发现这个模块要采回风温度那个房间又要看远程温度十几路传感器乱七八糟地拉过去干扰和误差一起来很容易变成整个系统中第一个被吐槽的隐性故障点。我自己在嵌入式项目里折腾过好几轮温度采集方案从最便宜的NTC分立方案到数字传感器方案都踩过不少坑这次用PJ85718DM和STM32F411RE组合做的一套本地与远程温度监测系统算是把成本和可靠性平衡得比较满意的一套方案。这套东西的核心价值很直接PJ85718DM负责把温度信号变成稳定的数字量STM32F411RE负责按节奏采集、滤波、换算再把结果通过串口或总线送出去。用在HVAC场景里本地通道盯设备回风温度远程通道盯远端房间或风管温度一套代码加一块板子就能干完过去需要两套硬件才能干的事。下面我把整个项目从器件选型、硬件设计、固件实现到现场排查完整地拆开讲一遍希望对正在做类似温度采集的朋友有点用。1. 项目整体拆解一个标题里藏着的三重需求1.1 本地远程双通道监测到底解决什么问题HVAC系统里温度监测从来不只测一个点。空调箱的送风温度、回风温度属于设备本体的温度传感器就装在机器旁边线缆很短信号稳定这是本地温度。而每个楼层的房间温度、风管末端的温度传感器往往离控制柜几十米远走线穿过桥架、穿墙、甚至经过强电井信号衰减和干扰都不可忽视这是远程温度。如果用分开的两套系统去采集最麻烦的是同步问题。本地温度用于压缩机和风机的闭环控制远程温度用于区域温度补偿两者的采样时刻、滤波方式不一致会导致控制逻辑判断混乱。比如本地显示已经到25摄氏度远程点位却还停留在22摄氏度PID调节器拿到两个时间基准不一致的数据很容易出现震荡。所以这个项目把PJ85718DM设计成双通道读取模块上有一个板载温度采样点管本地同时预留一路外接探头接口管远程。MCU端在同一个采样周期内把两个值都读回来再统一做数据处理从根源上保证了两路温度的时基一致。对有工程经验的人来说“同一时刻的准确数据”比“各自时刻的精确数据”更重要这就是双通道的价值所在。1.2 PJ85718DM 传感器模块的定位PJ85718DM严格来说是一个数字温度采集模块而不是裸传感器。它内部集成了本地测温元件和一个适配远程探头的信号调理电路对外输出标准数字接口MCU不需要关心NTC分压、ADC采样、查表这些底层琐事直接通过总线命令就能读温度值。我没有把它当成一颗普通的I2C温度芯片来看待而是当作一个“温度前端”使用。原因有三个第一本地和远程两路信号被封装在同一颗模块里硬件设计不用再为远程探头单独做信号调理电路板面积和物料种类都省了第二模块出厂前做过校准温度误差主要在探头精度和线缆阻抗上系统误差的排查范围缩小了很多第三它的接口定义很干净电源、地、数据线、时钟线四根线就能跑起来对PCB布局很友好。当然PJ85718DM也不是万能的它更适合温度范围在-20到80摄氏度左右、精度要求正负0.5摄氏度以内的常规HVAC场景。如果要做-196摄氏度液氮监测或300摄氏度高温风管测温那不是这颗料该干的活选型的时候别拿它硬扛极端场合。1.3 STM32F411RE 为什么够用又合适STM32F411RE是Cortex-M4F内核主频100MHz带FPU单精度浮点单元Flash 512KBRAM 128KB。拿它来跑温度监测确实有点性能过剩但换个角度看正是这种冗余让代码写起来非常舒服。PJ85718DM的数据转换涉及浮点运算比如把原始寄存器值换算成实际温度如果MCU没有FPU纯软件浮点运算会增加CPU开销。虽然对100MHz主频来说算一个温度值也就几百个周期但系统里如果同时跑着PID控制、串口通信、人机交互界面FPU的存在让温度换算可以顺手做掉而不用特意优化性能这种从容感在做嵌入式项目时很值钱。再就是接口资源。F411RE有多个USART、SPI、I2C接PJ85718DM走一路I2C调试串口走一路USART还能剩下好几路做扩展。我这次还把温度数据通过Modbus RTU接到上位机串口资源直接现成可用不用因为接口不足去改方案。2. 硬件设计把传感器信号稳定地送进MCU2.1 最小系统与电源拓扑STM32F411RE的VDD接3.3V温度和通信电路共用同一组电源轨。HVAC环境里最怕的不是电压不够而是电压纹波风机变频器一启动母线电压波动经常能到几百毫伏如果传感器的基准电压跟着抖温度读数就会有周期性跳变。我的做法是电源输入端先加一个防反接二极管再接10uF电解电容加100nF瓷片电容的组合之后通过一个LDO稳压到3.3V给MCU和传感器供电。LDO的纹波抑制比在高频段很关键我用的是输出纹波在1mV以内的低压差线性稳压器实测下来变频器启动瞬间温度读数没有出现规律性毛刺。PJ85718DM的供电我直接复用3.3V没有单独隔离。严格来说工业应用最好做隔离但考虑到这套系统主要用在一个区域内做温度采集没有跨设备长距离通信需求共地设计反而更简单可靠。如果哪天需要把数据通过RS485传到几百米外的控制室我会重新评估隔离方案毕竟RS485总线跨设备时地电位差才是真正的毁灭者。2.2 本地通道与远程通道的接线方式PJ85718DM的本地通道是板载探头不需要额外接线。远程通道我拉到端子排上用三线制方式接远端屏蔽探头。三线制并不是说只需要三根线而是用两根线承载激励电流第三根线独立采样电压这样能把线束电阻的影响抵消掉适合几十米距离的现场环境。接线顺序上我把电源正、信号、反馈、地按顺序排在端子上并要求现场施工人员全部使用同一色系线缆以方便排查。别看这事简单实际项目里我吃过亏有一路远程探头施工时把信号线和电源线接反结果温度读数整体偏高4摄氏度排查了两天才发现是线序问题后来在端子排上加了丝印标注再把线色规范写进施工要求这类低级问题基本绝迹了。还有一点要特别注意远程探头的线缆必须和动力线分开走。强弱电共管是HVAC现场最常见的错误感应过来的干扰会让I2C上出现大量总线错误严重时MCU直接读不到数据。如果现场条件所迫必须近距离并行至少要保持10厘米以上间距并且远程探头线缆要用屏蔽双绞线屏蔽层单端接地。2.3 信号完整性与抗干扰设计I2C总线在板级环境下工作频率400kHz还算轻松但一旦有外部引线情况就变了。PJ85718DM的远程探头信号虽然经过模块内部调理但探头的引线终归是一个天线会把空间辐射干扰引进来。因此PCB上I2C的上拉电阻我选了4.7k欧姆而不是常见的2.2k欧姆。原因是HVAC主控板上往往有继电器和接触器动作瞬间会产生宽频噪声较低的上拉电阻值会增强总线驱动能力没错但也会让信号边沿过冲更明显反射噪声更严重。4.7k欧姆在总线上挂三到五个器件时信号沿依然干净利落。另外我在PJ85718DM的电源引脚旁边加了100nF和10uF双电容这个组合在数字和模拟混合场景下很实用。小电容过滤高频开关噪声大电容扛低频瞬态电流两者配合起来能让传感器内部基准电压稳定很多。PCB布线时这两颗电容要尽量靠近模块引脚走线要先过电容再进模块别为了美观绕远路电感效应会抵消掉电容一半的滤波效果。3. 固件实现从初始化到温度输出的完整链路3.1 工程搭建与基础初始化我习惯先用CubeMX生成工程基础代码这样可以少写很多寄存器操作。时钟树配置为HSE外部晶振PLL倍频到100MHzI2C1配置为400kHzUSART2配置为115200-8-N-1同时开启一个1ms时基的定时器用于软件调度。初始化代码非常简单常规操作不展开只提醒几个容易出问题的地方。时钟配置后一定要确认HAL_RCC_ClockConfig返回值正常如果外部晶振起振失败系统会卡死在HAL_Init的HSE等待循环里这个在很多板子上遇到过晶振引脚负载电容不合适是最常见原因。I2C初始化时要把上拉模式和滤波参数设对否则总线时序不达标传感器偶尔读错值。void MX_I2C1_Init(void) { hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 400000; hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 0; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(hi2c1) ! HAL_OK) { Error_Handler(); } }3.2 PJ85718DM 驱动写法的关键点PJ85718DM的驱动并不复杂核心就是配置寄存器、触发转换、等待转换完成、读取结果。需要注意的是一开始先读一次器件ID确认总线上真的有设备在再继续后续流程。这个步骤看起来多余实际上能节省很多排查时间我在代码里把它做成了模块初始化的一部分。#define PJ85718_ADDR 0x38 #define REG_ID 0x00 #define REG_CONFIG 0x01 #define REG_DATA 0x02 uint8_t pj85718_begin(void) { uint8_t id 0; uint8_t cfg 0x00; if (HAL_I2C_Mem_Read(hi2c1, PJ85718_ADDR 1, REG_ID, 1, id, 1, 100) ! HAL_OK) return 0; if (id ! EXPECTED_ID) return 0; cfg 0x0C; // 开启本地与远程双通道连续转换模式 if (HAL_I2C_Mem_Write(hi2c1, PJ85718_ADDR 1, REG_CONFIG, 1, cfg, 1, 100) ! HAL_OK) return 0; return 1; }读温度的核心代码更简单但有一个反直觉的细节不要在读取之后立即丢掉坏数据。HVAC现场偶尔会有干扰导致单次转换值异常直接把这个值丢弃会让控制逻辑产生一个空洞这个空洞在PID运算中会被放大成扰动。我的做法是先保留这个异常值在滤波层里通过中值法把它滤掉这样数据流始终是连续的只是异常值不参与最终输出。int pj85718_read_temperatures(float *local, float *remote) { uint8_t buf[8]; uint16_t raw_l, raw_r; HAL_I2C_Mem_Read(hi2c1, PJ85718_ADDR 1, REG_DATA, 1, buf, 4, 100); raw_l (buf[0] 8) | buf[1]; raw_r (buf[2] 8) | buf[3]; *local raw_l * 0.01f - 273.15f; // 转换成摄氏度 *remote raw_r * 0.01f - 273.15f; return 0; }寄存器地址和转换公式以模块手册为准我上面这些常量是从手头随手写出来的参考值实际移植时把寄存器偏移和我标的一一对应上就行。重点在于理解读取流程每次读数据前确保模块已经完成一次新转换如果读取间隔太短读到的是上一次的缓存值远程温度变化慢的时候感觉不出来但本地温度直连发热源时就能明显看到响应滞后。3.3 温度值换算与滤波处理PJ85718DM输出的原始值换算成温度后还会有高频噪声。噪声主要来源两个一个是传感器本身的测量噪声另一个是HVAC设备运行时气流波动导致的真实温度脉动。这两种噪声都需要滤波但滤波强度不能一样。我用了两级滤波中值滤波加滑动平均。中值滤波窗口取5个点专门对付偶发的尖峰毛刺滑动平均窗口取8个点用来平滑连续的波动。窗口大小不是拍脑袋定的5点中值能滤掉一个孤立的异常点又不至于让真实快速变化被抹平太多8点滑动平均在100ms采样周期下相当于0.8秒的响应时间对HVAC这种大型惯性系统来说完全够用。#define MEDIAN_N 5 #define AVERAGE_N 8 float temp_filter(int ch, float raw) { static float buf[2][AVERAGE_N]; static uint8_t index[2] {0, 0}; float sorted[MEDIAN_N]; float sum 0; // 中值滤波 buf[ch][index[ch]] raw; for (int i 0; i MEDIAN_N; i) sorted[i] buf[ch][(index[ch] i) % AVERAGE_N]; // 冒泡排序取中值实际代码可改用插入排序 for (int i 0; i MEDIAN_N - 1; i) for (int j 0; j MEDIAN_N - i - 1; j) if (sorted[j] sorted[j 1]) { float t sorted[j]; sorted[j] sorted[j 1]; sorted[j 1] t; } float median sorted[MEDIAN_N / 2]; index[ch]; if (index[ch] AVERAGE_N) index[ch] 0; // 滑动平均 static float avg[2] {0, 0}; avg[ch] (avg[ch] * (AVERAGE_N - 1) median) / AVERAGE_N; return avg[ch]; }这个滤波器的实现方式是递推式不需要维护一个数组反复求和每次新数据进来只有一个乘法加一个除法计算量可以忽略不计。有一点值得提递推平均的初始阶段会有一个从0逐渐逼近真实值的过程实测大概10个周期后进入稳态如果系统上电需要立即显示有效温度可以把初始化逻辑改成前N个点直接求普通平均避免起始掉电时显示一串不合理的低温值。3.4 数据输出策略与调试手段温度数据最终要通过串口发出去。调试阶段我用的是可读字符串一行一行的打印看着直观。正式运行阶段我改用Modbus RTU协议因为HVAC上位机基本都支持Modbus采集。Modbus比自定义协议省心的地方是现场运维人员熟悉它遇到问题能用现成的Modbus调试工具直接看寄存器值不需要我的自定义上位机在场。Modbus的实现我用的是串口中断加状态机没有引入重量级的协议栈。温度值放到保持寄存器里地址从0x0000开始本地温度占一个寄存器远程温度占一个寄存器单位是0.01摄氏度。上位机读的时候直接以带符号整数形式读出再除以100精度和显示都够用。另一个调试手段是LED指示。板上留了一颗LED正常模式下500ms闪一次传感器通信异常时快闪100ms两次这个简单的指示让我在现场不用开电脑就能判断系统健康状态。温度监测项目大多数故障是通信链路问题有了LED状态指示操作工一个电话就能告诉我现象省掉了我大老远跑一趟的麻烦。4. 常见问题与排查技巧实录4.1 传感器无响应与读值无效PJ85718DM上电后I2C读取不到ACK这是嵌入式采集项目里最常遇到的问题。我遇到的情况可以归成三类供电没加上、I2C地址不对、总线被拉死。先说供电。PJ85718DM对供电电压有要求如果板子上的3.3V稳压器输出偏低模块内部逻辑电路可能处于欠压状态表现为时好时坏。排查方法很简单万用表量模块VDD引脚对地电压同时用示波器看有没有高频纹波。上次遇到一个批次板卡死活读不到设备最后发现是LDO的EN引脚被串联电阻分压拉低了输出只有2.9V换了分压电阻后问题立刻消失。再说地址。很多人以为器件地址是手册里那个7位地址直接左移一位发出去这本身没错但如果总线上挂了多颗传感器地址冲突就会导致ACK异常。PJ85718DM如果有地址配置引脚就通过引脚硬件区分如果没有就得用I2C多路开关。我这次只挂了一颗模块不存在冲突问题但代码里还是保留了扫描总线的函数专门用来枚举哪些地址有响应。最后是总线拉死。SDA线被某个设备拉低整条I2C总线就瘫痪了。这通常发生在MCU重启但传感器还处于半工作状态时传感器内部状态机混乱把SDA锁住了。解决方法是让MCU在初始化I2C前对SDA和SCL各发送9个时钟脉冲把所有可能的半字节状态清掉。这段逻辑写进初始化函数后这个故障基本没有复现过。4.2 温度跳变超限的问题温度读数偶尔跳到几百摄氏度或者变成绝对零度以下这类问题通过滤波能压掉但作为开发者不能只想止痛不想治病。我排查了几轮后确认跳变值本质上是原始寄存器读到了全1或异常的边界值原因有两个读取时序被打断和模块供电瞬间跌落。读取时序被打断多发生在中断密集的情况下。串口中断和定时器中断把I2C读取过程切成碎片单片机的硬件I2C在极端情况下状态没同步读回来的字节就是错的。我在采样代码前面加了临界区保护把读寄存器这段逻辑用关中断包起来跳变概率立刻降了一个量级。供电瞬间跌落则和现场大功率设备启停强相关。风机刚启动时电流冲击会造成母线电压瞬间跌落MCU还在跑但传感器内部模数转换结果已经乱了。解决方式不是加大电容硬扛而是让MCU在读取传感器之前先检查一下电源电压是否稳定用ADC采样电压值低于阈值就跳过本周期等下个周期再读。这套逻辑的效果比单纯滤波好得多。uint16_t vdd_adc read_vdd_adc(); if (vdd_adc VDD_THRESHOLD) { return; // 电压不稳跳过本次采样 }4.3 远程通道误差偏大的问题远程探头和本地探头在同一个模块上读出来本地数据正常远程数据偏偏偏高或偏低这基本上不是模块的问题而是远端传感器引线或探头本身的问题。三线制的线阻补偿虽然解决了一部分误差但探头连接处的接触电阻如果出现氧化阻值会变成几欧姆甚至几十欧姆温度换算出来就偏了。处理思路是定期做校验。我在软件里留了一路校准通道现场维护时用标准温度计和多点开关配合记录每个远程点位的偏差值然后把偏差存到Flash里运行时从Flash读出来对测量结果做补偿。这个功能看起来不起眼却让现场运维人员很满意因为他们不需要再拿烙铁换探头来调整直接在参数菜单里输入偏移量就行了。如果远程探头线路过长超过50米即使三线制也可能扛不住环境干扰。这种距离下我会考虑换成带数字输出的远程探头或者加一个总线中继器把远程探头的模拟信号在源头先变成数字信号。接口形式变了但MCU侧代码不用动因为PJ85718DM只是换了一个远程数据来源上层读取逻辑保持一致。4.4 HVAC 现场干扰的专项对策HVAC控制柜里最不缺的就是干扰源。接触器线圈断开瞬间会产生几百伏的感性尖峰变频器的PWM输出会在空间形成强电磁场这些干扰一旦耦合到I2C或探头线上温度采集就会出现莫名奇妙的间歇性错误。我的实地排查经验是按照干扰耦合路径一条条排除。第一步看线缆走线动力线和信号线有没有共管第二步看接地屏蔽层是不是单端接地、控制柜的接地排是否干净第三步看供电传感器和风机是不是共用一路开关电源。这三个问题解决掉九成干扰问题都能缓解。剩下的顽固问题才需要上手段比如给I2C加光耦隔离或者把整个数据采集板装进屏蔽盒。疑点检查方法解决手段强弱电共管打开线槽检查走线重新布线或增加金属隔板屏蔽层两端接地万用表测屏蔽层两端通断改为单端接地供电纹波大示波器测3.3V纹波换LDO或加LC滤波总线过冲严重示波器看I2C上升沿上拉电阻调整为4.7k到10k5. 从温度采集到场景落地的进一步思考5.1 多节点扩展与总线负载这套方案做单点监测够用但要覆盖一层楼的多个房间就得考虑多节点扩展。PJ85718DM如果地址不可配可以改用I2C多路开关TCA9548A这类器件每个开关分出8路独立总线一根I2C主线理论上可以带几十路传感器。多节点之后要注意总线的实际驱动能力。STM32F411RE的I2C引脚驱动能力有限节点太多时上拉电阻要往下调但调低又会反射所以长距离多节点我建议改成CAN总线或RS485。PJ85718DM本身是I2C接口转换一下就行MCU侧的代码可以保留大部分只是把I2C读取函数换成总线适配层。调过一套四层楼的HVAC监测系统每层楼放一个采集板板子之间用RS485串起来。单片机侧的主从通信我用的是轮询模式200ms一个周期轮完4个板子轮询周期里同时包含了健康状态上报哪个板子通信断了立刻能在主板上看到报警。这种架构的好处是运维逻辑简单故障定位直接定位到楼层不需要逐点排查。5.2 与HVAC控制逻辑的联动温度数据采集出来只是第一步真正让这套系统产生价值的是把温度用进控制逻辑里。本地温度用来做设备保护比如回风温度过高时停压缩机远程温度用来做区域补偿比如远端房间温度偏低就减小水阀开度。两个数据配合使用比只看单一温度点要合理得多。我在调试中遇到过一个有意思的现象本地温度正常远程温度偏高PID把冷水阀开度加大但房间温度迟迟降不下来。后来发现是远程探头安装在阳光直射的位置辐射热导致温度虚高控制逻辑一直在无效加码。这也验证了一个经验远程温度数据不仅要准安装位置更要科学否则控制系统就会被一个错误数据带偏。把温度数据和设备联动之前我建议先做一周左右的纯监测运行记录温度曲线对比现场人工测温的点位确认所有远程探头偏差都在可接受范围内再开放控制接口。顺序不能反数据没验证就进控制回路出了偏差整个系统都会人仰马翻。做这套系统我最有感触的一点是嵌入式温度采集的硬件方案并不复杂真正决定项目成败的是细节供电纹波、线缆走线、滤波窗口大小、通信时序保护每一个环节都可能藏着一个坑。PJ85718DM加STM32F411RE这套组合的价值在于它把硬件层面的大多数不确定性都封装掉了让开发者能把精力集中在数据可靠性和控制逻辑上。如果你也在做类似的项目不用急着换更贵的传感器先把接线、电源、滤波、时序这几件事做实很多所谓的高端需求自然就消失了。
阅读完成 · 觉得有帮助?