前阵子接了个楼宇自控相关的项目控制柜要同时盯两处温度机柜内部靠近回风口的环境温度以及楼顶新风管道深处的送风温度。以前遇到这种场景我的第一反应是上热电阻加调理电路但这次我换了思路用 PJ85718DM 这颗数字温度传感器配合 STM32F373VC 主控把本地和远程温度监测一起做掉了。这篇文章主要是把这套方案的选型理由、接线方式、驱动代码和联调中踩过的坑记录下来给准备做嵌入式温控、HVAC 监控或者其他需要“多测点、低成本、精度够用”的开发者一个参考。如果你手头也有一颗这样的数字温度传感器想快速挂在 MCU 上跑起来这篇文章可以直接照着抄。1. 需求落地一个控温任务里“本地”与“远程”到底怎么接线1.1 “远程”不是无线而是布点距离与数据链路的组合先明确一个概念标题里的“本地与远程温度”在实际 HVAC 项目里通常不是指 Wi-Fi 或无线传输而是指测点物理位置的远近。我这次的项目场景很典型控制柜在配电间新风管道在走廊吊顶深处两者直线距离不到二十米但中间隔着墙体、桥架和一堆强电电缆。本地温度用来监控控制柜内部散热是否正常防止夏天柜内过热导致主控死机远程温度则要参与新风阀开度联动送风温度一旦低于某个阈值就要触发防冷风保护。所以项目里其实有两条信息链路一条是传感器到主控的采集链路另一条是主控到上位机或触摸屏的通信链路。前者解决“远端测点怎么把数据传回来”后者解决“本地测得再准上位机怎么知道”。1.2 两条 PJ85718DM 挂在同一条 I2C 总线上的拓扑传感器选了 PJ85718DM这颗芯片是 I2C 接口的数字温度传感器典型测量范围能覆盖 -55 到 125℃日常 HVAC 场景里的 0 到 70℃ 区间完全够用。它内部直接完成温度到数字量的转换主控只要通过 I2C 读寄存器就行不需要额外搭运放和 ADC。两个测点的接线方式我一开始就定了本地传感器和控制板在同一个小空间内直接四根线连远程传感器放在风管探头里通过屏蔽双绞线拉过去。两颗传感器挂在同一条 I2C 总线上通过地址引脚区分。我买的是支持两到三个可配置地址的版本实际配置成两个地址即可传感器地址引脚状态挂载位置用途PJ85718DM 第一颗A0 接地控制柜内本地温度PJ85718DM 第二颗A0 接 VCC新风管道探头远程温度这里有个细节地址引脚不能浮空宁可多花一秒钟把引脚焊到确定的电平也不要偷懒不接。浮空状态下地址不稳定总线偶尔枚举不到设备排查起来非常痛苦。1.3 供电和上拉的工程设计电源方面系统里有 24V 开关电源先降到 3.3V 给主控和传感器供电。我建议给传感器供电单独加一个 100Ω 磁珠和一两个 0.1μF 陶瓷电容把主控数字电路的高频噪声隔开。HVAC 控制柜里常有变频风机和压缩机启动电源上的一点点毛刺都可能让传感器输出抖动。I2C 上拉电阻我选了 4.7kΩ默认 400kbps 模式下工作正常。这个阻值是常规选择但如果你把传感器拉到十米以外就要重新算一下后面第 4 节我会专门聊这件事。还要注意I2C 总线上的 SDA、SCL 不是随便两根杜邦线就能乱飞的地方。HVAC 柜内空间拥挤信号线尽量远离继电器、交流接触器的线圈端子至少保证 10mm 以上的距离或者直接走屏蔽双绞线屏蔽层单端接地。2. 选型逻辑数字传感器配带片内放大器的 MCU省掉的不只是运放2.1 直接数字输出把模拟链路的坑都填平了很早以前做温度采集我习惯用 NTC 热敏电阻因为它便宜到几乎不要钱。但 NTC 的问题是电阻-温度曲线高度非线性而且个体一致性差。同一个型号的一批货在 25℃ 时阻值能差出百分之几这意味着每个通道都要单独校准否则精度根本没法看。后来换成 PT100精度确实上去了但配套的调理电路也上来了恒流源、仪表放大器、冷端补偿、外部 ADCPCB 面积和 BOM 成本翻着倍涨。而且这些模拟环节里任何一处不干净最终误差都会叠加到温度值上。PJ85718DM 这种数字传感器把整个模拟链路都省掉了。芯片内部自带温度感测单元和 ADC数字量通过 I2C 直接送进 MCU。我要做的只是初始化总线、读寄存器、换算单位再处理一下滤波和校准。对于 HVAC 这种“精度在 ±0.5℃ 左右就能接受”的场景数字传感器就是效率和成本的平衡点。2.2 STM32F373VC 真正加分的是集成 PGA 和高精度 ADC有人会问既然 PJ85718DM 已经自带 ADC 了那主控选一颗普通 STM32F1 不就行了为什么偏要选带 F373 后缀的我的理由是要给后续扩展留余地。STM32F373VC 这颗主控除了 Cortex-M4F 内核、72MHz 主频、256KB Flash 这些常规资源之外比较特别的地方是内部集成了可编程增益放大器PGA和 16 位 Σ-Δ ADC还有多个独立的 I2C 外设。在实际项目里这套组合的价值非常明显。我这次虽然只用了数字传感器但后期客户可能临时要求增加一路 PT1000 测水管温度或者接一个 0-10V 输出的室外温湿度变送器。如果主控本身带 PGA 和高精度 ADC我就直接在片内做信号调理不用重新改版加运放和外部 ADC 芯片。散热上F373 的工作温度范围也覆盖工业级区间放在控制柜里不用额外加风扇。当然不会所有项目都需要用到片内 PGA这只是一个选型上的“保底选项”。真正常用的反而是 F373 的多路 I2C 外设我把本地传感器挂在 I2C1 上远程那条挂在使用 GPIO 模拟的慢速 I2C2 上两条总线互不干扰比共用一条总线省心得多。2.3 对比传统方案成本和调试时间差在哪里我不喜欢空谈选型更习惯用一张表格做决策方案传感器成本外围电路校准成本精度表现适用场景NTC 运放 MCU ADC低中等每通道校准一般受温漂影响大消费类、对精度要求不高的温度监测PT100 仪表放大器 外部 ADC高高需要精密电阻匹配高实验室、工控高精度场景PJ85718DM STM32F373VC低极低只需两点校准即可满足 HVAC 需求暖通、嵌入式多测点监测实际做下来数字传感器方案的 BOM 里基本就是传感器、两个上拉电阻、磁珠和电容外加主控本身就有的 I2C 外设。调试时间也明显缩短我不需要拿示波器去盯放大器的输出漂移只要确认总线波形干净、寄存器读数稳定就行。3. 驱动与读数寄存器配置、单次转换和温度值的符号处理3.1 初始化代码先把总线和寄存器摸清楚PJ85718DM 的寄存器映射不同批次可能略有差异我建议拿到芯片先做两件事读数据手册确认设备地址和寄存器地址然后用一个空工程把“能读到设备 ACK”作为第一目标。下面是我基于常见映射写的初始化代码具体寄存器数值以你手头芯片的手册为准#define PJ85718DM_ADDR_LOCAL 0x4C // 本地传感器地址 #define PJ85718DM_ADDR_REMOTE 0x4D // 远程传感器地址 #define REG_TEMP_H 0x00 #define REG_TEMP_L 0x01 #define REG_CONFIG 0x04 void bsp_temp_sensor_init(void) { // I2C1 400kHz地址模式 7bit HAL_I2C_InitWithConfig(hi2c1, 400000, I2C_ADDRESSINGMODE_7BIT); // 典型配置测量分辨率最高、单次触发模式 uint8_t cfg 0x80; // 具体值请参考数据手册 CONFIG 寄存器 HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR_LOCAL, REG_CONFIG, 1, cfg, 1, 100); HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR_REMOTE, REG_CONFIG, 1, cfg, 1, 100); }很多初学者会卡在 I2C 设备地址的“8 位”和“7 位”换算上。HAL 库里的地址参数一般填 7 位地址但实际发送时总线上的首字节是地址左移一位后再拼读写位。如果驱动库要求填 7 位你填 0x4C如果要求填 8 位写地址那就是 0x98。别把两个混了否则设备永远 ACK 不了。3.2 读温度和负数处理不只是一个乘 0.0625 的事温度寄存器是 16 位宽度高字节在前。标准做法是把高字节左移八位后和低字节合并再判断符号。分辨率常见的是 12 位即 LSB 对应的温度值为 0.0625℃。下面这段读取函数我比较常用static float read_temp(uint16_t dev_addr) { uint8_t buf[2]; int16_t raw; if (HAL_I2C_Mem_Read(hi2c1, dev_addr, REG_TEMP_H, 1, buf, 2, 100) ! HAL_OK) return -100.0f; // 返回一个明显无效值 raw (int16_t)((buf[0] 8) | buf[1]); // 12 位数据高 4 位是符号扩展位 raw (raw 4); // 右移对齐 if (raw 0x0800) // 12 位符号位 raw | 0xF000; return (float)raw * 0.0625f; }负数温度在 HVAC 里可能不常见但新风混风段在严寒地区偶尔也会到零下符号扩展这一步不能省。如果直接把两个字节拼成 int16_t 再乘 0.0625在 12 位有效数据的芯片上会出现负温度变成巨大正数的问题。我见过不止一次有人在这里栽跟头。3.3 单次转换模式的选择总线占用低数据更可控这颗传感器支持连续转换和单次转换两种模式。我最终用了单次转换模式原因是在主循环里按固定周期读温度同一时刻总线上只有我一个主设备在发起读取逻辑非常清晰。单次转换流程是写 CONFIG 寄存器触发一次转换等待转换完成然后读温度寄存器。转换完成的判断有两种一种是查询状态位另一种是简单延时等待我习惯用延时等待典型转换时间在几十毫秒量级具体看手册。主循环每 500ms 触发一次读取功耗和实时性都兼顾了。如果你的项目要求低功耗比如电池供电的无线温度标签单次转换模式更是唯一选择平时让传感器休眠定时唤醒读数读完再睡平均电流可以压得很低。3.4 软件滤波让空调机组的风机噪声不再干扰读数刚调通的时候我发现本地温度在 25.2℃ 和 25.5℃ 之间小幅跳动虽然幅度不大但传到上位机上曲线很难看。原因不难猜柜内开关电源纹波、压缩机启动瞬间的电流冲击都可能在传感器电源引脚上造成毫秒级的毛刺。此时硬件上再加电容的意义不大了我在软件里做了两层滤波三取中值滤波连续采三个值取中位数用来扛单点毛刺滑动平均滤波中值滤波结果再进一个长度为 5 的环形缓冲区算平均用来平滑噪声。#define MED_BUFFER_SIZE 3 #define AVG_BUFFER_SIZE 5 static float temp_med_buffer[MED_BUFFER_SIZE]; static float temp_avg_buffer[AVG_BUFFER_SIZE]; static uint8_t med_idx 0, avg_idx 0; static float temp_filtered 0.0f; float temp_filter(float new_raw) { temp_med_buffer[med_idx] new_raw; med_idx (med_idx 1) % MED_BUFFER_SIZE; // 简单排序取中值数组只有 3 个元素直接硬排 float a temp_med_buffer[0], b temp_med_buffer[1], c temp_med_buffer[2]; if (a b) { float tmp a; a b; b tmp; } if (b c) { float tmp b; b c; c tmp; } if (a b) { float tmp a; a b; b tmp; } temp_avg_buffer[avg_idx] b; avg_idx (avg_idx 1) % AVG_BUFFER_SIZE; float sum 0.0f; for (int i 0; i AVG_BUFFER_SIZE; i) sum temp_avg_buffer[i]; temp_filtered sum / AVG_BUFFER_SIZE; return temp_filtered; }这段代码运行在 72MHz 主频上几乎不占 CPU 时间但效果非常明显。加上滤波之后温度曲线稳定在 ±0.1℃ 以内触摸屏上不再出现“忽高忽低”的观感。3.5 两个测点的调度思路两个传感器共用一个 I2C 外设时最忌讳在主中断和主循环里同时调用 I2C 读取函数这会造成总线冲突。我的做法很简单在 while(1) 主循环里设置一个 500ms 的软件定时器定时到之后先调用本地温度读取和滤波再调用远程温度读取和滤波两个传感器轮询式读取互不排斥。不要在 I2C 读取时用 HAL_I2C_Mem_Read 的默认 HLE 阻塞太久一定要传超时时间参数。I2C 设备如果物理连接虚焊阻塞式读取会把主控死死卡在那里。超时时间我给的是 100ms读不到就返回一个无效温度值上位机那边看到这个值就报传感器故障而不是显示一个吓人的温度。4. 远程通道短距离 I2C 延长与长距离 Modbus RTU 子站4.1 十米以内的低成本延长屏蔽双绞线加慢速模式远程传感器离主控十米以内的话可以直接把 I2C 总线延长。但 I2C 本身是为板内通信设计的布线电容一大信号上升沿就变慢通信频率就得降。我第一次把传感器装在五米远的塑料探头里用普通网线跑 400kHz结果读数时好时坏。拿示波器一看SCL 高电平上升沿已经变成一条缓坡逻辑门限附近反复跳变。解决办法有两个方向把总线速率从 400kHz 降成 100kHz 甚至 50kHz同时把上拉电阻从 4.7kΩ 换成 1kΩ增强上拉能力在远端传感器座附近加一颗 I2C 总线缓冲器把长线电容和本地总线隔离开。实测下来五米到十米以内的距离用 100kHz 加 1kΩ 上拉就能稳定工作没必要多花钱加缓冲器。如果你要拉二十米以上我更建议直接用下面的 RS485 子站方案。4.2 远距离和工业现场给 PJ85718DM 搭配 RS485 从站二十米、三十米的场景I2C 延长再怎么做都不划算。我在现场用的是更稳妥的方案远端放一个带 RS485 收发器的小采集节点节点上挂 PJ85718DM采集到的温度通过 Modbus RTU 协议回传给 STM32F373VC。这样做的稳定性比 I2C 延长高一个量级RS485 是差分信号抗共模干扰能力强能跑几百米双绞线哪怕贴着电缆桥架走也不太会受变频器噪声影响。STM32F373VC 在这套结构里扮演的角色有两个一是本地主控负责读控制柜内的温度传感器二是 Modbus 主站通过 RS485 轮询远端节点。如果你愿意也可以让 F373 当 Modbus 从站把两个温度值同时上传给楼宇上位机看现场需求。4.3 Modbus RTU 报文设计温度值用 0.1℃ 整数传输和上位机对接的时候浮点数直接丢在 Modbus 报文里容易出问题CRC 计算、字节序、小数位处理都很麻烦。我习惯把温度放大十倍用整数传输比如 25.4℃ 就传输 254上位机收到后自己除十。保持寄存器规划如下寄存器地址内容单位示例值0x0000本地温度0.1℃252 表示 25.2℃0x0001远程温度0.1℃270 表示 27.0℃标准读保持寄存器指令是这样的主站请求01 03 00 00 00 02 C4 0B从站响应01 03 04 00 FC 01 0E B8 A1其中00 FC是 25201 0E是 270最后两个字节是 CRC16。这套报文用市面通用的 Modbus 调试工具就能直接解析省得自己写蹩脚的上位机协议。4.4 RS485 侧的防护隔离和 TVS 不能省工业现场的 RS485 总线会伴生地电位差和浪涌尤其是暖通项目里变频器多总线上的共模电压可能高达几十伏。我的建议是RS485 收发器电源用隔离 DC-DC信号侧用数字隔离器隔开A/B 线之间加 TVS 管串联 PTC 自恢复保险丝避免高压直接打坏收发器屏蔽层单端接地不要在两端都接地防止地环路电流。这些措施看起来多花了几十块钱但能避免现场调试时烧一片接口芯片的窘境。我一个同事就因为在柜内直接接了双绞线没做隔离变频器一启动上位机通信就掉线最终排查了半天才发现是共模干扰。5. 联调复盘波形异常、凝露浸泡和两段式校准5.1 排查链路先怀疑程序然后发现是线缆电容的问题项目调试到第二周远程温度出现了一个很诡异的故障每天早上开机正常运行一小时后通信中断重新上电又恢复。按常规流程我开始排查先用示波器抓 I2C 波形发现波形正常时通信正常波形畸变时通信失败先确认是硬件问题再用替换法把远程传感器线缆换成短跳线故障消失锁定是长线缆引入的问题最后用万用表/电容测试仪确认二十米网线的 SDA 与 SCL 之间的分布电容太大加上线缆被桥架里的强电感应出噪声导致信号恶化。最终的修复方案不是换更好的线而是改架构远程传感器不再直接用 I2C 硬扯二十多米而是换成 RS485 子站。如果早知道现场布线条件这么差我一开始就该上子站方案。这个复盘告诉我们用 I2C 拉长线这件事在实验桌上很美好到了工地现场就是灾难现场。遇到超过十米的测点需求直接上 RS485别犹豫。5.2 远程探头凝露读数漂移的真实教训还有一个坑发生在安装阶段远程探头做成了不锈钢螺纹管直接拧在风管盲端探头和冷风接触运行几天后读数缓慢漂移最终比真实值低了近 8℃。拆开检查发现探头内部已经有冷凝水珠PJ85718DM 的 PCB 引脚都泡在水里了。数字传感器本身防护等级并不高栅格焊盘附近有水膜时漏电流会明显改变内部测量结果。解决方法是把传感器 PCB 整体用环氧树脂灌封在探头内部只露出供电和 I2C 引线探头安装方向保持“插头朝上、感温头朝下”让冷凝水自然流走不往 PCB 方向积。后来我又给探头加了一层透气膜维持大气压力平衡防止密封腔体内外压差过大导致水汽渗入。5.3 两段式校准零点和五十度点虽然 PJ85718DM 出厂有校准但你要想读出“可信赖”的绝对温度还是建议做一次两点校准。方法是零点把传感器探头放进冰水混合物里等读数稳定后记录偏差高温点用恒温水槽或精密温控箱设定 50℃ 或 80℃记录偏差在固件里存两组 offset 和 gain对换算后的温度做线性修正。校准公式很简单修正温度 原始温度 × gain offset。两颗传感器分别存一份校准参数上位机对应显示修正后的数值。我手头的两颗传感器冰点和 50℃ 点的偏差都在 ±0.3℃ 以内做完修正后整体一致性很好跨传感器温差控制在 ±0.15℃ 以内完全满足 HVAC 温控要求。5.4 常见问题速查表现象可能原因解决办法I2C 总线 ACK 失败地址引脚浮空、地址换算错误、线缆过长固定地址电平核对 7 位/8 位地址改用慢速模式温度读数跳变 10℃ 以上探头进水/凝露、电源毛刺、接插件氧化探头灌封传感器电源加磁珠重新插拔并检查端子通信偶尔中断线缆电容过大、总线上有强干扰降低速率、减小上拉电阻改用 RS485 子站上位机读到无效温度超时时间设置不当、传感器掉线超时返回无效标志增加看门狗恢复机制负温度显示为 65535 附近符号扩展忘了处理按 12 位数据做符号扩展后再乘 LSB以前做多测点温控我总喜欢把所有传感器都拉到一个主控下面节省成本结果布线复杂调试周期也被拉得很长。现在我的习惯变了短距离、少测点I2C 直接连长距离、多测点果断拆成总线节点。PJ85718DM 配上 STM32F373VC非常适合“本地一个点、远端一个点”这种中小规模方案精度够、成本低、扩展性好。最后再分享一个实用的细节PJ85718DM 这类数字传感器在第一次上电时如果 I2C 波形不稳偶尔会进入异常状态表现为一直 NACK。我在初始化里固定加了一段“总线恢复”流程把 SCL 连续拉出九个时钟脉冲再重新枚举设备。这个动作成本几乎为零但能让产品在现场复杂环境下少很多莫名其妙的问题。
阅读完成 · 觉得有帮助?