
前阵子帮某设备厂做了一套 HVAC 控制柜改造要求把“控制器附近的温度”和“分布在现场各处的温度”都集中到一块板子上统一显示、统一报警。最后选型的时候定了 STM32F746VG 做主控温度采集用 PJ85718DM。这套方案做下来结构上很清晰PJ85718DM 负责把温度转成数字量STM32F746VG 负责调度读取、滤波、协议转换和人机交互。本地温度用主控板上的 PJ85718DM 直接通过 I2C 读远程温度则是分布在风道、回风口、管道外壁上的若干个 PJ85718DM 采集节点通过 RS485 总线汇入主控。做完以后整套系统实测工作稳定数据刷新率能到每秒 4 次以上远程节点最长的布线距离跑到过 300 米左右。这篇文章想把一套完整的本地/远程温度监测系统怎么拆、怎么做、怎么调都说清楚。适合正在做 HVAC、变频柜、洁净室、机房监控这几类项目的朋友参考。不管你是还没选型还是画板的时候遇到了问题或者已经在调 I2C 和 RS485都能找到一些可以直接用的经验。1. 项目拆解本地温度与远程温度到底怎么分1.1 场景需求为什么不能只测一路温度很多刚接手 HVAC 项目的开发者容易忽略一个问题温度不是一个点一个房间、一个风柜的温度至少需要测好几个位置。举一个最简单的例子一套风机盘管系统回风温度、送风温度、盘管进口水温、盘管出口水温、室外温度这五路数据全部有意义。回风温度代表房间当前温度送风温度影响人体舒适度进出水温差能判断换热效率室外温度则作为节能控制的补偿依据。如果你只把 MCU 旁边的温度测出来那只是“电路板的温度”不能代表实际被控对象。我把本项目的测温需求分成两类本地温度主控电路板附近的空气或散热器表面温度用于保护电子元器件、判断机柜通风是否正常。远程温度远离主控分布在 HVAC 设备各关键测点的温度用于控制逻辑和能效分析。这两类温度的物理意义不同采集方式也不同。本地温度要的是“快”和“靠近芯片”远程温度要的是“能拉线”和“抗干扰”。所以不能用一套简单方案硬套必须从架构上有区分。1.2 系统架构主控、传感器和总线的分工整个系统的硬件拓扑可以看成三部分。第一部分是主控板。STM32F746VG 跑主逻辑板载一个 PJ85718DM作为本地温度传感器。它挂在 I2C 总线上地址设成 0x48和主控间距只有几厘米读取速度很快几乎不占用资源。第二部分是远程采集节点。每个节点由一块单独设计的采集小板组成PJ85718DM 一颗小单片机我这边用的是一颗 STM32G0 系列 RS485 收发器。PJ85718DM 负责测现场温度小单片机负责把温度从 I2C 读出来再转成 Modbus RTU 从站协议最终通过 RS485 总线传给主控。第三部分是总线链路。所有远程节点并联在两根双绞线 A/B 上末端加 120Ω 终端电阻。主控作为 Modbus 主站周期轮询各节点地址逐个拿回温度数据。这条链路把“远程温度”从物理上拉了回来。有的朋友可能会问为什么远程节点不用 STM32F746VG 作为主控直接通过 I2C 拉长线答案很简单I2C 本质上是板级总线速度、抗干扰、寻址能力都不适合长距离多点传输。RS485 是差分信号传输几百米没什么问题而且多节点轮询非常成熟。既然标题里写的是“嵌入式和 HVAC 应用”工程现场必然有强电、变频器、电机这类环境里 RS485 远比 I2C 可靠。1.3 为什么选 STM32F746VG 做主控其实做纯温度采集一颗便宜的 M0 内核 MCU 就够了。但实际项目里主控还要承担几件事跑 PID 控制算法、操作 TFT 屏幕、控制继电器、预留以太网接口向上位机上报数据。这种情况下选 STM32F746VG 就顺理成章了。这颗芯片的硬资源对我很有吸引力主频 216MHzCortex-M7 内核带双精度硬件浮点单元跑浮点滤波算法非常轻松。外设方面多路 UART可以同时接 RS485、调试串口、蓝牙模块I2C 也有多个本地传感器和 EEPROM 可以分开挂自带以太网 MAC加一颗 PHY 芯片就能把数据上报给远程监控中心。举个实际例子我在这套系统里跑了一个四路 PID 温控循环PWM 频率 2kHz同时还在用 LWIP 做 UDP 数据上报CPU 占用率只到 20% 上下。如果换成低主频的 M0别说同时干这些活光是浮点运算就得用软件模拟实时性会差很多。选型还有一个隐藏因素生态。STM32F746VG 的 HAL 库和参考代码很全社区讨论也多。项目工期紧张时这类成熟东西能帮你省下大量踩文档坑的时间。2. 关键器件选型解读PJ85718DM 和 STM32F746VG 各管什么2.1 PJ85718DM一颗典型的 I2C 数字温度传感器先把我手上这颗 PJ85718DM 的基本特性整理一下。它是一颗支持 I2C/SMBus 接口的数字温度传感器内部有带隙温度基准和 12 位 ADC不需要外接任何模拟调理电路直接输出电压量。主要参数如下参数数值测量范围-40℃ ~ 125℃精度±0.5℃典型温度分辨率9~12 位可配置默认 12 位接口I2C支持 SMBus 报警供电2.7V ~ 5.5V功耗低功耗模式空闲时微安级可编程地址3 个地址引脚最多 8 个设备PJ85718DM 的寄存器结构也很直观核心就四个寄存器温度寄存器、配置寄存器、上限阈值寄存器和下限阈值寄存器。读取温度时只需要从温度寄存器读两个字节然后把数据右移三位再乘以 0.125就是实际温度值。这个传感器最让我满意的一点是内置了可编程报警输出引脚。你可以把上限阈值设成 75℃温度超过后 OS 引脚会被拉低直接触发主控的 I/O 中断。这个功能在 HVAC 场景里非常实用因为风柜现场可能有异常高温靠主控软件轮询也行但要做到毫秒级响应硬件中断更可靠。2.2 STM32F746VG 的引脚与外设规划我在做引脚规划时第一原则是“先把通信外设分清楚再分配 GPIO”。具体分配如下功能外设引脚本地 PJ85718DMI2C1PB8SCL/ PB9SDARS485 主站串口USART3PB10TX/ PB11RXRS485 方向控制GPIOPD4以太网 RMIIETHPA1/PA2/PC1/PA7/PC4/PC5 等报警输入EXTIPE2这里有个细节RS485 的方向控制引脚不能随意接最好选一个具备定时器复用功能的引脚。原因是 Modbus 轮询时发送完数据后往往需要精确延迟一段时间再关闭发送使能这个延时用定时器输入捕获来测量比单纯用 while 循环延时稳定得多。2.3 两颗芯片配合起来解决什么问题PJ85718DM 负责模拟世界的测量STM32F746VG 负责数字世界的处理。前者把温度变成 0 和 1后者把 0 和 1 变成控制策略。在本地测温上PJ85718DM 直接参与MCU 读取、软件滤波、与机柜内其他传感器交叉验证。在远程测温上PJ85718DM 在每个节点板子上测量STM32F746VG 通过 Modbus 轮询汇总所有节点的数值。整个系统做到了“就地采集、集中处理”既避免了模拟信号长线传输的衰减问题又减少了主控背负的计算压力。3. 硬件设计细节与实操要点3.1 本地传感器连接I2C 的电路设计PJ85718DM 挂在 STM32F746VG 的 I2C1 上电路连接非常常规。VDD 接 3.3VVDD 和 GND 之间必须放一个 100nF 陶瓷电容距离芯片引脚越近越好。SDA、SCL 各接一个 4.7kΩ 上拉电阻到 3.3V。A0、A1、A2 三个地址引脚根据需要接高或低我这边直接全部接地地址就是 0x48。有一点要提醒如果在同一条 I2C 总线上还有其他设备上拉电阻的阻值需要重新计算。多个设备的 SDA/SCL 引脚相当于并联接在一起等效下拉能力变强但上拉电阻并联后等效阻值变小。一般一对 4.7kΩ 上拉电阻能挂三四个标准 I2C 设备如果挂得更多换成 2.2kΩ 会更稳。PCB 布局上PJ85718DM 要尽量远离发热器件比如功率电阻、DC-DC 电感和 MOS 管。我在第一版 PCB 里把本地温度传感器放到了电源芯片附近结果空载时温度读数比实际环境温度高了 6℃后来把传感器挪到板边温度才恢复正常。这个教训让我以后再画板时会把“传感器远离热源”当成硬性规则。3.2 远程采集节点RS485 总线怎么搭远程节点是整套系统里最容易出问题的地方。每个远程节点由三部分组成PJ85718DM、单片机、RS485 收发器。单片机负责读 PJ85718DM再通过收发器接到总线上。这么做的好处是节点和主控之间传输的是标准差分电平抗共模干扰能力强。电路设计上我关注几个点收发器的 A/B 端接两根总线A 接上拉B 接下拉保证总线空闲时电平确定。每个节点的 RS485 收发器电源建议加隔离至少也要用一块独立的 5V 转 3.3V LDO避免现场干扰通过电源窜进总线。总线末端必须接 120Ω 终端电阻不接的话长线传输时会出现反射数据容易坏。如果现场节点很多推荐用带自动方向控制的 RS485 收发器或者仔细控制 DE 引脚时序否则收发切换会产生乱码。远程节点的地址用拨码开关设定我采用二进制编码三根拨码脚最多 8 个地址。现场调试时如果出现地址冲突两个节点同时回数据总线会乱套。因此每个节点投运前一定要先单独接上去扫描总线上能搜到几个设备。3.3 电源与地线的处理HVAC 柜内往往同时有风机、阀门、变频器和接触器这些设备拉低或抬高地的电势会造成传感器数据跳变。我的处理策略是“集总滤波分级供电”。主控板供电入口先经过一个共模电感和两级 π 型滤波然后给 RS485 收发器独立供电。模拟部分和数字部分不刻意分割地平面但在传感器底下单独铺一小块“传感器地”通过一个 10Ω 电阻和主地相连。这样既能保持回流路径短又能滤掉一部分高频噪声。远程节点因为走线长我坚持使用隔离式 RS485 方案电源用隔离模块信号用磁隔离。实际效果非常明显在没有隔离的试验板上变频器一启动温度读数会有 ±0.6℃ 的波动加了隔离之后同样条件下波动降到 ±0.1℃ 以内。4. 软件架构与核心代码实现4.1 I2C 驱动与本地温度读取先说本地温度的读取。STM32 的 HAL 库提供了非常方便的 I2C 内存读写接口。读取 PJ85718DM 温度寄存器的逻辑如下#define PJ85718DM_ADDR 0x48 #define TEMP_REG 0x00 uint8_t buf[2]; int16_t raw; float temperature; HAL_I2C_Mem_Read(hi2c1, (PJ85718DM_ADDR 1), TEMP_REG, 1, buf, 2, 100); raw (buf[0] 8) | buf[1]; raw 3; temperature raw * 0.125f;这段代码里有一个容易忽略的细节温度寄存器的高 8 位和低 8 位取出后低 3 位是无效位。如果不清除这 3 位直接当成 16 位有符号数处理低温时会得到错误结果。所以拿到 16 位原始数据后先算术右移 3 位再做浮点转换。我在实际项目里没有直接用瞬时值而是加了一个三阶滑动平均。采样周期是 200ms 一次每 5 次取平均输出给控制逻辑用。这样能滤掉传感器自身的小幅噪声和风环产生的快速波动而不会让控制动作频繁启停。4.2 Modbus RTU 轮询远程节点远程节点采用 Modbus RTU 从站协议主控作为主站发请求从站回数据。功能码就一个0x03 读保持寄存器。每个从站地址由拨码开关设置需要上报的温度值放在寄存器 0x0000 里面。主控发送的请求帧格式如下从站地址 | 功能码 0x03 | 寄存器地址高 | 寄存器地址低 | 寄存器数高 | 寄存器数低 | CRC16低 | CRC16高比如请求 地址为 1 的从站读取温度寄存器01 03 00 00 00 01 84 0A从站回应01 03 02 10 48 3A 77其中0x1048是原始温度数据右移 3 位后乘以 0.125得到8.0℃。我建议把所有远程节点的解析逻辑统一封装成一个函数方便主控在轮询时减少重复代码。轮询周期不能太短。一个字节在 9600 波特率下约 1.04ms一个请求加响应的完整事务大概需要 35ms。如果接了 8 个节点一轮轮询就是 280ms勉强 3 秒刷新一次。想要更快可以提高到 19200 波特率或者把温度数据用双字节合并上报减少通信帧数量。4.3 数据滤波与异常值剔除远程温度数据在传输过种中偶尔会受到干扰比如总线瞬态尖峰导致读到明显离谱的数值。我做了两层防护。第一层是范围检查。温度值必须在 -40℃ 到 125℃ 之间超出这个范围直接丢弃再用上一次的有效值填充。这个办法简单但有效能防住大部分单字节跳变。第二层是中值滤波。对于连续读取的 7 个有效采样点先排序取中间三个做平均再把输出作为控制温度。这个算法比单纯滑动平均更能抵抗瞬时毛刺而且不会像滑动平均那样被异常值拖慢响应速度。需要特别注意如果现场信号长期不稳定过滤完以后也要留下一个“数据有效标志”。我在每一个远程节点数据后面都加了一个 freshness timer超过 10 秒没有更新就认定节点离线并报警。这样操作人员能立刻知道是传感器坏了还是总线断了而不是看着一个陈旧数据发呆。4.4 远程上报把温度数据交给监控层这套系统的“远程监测”还有一个出口通过以太网上报给上位机。我在 STM32F746VG 上运行了 LWIP开了最简单的 UDP 端口。把本地和远程所有温度打包成 JSON 字符串每两秒发一次给上位机。上位机收到后解析入库做历史曲线。UDP 虽然不保证一定送达但在局域网环境里足够而且代码简单。上位机使用固定端口监听下面的数据格式可以直接用{ local_temp: 25.6, node1_temp: 22.3, node2_temp: 19.8, timestamp: 1700000000 }建议在数据包里加一个递增序号方便上位机判断掉线情况。虽然有网口后很多项目会想直接走 MQTT但真做起来还得考虑用户的网络环境我在现场一般先打通 UDP 的稳定通道再视需求增加 MQTT 适配。5. 常见问题与调试实录5.1 I2C 探测不到传感器这个问题基本是每次新焊板的“必考题”。我调试时第一件事不是看代码而是动万用表量 PJ85718DM 的 VDD 引脚是否是 3.3V、SDA/SCL 对地电压是否是 3.3V。如果 SDA/SCL 电压只有 1V 左右大概率是上拉电阻没焊好或者芯片虚焊。第二步是用 I2C 扫描程序让主控遍历所有地址查看是否有 ACK 回包。扫描出来地址和配置对不上多半是 A0/A1/A2 引脚接法不对。还要记住 HAL 库的函数参数要传 8 位地址的左移一位这个细节很容易让人怀疑是硬件问题。5.2 温度读数来回跳误差很大温度跳动大多数是电源和布局问题。先看读数波动幅度正负 0.3℃ 以内属于正常超过这个幅度基本是干扰。检查顺序传感器旁边是否有大电流走线传感器底下有没有铺铜散热VDD 的 100nF 电容离芯片够不够近。我遇到过一块板子PJ85718DM 的电源和一路继电器驱动共用一个 3.3V继电器一吸合温度就跳 0.5℃。后来在传感器电源前端加了一个 10Ω 电阻和一个 10µF 电容问题消失。5.3 RS485 偶尔丢包数据乱码RS485 丢包的原因不外乎几种终端电阻没接、方向切换时序不对、波特率误差太大。方向切换是最容易踩的坑。主控发送完数据后如果立刻把 DE 拉低此时收发器还在发送最后一个停止位总线被“切了半截”从站会收到不完整的帧。我在代码里发送完以后加了一个 10ms 延时再关闭 DE实测 9600 波特率下丢包率从 5% 降到几乎为 0。如果追求更精细可以用定时器计算发送 1 字节的耗时乘上字节数得到精确延时。最极端的一次是现场有两条 RS485 设备接在同一总线上一个用 120Ω 终端一个用 100Ω加上走线蛇形弯总线上反射严重。把终端电阻统一成 120Ω 并在最末端才接之后通信立刻恢复正常。5.4 远程节点温度偶尔变负数出现负数先别急着怀疑传感器坏了。PJ85718DM 的温度寄存器是无符号格式时0x8000 表示负温但右移 3 位后的符号位处理容易出错。代码里如果用uint16_t接收原始值然后直接乘以 0.125负温度就会变成很大的正数。必须用int16_t并且让算术右移保留符号位。我自己的调试经验是先把一个节点的温度传感器换成已知温度源再用串口打印原始值人工换算一遍确认软硬件链路正确以后再批量焊接。这样能避免一次烧几十个节点后才发现是个低级 bug。5.5 怎么校准一个相对可靠的温度HVAC 控制不一定要求绝对精度但至少要做到和标准水银温度计误差不超过 ±0.5℃。最实用的校准办法是两点校准把传感器放在 0℃ 冰水混合物里测一次再放在 50℃ 恒温水浴里测一次记录偏差。假设在冰水和热水里测得的误差分别为 offset0 和 offset50那么线性修正公式就是float corrected_temp (measured_temp - offset0) (offset0 - offset50) / 50.0f * measured_temp;我在每批焊接完的节点上随机抽两个做校准其余按出厂值。没校准的节点误差在 ±0.8℃ 左右校准后可以压到 ±0.3℃。对于 HVAC 的送风温度控制和防冻保护来说这个精度足够。6. 整套系统跑下来的体会这套基于 PJ85718DM 和 STM32F746VG 的本地/远程温度监测系统前后调试了大概两个星期。整体运行后主控板 CPU 占用率不到 20%本地温度刷新率 5Hz远程 8 个节点全部接入时刷新率也有 1Hz 左右。作为 HVAC 控制的前端数据采集性能非常富余。整个过程里我最满意的其实是 PJ85718DM 这颗传感器本身。虽然看起来不起眼但它把温度采集这件事做得非常省心不涉及模拟运放、不用频繁校准I2C 读出来就是干净的数字量。最痛苦的则始终是 RS485 那条总线尤其是方向切换和长线抗干扰。如果让我重新设计我可能会在第一版就加自动方向控制的收发器能省掉不少软件上的折腾。这个项目后续我还打算做两件事。第一是把远程节点从单一温度扩展为温湿度一体把同样一套 Modbus 轮询逻辑直接用起来只需要修改寄存器地址定义主控代码几乎不用动。第二是给主控加一个配置文件通过串口直接修改节点的地址和报警阈值省去每次客制化都要重新编译固件的麻烦。如果你也在做类似的环境监测项目我建议你一开始就把“本地温度”和“远程温度”当成独立的通道设计不要混在一起。本地温度看趋势远程温度看控制两者都有冗余性单点失效也不至于让整个系统失控。工程上的教训往往是方案越简单现场越可靠。