ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

低功耗MCU加远程温度传感器:嵌入式与HVAC两路测温的完整设计

低功耗MCU加远程温度传感器:嵌入式与HVAC两路测温的完整设计

过去一年多,我在三个产品上做过同一件事:用一颗低功耗 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 地址。按这类器件的常见映射,默认地址可能是 0x48,ADDR 接 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 反而省心。先初始化 GPIO:SCL 用 PB6,SDA 用 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) { // T1=25, T2=60, 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 ms,MCU 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;而正确的处理是先转成 int16,0xFCE0 对应的有符号值是 -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°C
45.0°C44.96°C45.02°C44.91°C
70.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 只做数字搬运和时机调度,剩下的就是对阈值和滤波逻辑的持续打磨了。祝一次过。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进