
过去一年多我在三个产品上做过同一件事用一颗低功耗 MCU 加一颗带远程通道的温度传感器把本地温度和远端温度一起读准再按周期送出去。三块板子分别是某项目的 HVAC 控制面板、一个电机驱动器和一张电池管理检测子卡测温对象虽然完全不同但骨架完全一样——PJ85718DM 负责把本地温度与远端探头的温度变成数字量STM32L021K4 负责在大部分时间里休眠只按周期醒来取数、校准、上报。如果你正在做嵌入式温度监测或者想给 HVAC 的控制器加一路远程测温这篇应该能帮你省掉不少弯路。下面我把这套设计从需求、电路、固件到联调细节完整捋一遍包括我在实际调试中踩过的几个坑能避一个是一个。1. 这套组合要解决什么本地与远程测温的两个典型场景1.1 嵌入式里的远程温度和 HVAC 里的远程温度不是一回事在写代码之前先把概念捋清楚。嵌入式系统里说的本地温度通常指传感器所在 PCB 附近的环境温度或者机箱内部温度远程温度则指放在远处测温对象里的测温二极管比如电源管理芯片、功率器件模块内部的衬底二极管。这类二极管并没有封装成传感器只是芯片里引出来的一对引脚需要专门的测温 IC 去激励和读取。HVAC 行业对两个词的理解略不同。本地温度一般指控制器所在柜体内的空气温度远程温度指安装在风管、送风口、回风口或管道表面的探头读数探头和控制器之间可能隔着 1 到 10 米线缆。HVAC 的远程强调的是距离和现场安装条件而不单纯指半导体二极管。这两种场景放在一起看共性非常清楚都需要两路测温一路在板子上一路在别处都需要较低的系统功耗因为 HVAC 控制器和嵌入式设备经常长期通电待机都需要尽可能免标定、读数稳定、抗现场干扰。PJ85718DM 正好把本地和远程两个通道做在一颗芯片里MCU 只需要通过 I2C 读寄存器不需要自己搭二极管激励电路。这就是整套设计的出发点。1.2 为什么不用两路 NTC 或 MCU 内置 ADC 直接采NTC 便宜HVAC 里非常常见但每个探头都有 B 值和精度离散性批量生产时要逐个标定或者接受比较宽的误差。MCU 内置 ADC 直采还要处理参考电压、自热、走线串扰如果板子上还有 PWM、继电器、变频电机模拟采样的抗干扰成本并不低。远程二极管测温的核心优势在于测的是两个电流下的 ΔVBE 压差二极管绝对压差的离散偏移被抵消所以探头一致性好。数字接口 I2C 比长距离模拟线好处理虽然 I2C 本身不适合拉 10 米但把模拟前端从探头搬到传感器芯片里之后探头到芯片只要两根短线或双绞线芯片到 MCU 又是标准数字总线校准和故障定位都方便得多。如果你是做纯 HVAC、远端探头距离 5 米以上、产品标定流程已经很成熟NTC 方案依然好用但如果你想要免标定、两路同时采、还要兼顾嵌入式里那种二极管测温需求这套方案的优势就体现出来了。这不是谁替代谁的问题是看你的探头形态和生产流程更匹配哪条路。1.3 为什么是 PJ85718DM STM32L021K4而不是别的排列组合PJ85718DM 从型号定位来看属于本地加远程两通道的数字温度传感器I2C 接口地址可配置带关断或单次转换模式。一颗芯片干完两个通道的活BOM 简单软件上也不用面对两片传感器的地址和时序协调问题。STM32L021K4 这边LQFP32 封装适合中小批量板子16 KB Flash 跑一个温度采集状态机足够供电从 1.65V 到 3.6V对电池和 3.3V 系统都友好stop 模式电流不到 1 µA配合低功耗定时器可以做到秒级唤醒采集。Cortex-M0 核心虽然没有硬件浮点但温度换算只是乘一个 0.0625 的常数软浮点完全感觉不到负担。如果换用普通 Cortex-M0 加独立远程测温 IC要多管一片 I2C 总线和一路使能信号如果换用带丰富模拟前端的高端 MCU成本又上去了。选这套组合本质上是把模拟前端和低压低功耗 MCU两个问题分别交给最合适的器件各管一段出了问题排查面也窄。2. 从传感器到 MCU 的电路细节供电、地址、上拉与现场保护2.1 PJ85718DM 的供电、地址与报警脚设计先交代最基础的连接。PJ85718DM 的 VDD 我习惯直接接 3.3V电源引脚旁边放一个 100 nF 陶瓷电容紧贴引脚稍远一点再并一个 1 µF如果传感器和 MCU 用同一个电源域上电时序问题基本不存在。GND 引脚底下记得铺地别让 I2C 走线穿过继电器或者加热器区域。地址引脚 ADDR 接 GND 或 VDD 可以换来不同的 7 位 I2C 地址。按这类器件的常见映射默认地址可能是 0x48ADDR 接 VDD 后变成 0x49具体以手册为准。板子上如果只有这一颗传感器地址不用搞复杂固定接死就行但如果一个系统里挂多颗ADDR 的几种接法就要在原理图阶段规划清楚别等 PCB 回来了才发现地址撞了。ALERT 脚是开漏输出我一般接一个 10 kΩ 上拉到 3.3 V再连到 MCU 的一个普通 GPIO。如果你的系统不打算用中断方式处理温度越限这颗脚也可以完全不接软件轮询寄存器就够了。真正要注意的是 ALERT 脚的配置模式这个后面在踩坑部分会详细讲。2.2 本地通道与远程通道的接法本地通道不需要任何外部元件传感器内部自带测温结。板上布局时注意离发热元件远一点如果把它贴在电源旁边读出来的就不是环境温度而是局部受热温度。很多现场问题其实从这里就埋下了。远程通道按用途分两种接法。嵌入式场景里远程测温对象通常是目标芯片内部的衬底二极管直接把传感器的 D/D- 两个引脚连到目标芯片的测温引脚即可走线尽量短两个引脚之间不要跨过 PWM 输出或高频信号线。HVAC 场景里远程通道需要把一个 PN 结封装成独立探头推荐用二极管接法的 PNP 小信号三极管发射极接 D基极和集电极短接后接 D-。这个接法符合多数远程测温 IC 的电流方向约定双绞线引到控制器线长控制在手册标称范围内。极性一定不能接反接反了读数会直接变成乱码或者明显错误装机前先用万用表确认一遍。2.3 I2C 上拉电阻的计算与电平匹配I2C 是开漏总线必须有上拉电阻。上拉阻值取决于总线电容和时钟频率公式是R_pullup ≤ t_rise / (0.8473 × C_bus)同时还要保证从机在拉低时能满足低电平输出要求。实际计算起来不用太精确按经验值选就好总线电容100 kHz 推荐400 kHz 推荐100 pF4.7 kΩ ~ 10 kΩ2.2 kΩ ~ 4.7 kΩ200 pF3.3 kΩ ~ 4.7 kΩ1.5 kΩ ~ 2.2 kΩ板子上的走线很短总线电容通常在 100 pF 以下所以我量产设计里 100 kHz 用 4.7 kΩ100 kHz 是首选。HVAC 现场电磁环境差低速总线本身抗干扰能力就更好没有必要为了快那一点去跑 400 kHz。电平匹配方面PJ85718DM 和 STM32L021K4 都用 3.3 V 供电时不存在问题但如果传感器是 5 V 供电的版本就要考虑开漏上拉到 3.3 V 还是 5 V以及 MCU 引脚是否容忍 5 V。用同一个电压域是最省事的我这边全按 3.3 V 设计。2.4 HVAC 现场保护电路探头线进柜子之前做的事HVAC 现场最怕两件事雷击感应和变频器传导干扰。探头线进入控制柜之后我会在传感器之前加一级保护D/D- 之间并一颗双向 TVS对地也加 TVS再串一个 1 kΩ 左右的限流电阻靠近传感器引脚放。电源线上加磁珠I2C 走线尽量短而直。线缆屏蔽层的接地也有讲究。屏蔽双绞线的屏蔽层应该在控制器侧单端接地不要两端都接否则会形成地环路反而把干扰引进系统。我见过不少现场案例读数周期性跳动最后查出来就是屏蔽层两端都接了地。另外提醒一句I2C 总线不适合像 RS485 那样长距离挂一堆节点。如果 HVAC 系统里有多路远程探头更合理的做法是每路探头配一颗带远程通道的传感器芯片然后通过 RS485 或 Modbus 汇总到主控不要在一条 I2C 上硬拉 10 米。3. STM32L021K4 固件I2C 读取、校准与低功耗调度3.1 初始化引脚复用、时钟和 I2C 参数固件这边我按 HAL 库来写因为 STM32L021K4 的资源有限用 HAL 反而省心。先初始化 GPIOSCL 用 PB6SDA 用 PB7复用为开漏输出内部上拉关掉因为外部已经有 4.7 kΩ 上拉。然后初始化 I2C1__HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_OD; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 100000; hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; HAL_I2C_Init(hi2c1);这里有两个容易忽略的点。第一速度等级不要选太高低速 GPIO 配合 100 kHz 时钟足够还能减少边沿噪声第二I2C 时钟频率我建议直接定 100 kHz现场稳定远比速度重要调试的时候再临时切 400 kHz 测性能也不迟。3.2 读温度寄存器的时序与代码这类温度传感器的标准读法是一样的先写寄存器指针再读数据。以读本地温度为例先把指针指向温度寄存器然后连读两个字节uint8_t reg 0x00; uint8_t rx[2] {0}; HAL_I2C_Master_Transmit(hi2c1, PJ85718DM_ADDR, reg, 1, 50); HAL_I2C_Master_Receive(hi2c1, PJ85718DM_ADDR, rx, 2, 50); int16_t raw (int16_t)((uint16_t)(rx[0] 8) | rx[1]); float temperature (float)raw * 0.0625f;其中 0.0625 是这类器件常见的温度分辨率因为内部 ADC 是 12 位最高位是符号位每位代表 0.0625°C。注意这里的符号处理必须先转成 signed 16 位再做乘法否则后面全是负数处理问题。寄存器具体是 12 位左对齐还是右对齐不同器件不同以手册为准如果手头的寄存器格式和示例不同把移位逻辑调整一下就行整体流程不变。远程通道的读法完全一样只是寄存器指针不同。代码层面建议把写指针 读两字节 换算温度封装成一个函数本地和远程各调一次别在主循环里裸写 I2C 事务。3.3 远程通道的校准与偏移处理远程测温的原理是利用二极管在不同电流下的正向压降差ΔVBE (kT/q)·ln(N)其中 N 是两个激励电流的比值。这个公式的好处是把二极管的绝对压降消掉了所以探头一致性很好但二极管理想因子和线阻仍然会带来偏差所以远程通道多少要做一点校准。我的校准方法是两点线性插值。先在 25°C 恒温环境读远程通道记录偏差 delta1再在 60°C 恒温环境读一次记录偏差 delta2。固件里保存这两个点实际读数出来后做线性补偿float calibrate_remote(float raw_temp) { // T125, T260, offset1/offset2 由实测得到 float slope (offset2 - offset1) / (T2 - T1); return raw_temp (raw_temp - T1) * slope offset1; }本地通道通常做一个固定偏移就够了因为它的偏差主要是封装和贴片应力引起的线性度本身不错。远程通道如果现场线长是固定的两点校准之后偏差可以压到 ±1°C 以内对 HVAC 控制来说足够了。3.4 低功耗调度1 秒周期上报平均电流怎么算系统大部分时间应该待在 stop 模式用 LPTIM 或 RTC 产生 1 秒唤醒。唤醒后做三件事读两个通道、校准换算、写 UART 或 RS485 上报然后回到 stop。PJ85718DM 如果有关断或单次转换模式读完立即切过去它能省下的电流可能比 MCU stop 电流还大。平均电流可以这样估算假设每次唤醒后运行 5 ms运行态整链路电流约 1.2 mA休眠 995 msMCU stop 加传感器关断合计约 1.2 µA。那么一秒钟的电荷消耗大约是 1200 µA × 0.005 s 1.2 µA × 0.995 s ≈ 7.2 µA·s平均电流就是 7 µA 左右。这个量级对电池供电的嵌入式监测设备很友好。如果你想把平均电流再压下去把上报周期从 1 秒拉到 5 秒或 10 秒平均电流能降到 2 µA 以下但 HVAC 控制回路一般需要 1 秒以内的采样率所以我在 HVAC 项目里坚持用 1 秒在电池监测项目里才用更长周期。4. 联调时踩过的几个坑死锁恢复、线阻误差与数据处理4.1 总线死锁传感器把 SDA 拉低不放手第一次遇到这个现象时我以为上拉电阻焊坏了拆了又焊焊了又拆折腾了快一天。现象是这样的程序跑一段时间后I2C 总线的 SDA 一直为低MCU 复位也没用必须整板断电才能恢复。这类温度传感器带 SMBus 风格的总线协议如果主机在错误时序下多发了时钟或者从机以为主机还在读数据它就可能一直把 SDA 拉低形成死锁。恢复方法其实很简单用 GPIO 模拟 SCL 连续翻转 9 个时钟让从机释放 SDA然后再发一个 STOP。代码上就是在 I2C 引脚切换成普通 GPIO 后手动拉高拉低 SCL。更重要的还是预防。每个 I2C 事务都加超时判断不要无限等 HAL 回调读数据时先写寄存器指针再读确保持久事务之间的 STOP/RESTART 行为符合手册要求。另外如果 ALERT 引脚被误配成了比较器模式且阈值不合理它也可能一直把某个引脚拉低让你误以为是总线问题。4.2 远程线缆线阻与滤波电容造成的读数偏差现场反馈远程探头读数比实际高 1 到 2°C实验室里怎么都复现不了后来发现是 3 米双绞线的线阻在作怪。远程二极管测温会把线阻上的压降也计入 ΔVBE线越长、线越细正偏差越大。解决办法有两个方向一是现场条件允许就换粗一点的屏蔽双绞线并确认 D/D- 的压降在手册允许范围内二是用传感器自带的线阻补偿或者 offset 寄存器把这个系统性偏差消掉。但要提醒一句补偿值是对固定线长有效的如果现场线长不固定最好在安装说明里规定最长线缆规格让每个项目的补偿条件一致。另一个坑是滤波电容。为了抗干扰我在 D/D- 之间并了 100 nF结果读数开始漂。原因是激励电流切换后电容充放电会干扰 ADC 的采样时机。这类远程通道的抗干扰应该靠芯片内部滤波和双绞线本身外部并电容要非常克制我最后把容值降到 1 nF 以内才稳定。4.3 符号位与分辨率负数温度别读成 4046°C有次凌晨收到现场消息说温度显示 4046°C我第一反应是探头短路了。排查到最后原因很简单某次读到原始值 0xFCE0按 uint16 计算是 64736乘 0.0625 就是 4046°C而正确的处理是先转成 int160xFCE0 对应的有符号值是 -800再乘 0.0625 才是 -50°C。这个问题在 0°C 以下的制冷场景很容易踩。不同器件的寄存器格式差别很大有的是 12 位左对齐有的是右对齐有的带符号扩展位。我建议把字节转温度的代码集中在一个函数里把所有位数、符号、缩放逻辑都写清楚业务层只认 float 温度值不要在多个地方各自移位。如果你用 Modbus 上报还要注意协议层的符号约定。常见做法是温度乘以 10 后以 int16 上报那么 -5.0°C 就是 -50接收端再除以 10。这一步看起来简单但发送端和接收端只要有一边忘了缩放现场调试就会多耗半天。4.4 报警脚、中断风暴与地址冲突ALERT 脚如果不打算用硬件中断最安全的做法是在初始化时把芯片配置成中断模式并让主机轮询状态寄存器来清标志或者干脆关闭报警功能只靠软件阈值。如果误用比较器模式阈值设置不合理ALERT 会一直拉低和总线死锁症状一模一样很容易误导排查方向。多传感器场景下地址冲突是原理图阶段就该解决的问题。上电初始化时先扫描一遍 I2C 地址确认每个地址都能读到预期器件 ID再进入正常运行读不到就报故障而不是用默认温度继续跑。HVAC 里如果温度信号丢失还按正常温度控制风机和阀门就会按错误逻辑动作后果比报警本身严重得多。5. 实测数据与落地部署从实验室到 HVAC 现场5.1 实验室验证恒温箱与铂电阻参考固件写得差不多之后我做了三轮验证。方法不复杂把整个测试板放进恒温箱远程探头通过 2 米 24AWG 双绞线引出箱外再放回箱内模拟现场走线稳定 30 分钟后同时读本地通道、远程通道和铂电阻参考值。数据大概是这样恒温箱设定铂电阻参考本地通道远程通道25.0°C25.03°C25.01°C25.05°C45.0°C44.96°C45.02°C44.91°C70.0°C69.94°C69.98°C70.11°C本地通道最大偏差 0.07°C远程通道经过两点校准后最大偏差约 0.15°C这个精度对 HVAC 和嵌入式保护用途都足够。需要说明的是这只是我自己测试板的个别数据不能代表芯片全温区指标量产项目还是要按批量抽检方式做统计。5.2 HVAC 场景控制柜加管道探头现场环境比实验室残酷得多。某项目的控制柜装在机房角落风管探头在 3 米外中间还过了两个弯。柜内本地温度实测 38°C风管里的远程温度只有 24.5°C。如果把控制逻辑只看本地温度系统会一直以为环境过热而加大制冷实际风管侧早就达标了加入远程通道作为控制变量后阀门的动作才合理。安装层面的心得是探头不能贴在风管保温层外面那测的是环境温度不是管道温度D/D- 极性装机前必须确认屏蔽层接柜内大地不接传感器地探头线不要和 220V 动力线走同一个线槽。这些都是现场没人会替你保证的细节写进安装手册最靠谱。5.3 嵌入式场景电机驱动器与电池管理监测另一个项目是电机驱动器功率模块内部有测温二极管用同一颗 PJ85718DM 的远程通道去读。实测远程结温 87.5°C 时本地通道看到的 PCB 空气温度只有 52°C。这个温差很正常但也说明了一个问题如果你的保护逻辑用的是板载温度实际功率结温可能已经超限了。这类场景里D/D- 走线尽量短在 PCB 上走差分形式远离 PWM 输出和功率走线。我做过一次调整把远程通道的两根线从功率管旁边拉开 2 mm读数稳定度立刻改善。电池管理子卡上也类似远程通道贴在电芯表面用来做充放电温度保护读取周期可以放到 5 秒平均电流非常低。5.4 数据送出去串口、Modbus 与本地记录温度读出来总要送出去才有意义。HVAC 项目里我把 STM32L021K4 的 USART 接了一颗 RS485 收发器跑 Modbus RTU 上报本地温度和远程温度分别映射到两个保持寄存器单位是 0.1°C。代码里只有一行关键转换uint16_t reg_local (uint16_t)(int16_t)(local_temp * 10.0f); uint16_t reg_remote (uint16_t)(int16_t)(remote_temp * 10.0f);嵌入式场景更简单直接 UART 输出文本帧或者二进制帧给主控。如果主控资源紧张可以在 STM32L021K4 的 Flash 里做一个环形缓冲保存最近 100 条记录报警前后各留 20 条方便事后分析。这套固件状态机很小16 KB Flash 完全装得下我最后还塞了一个简单的自检逻辑上电时确认寄存器能读写才进入主循环。最后聊一个我自己的习惯每次拿到新批次 PJ85718DM我会先做一次 25°C 和 60°C 的两通道复测把偏差写进固件里的偏移表而不是依赖生产环节做满板标定。这样批量焊接后的分布很稳定现场出问题的概率小很多。这套架构真正省心的地方在于传感器把模拟前端处理掉了MCU 只做数字搬运和时机调度剩下的就是对阈值和滤波逻辑的持续打磨了。祝一次过。