1. 项目缘起与整体设计思路
温度监测这件事,听起来像是电子工程里最基础的入门课题,但真正落到工业级或楼宇级的嵌入式场景里,坑远比想象中多。我这次做的项目,核心就是用一颗 PJ85718DM 温度传感芯片配合 STM32L081CB 微控制器,搭建一套能同时覆盖本地温度采集和远程温度监控的完整方案,目标场景是嵌入式设备内部热管理和 HVAC(暖通空调)系统的温度巡检。
先说为什么选这两个核心器件。STM32L081CB 属于 STM32L0 系列的超低功耗 MCU,Cortex-M0+ 内核,主频 32MHz,带 128KB Flash 和 16KB RAM,封装通常是 LQFP48。它的定位很明确:电池供电或长期在线但功耗敏感的场合。HVAC 场景里,温度节点往往分布在管道、风道、回风口这些位置,很多节点没有稳定的市电供应,靠电池或者能量采集供电,所以 MCU 的待机功耗直接决定了维护周期。STM32L0 在 Stop 模式下能做到亚微安级,RTC 加低功耗定时器唤醒的架构非常适合周期性温度巡检。
PJ85718DM 这颗温度传感器,是我在对比了几款常见数字温度器件之后定下来的。它是一颗本地温度传感器,支持 I2C 或 SMBus 接口,测温范围覆盖 -40°C 到 +125°C,典型精度在常温区间能做到 ±0.5°C,分辨率可配置到 0.0625°C。相比一些只给 9 位或 12 位固定分辨率的器件,它的可配置分辨率在需要精细温控的 HVAC 场景里更灵活。另外它支持多地址配置,同一条 I2C 总线上可以挂多颗,这对多点温度巡检非常关键。
整个系统的设计思路分三层:感知层、控制层、通信层。感知层就是 PJ85718DM 负责本地温度采集,控制层是 STM32L081CB 负责数据读取、处理和逻辑判断,通信层负责把温度数据传到远程监控端。本地监测指的是设备自身或就近节点的温度实时读取和阈值报警,远程监测则是通过通信接口把数据汇总到上位机或云端。
为什么要把本地和远程分开设计?因为实际运维中,本地响应要求的是实时性和可靠性,比如设备过热要立刻触发保护;远程监测要求的是数据完整性和趋势分析,允许有秒级延迟。两者对采样频率、通信策略、功耗预算的要求完全不同,混在一起做容易顾此失彼。所以我在架构上就把这两条链路拆开,本地走中断加阈值比较,远程走周期性上报加缓存补传。
这个方案适合谁参考?如果你在做嵌入式设备的热管理、楼宇自控的温度节点、或者任何需要低功耗多点温度采集的项目,这套组合可以直接抄作业。即使你用的是别的 MCU 或传感器,里面的采样策略、I2C 时序处理、低功耗调度思路也是通用的。
2. 核心器件细节与硬件设计要点
2.1 PJ85718DM 的关键参数与配置逻辑
PJ85718DM 的寄存器结构不算复杂,但有几个配置项如果设错,测温结果会差很多。它的核心寄存器包括温度结果寄存器、配置寄存器、阈值寄存器和迟滞寄存器。温度结果寄存器是只读的,16 位,高 12 位有效,低 4 位根据分辨率设置决定是否有效。
分辨率配置是第一个要搞清楚的点。器件支持 9 到 12 位分辨率,对应 0.5°C、0.25°C、0.125°C、0.0625°C 的步进。分辨率越高,转换时间越长。9 位时转换时间典型 30ms,12 位时要 120ms 左右。在 HVAC 场景里,温度变化本身很慢,没必要每次都跑 12 位。我的做法是默认用 11 位(0.125°C),在需要精确标定的环节临时切到 12 位。这样在精度和功耗之间取了个平衡。
阈值和迟滞寄存器是本地报警的核心。PJ85718DM 支持独立的过温阈值和欠温阈值,还有一个迟滞值。举个例子,如果你设过温阈值 60°C,迟滞 2°C,那么温度超过 60°C 时报警引脚拉低,直到温度降到 58°C 以下才恢复。这个迟滞机制能有效防止温度在阈值附近波动时报警引脚反复抖动,这在风机控制场景里特别重要,否则继电器会频繁吸合释放,寿命大打折扣。
I2C 地址方面,PJ85718DM 通过地址引脚可以配置多个地址。具体地址组合要看数据手册的地址表,通常能支持 4 到 8 个不同地址。我在一个 HVAC 节点上挂了 3 颗,分别测回风、送风和盘管表面温度,地址通过引脚拉高拉低区分,软件里用不同的设备地址轮询。
2.2 STM32L081CB 的外设分配与低功耗设计
STM32L081CB 的外设资源在这个项目里用得比较满。I2C1 用来接 PJ85718DM,配置成标准模式 100kHz 或快速模式 400kHz。这里有个细节:如果总线走线较长,比如超过 20cm,建议降到 100kHz 并加上拉电阻调整。我实际用 4.7kΩ 上拉,400kHz 下波形还算干净,但如果你的板子布线不理想,4.7kΩ 可能偏大,可以降到 2.2kΩ 试试。
USART 用来做远程通信,接 RS485 收发器或者无线模块。我这边远程链路用的是 RS485 半双工,配合 Modbus RTU 协议,这是楼宇自控里最通用的组合。USART 配置成 9600 或 19200 波特率,8 数据位、1 停止位、无校验。为什么不用更高波特率?因为 RS485 在长距离和电磁干扰环境下,低波特率更稳。9600 在 1200 米以内基本没问题,19200 在 500 米以内可靠。
定时器方面,我用 TIM2 做周期采样触发,TIM3 做通信超时检测。RTC 用来做时间戳,每条温度记录都带上时间,方便远程端做趋势分析。低功耗调度上,系统大部分时间处于 Stop 模式,RTC 闹钟或 TIM2 中断唤醒。唤醒后依次做:读温度、判断阈值、存缓存、决定是否上报,然后继续睡。
这里有个功耗计算的实际例子。STM32L081CB 在 Stop 模式下典型电流约 0.4μA(RTC 开启),运行时约 2mA(32MHz 全速)。假设每 10 秒唤醒一次,每次活跃 50ms,那么平均电流大约是 0.4μA + (2mA × 50ms / 10s) = 0.4μA + 10μA = 10.4μA。用一颗 2000mAh 的锂亚电池,理论续航约 2000mAh / 0.0104mA ≈ 192307 小时,约 22 年。当然实际受电池自放电、传感器功耗、通信模块功耗影响,但即便打对折,10 年维护周期也是可期的。
2.3 硬件布局与抗干扰注意事项
PCB 布局上,PJ85718DM 要尽量远离发热源,比如 LDO、功率 MOS、继电器。如果实在避不开,至少保证传感器和热源之间有足够的铜皮隔离或开槽。我见过一个案例,传感器离 LDO 只有 8mm,结果测出来的温度比实际环境高了 3°C 多,后来把传感器挪到板边才解决。
I2C 走线要等长、尽量短,远离高频信号线。如果和 RS485 走同一块板,两者要垂直交叉而不是平行走线,减少耦合。RS485 的 A/B 线要加 TVS 管做浪涌保护,终端加 120Ω 匹配电阻。这些在 HVAC 现场是保命措施,雷击浪涌和静电放电是常态。
电源去耦方面,PJ85718DM 的 VDD 引脚旁边放 0.1μF 陶瓷电容,STM32L081CB 每个电源引脚都要有 0.1μF,另外在电源入口放一个 10μF 钽电容或电解电容做储能。如果系统里有继电器或电机,建议传感器和 MCU 的电源单独走一路 LDO,和功率部分隔开。
3. 软件架构与核心代码实现
3.1 I2C 驱动层与传感器读写封装
软件上我分了三层:底层驱动、中间件、应用逻辑。底层驱动就是 I2C 读写和 PJ85718DM 的寄存器操作。STM32L081CB 的 HAL 库提供了 I2C 接口,但 HAL 的阻塞式读写在某些时序要求下不够灵活,所以我用 LL 库重写了 I2C 的底层时序,保留 HAL 做初始化。
PJ85718DM 的读温度流程是这样的:先发设备地址加写位,发温度结果寄存器地址,然后 restart,发设备地址加读位,读两个字节。第一个字节是高 8 位,第二个字节是低 8 位。组合成 16 位后右移 4 位(如果配置成 12 位分辨率),再乘以 0.0625 得到摄氏度。
#define PJ85718DM_ADDR 0x48 << 1 #define TEMP_REG 0x00 #define CONFIG_REG 0x01 float PJ85718DM_ReadTemp(void) { uint8_t buf[2]; int16_t raw; float temp; I2C_Start(); I2C_SendByte(PJ85718DM_ADDR | 0); I2C_WaitAck(); I2C_SendByte(TEMP_REG); I2C_WaitAck(); I2C_Start(); I2C_SendByte(PJ85718DM_ADDR | 1); I2C_WaitAck(); buf[0] = I2C_ReadByte(); I2C_SendAck(1); buf[1] = I2C_ReadByte(); I2C_SendAck(0); I2C_Stop(); raw = (buf[0] << 8) | buf[1]; raw >>= 4; temp = raw * 0.0625f; return temp; }这段代码是简化版,实际用的时候要加超时和错误重试。I2C 总线在工业环境里容易被干扰,一次读失败很正常,我一般重试 3 次,3 次都失败才上报通信故障。
配置寄存器的写入要注意,PJ85718DM 的配置寄存器有些位是保留位,写的时候要读-改-写,不能直接覆盖。比如设置分辨率只改对应的两位,其他位保持原值。这个坑我踩过,直接写导致报警极性反了,排查了半天。
3.2 本地温度监测与阈值报警逻辑
本地监测的核心是快速响应。我的做法是配置 PJ85718DM 的报警引脚,把它接到 STM32L081CB 的外部中断引脚。当温度超过阈值时,传感器硬件直接拉低报警引脚,MCU 进中断,立刻执行保护动作,比如关断加热器、启动风扇、或者拉响蜂鸣器。这条链路不经过软件轮询,响应时间在毫秒级。
但硬件报警有个问题:它只告诉你超温了,不告诉你具体温度值。所以中断里我还要快速读一次温度寄存器,拿到当前温度,再决定动作级别。比如超过阈值 5°C 以内只开风扇,超过 10°C 就切断负载并上报故障。
阈值设置上,我建议不要设得太贴近正常工作温度。比如设备正常工作在 45°C,过温阈值设 60°C 比较合理,留 15°C 余量。迟滞设 2 到 5°C,看系统热惯性。热惯性大的系统,迟滞可以设大一点,避免频繁动作。
软件轮询作为硬件报警的补充,每 10 秒读一次温度,记录到缓存。缓存用环形缓冲区,深度 64 条,存满后覆盖最旧的数据。这样即使远程通信中断,本地也有最近一段时间的温度记录,恢复后可以补传。
3.3 远程温度上报与通信协议设计
远程上报我用的 Modbus RTU,因为它在楼宇自控里兼容性最好,上位机、PLC、组态软件基本都支持。STM32L081CB 做 Modbus 从站,上位机做主站轮询。从站地址、波特率、数据格式都可以通过拨码开关或 Flash 配置。
Modbus 寄存器映射我这样设计:保持寄存器 0x0000 到 0x0002 存三路温度,单位 0.1°C,用有符号 16 位表示。比如 253 表示 25.3°C,-105 表示 -10.5°C。保持寄存器 0x0010 存状态字,每一位对应一个报警标志。输入寄存器 0x0000 到 0x0002 存原始 ADC 值,方便调试。
为什么用 0.1°C 而不是 0.01°C?因为 Modbus 寄存器是 16 位的,0.01°C 精度下,32767 只能表示 327.67°C,范围够但没必要。HVAC 场景 0.1°C 分辨率完全够用,而且数据传输量小,解析也简单。
上报策略上,我做了自适应。温度正常时每 60 秒上报一次,温度接近阈值时每 10 秒上报一次,报警时立刻上报并持续上报直到恢复正常。这样既保证了正常时的低功耗和低带宽占用,又保证了异常时的实时性。
通信超时处理也很关键。如果上位机超过 3 个轮询周期没有来读,从站就认为通信中断,把未上报的数据存到 Flash 缓存区。Flash 写次数有限,所以不能每条都写,我的做法是缓存满 32 条或者检测到通信中断时才写一次 Flash。STM32L081CB 的 Flash 擦写寿命约 10 万次,按每天写 10 次算,能用 27 年,足够了。
4. 实操过程与关键环节记录
4.1 硬件搭建与初步调试
第一步是焊接核心板。我用的是 STM32L081CB 的最小系统板加 PJ85718DM 的 breakout 板,通过杜邦线连接。正式产品当然要画 PCB,但调试阶段这样最快。I2C 的 SCL、SDA 各接 4.7kΩ 上拉到 3.3V,报警引脚接 PA0 做外部中断。
上电后第一件事是扫描 I2C 总线,确认传感器地址能被识别。我写了一个简单的扫描程序,从 0x08 到 0x77 逐个发地址,看哪个有 ACK。PJ85718DM 配置成默认地址时,扫描结果应该是 0x48(7 位地址)。如果扫不到,先查供电和上拉,再查地址引脚配置。
扫到地址后,读配置寄存器的默认值。PJ85718DM 上电默认是 9 位分辨率、比较器模式、报警极性低有效。我一般先改成 11 位分辨率、中断模式、报警极性低有效,然后写阈值和迟滞。写完后回读一遍,确认写入成功。
温度读取的初步验证,我用手捏住传感器看温度上升,松开看下降。常温下读数应该在 25 到 30°C 之间(取决于环境)。如果读数明显偏高或偏低,先检查分辨率配置和移位操作。我遇到过读出来是实际值 16 倍的情况,就是移位搞错了。
4.2 低功耗模式配置与实测
低功耗调试是这个项目的重头戏。STM32L081CB 进 Stop 模式前,要把所有不用的外设时钟关掉,GPIO 配置成模拟输入或保持状态,避免浮空引脚漏电。I2C 引脚要配置成开漏输出加外部上拉,或者复用功能,不能配成推挽输出,否则总线会被拉死。
RTC 闹钟唤醒的配置:先初始化 RTC,设置闹钟时间,使能闹钟中断,然后在主循环里调用HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI)。唤醒后系统时钟会切回 HSI,需要重新配置系统时钟到 HSI 或 HSE。这个坑很常见,很多人唤醒后发现串口波特率不对,就是时钟没恢复。
实测功耗时,我用的是高精度电流表串联在电源回路里。Stop 模式下读数约 0.5μA,和手册标称的 0.4μA 接近,差异来自板上的 LDO 静态电流和上拉电阻漏电。运行时峰值约 2.5mA,平均下来 10 秒周期约 11μA。用 2000mAh 电池实测,连续跑了 3 个月,电压从 3.6V 降到 3.5V,推算续航确实在 10 年以上。
这里有个经验:如果发现 Stop 模式电流远高于预期,先查 GPIO 状态。浮空的输入引脚会因为电平不定导致输入级振荡漏电。把所有未用引脚配成模拟输入或输出低,能省不少电。另外,调试器的 SWD 引脚在低功耗时也会漏电,正式测功耗要把调试器拔掉。
4.3 远程通信联调与数据验证
RS485 联调我先用 USB 转 485 模块接电脑,用 Modbus 调试助手做主机。从站地址设 1,波特率 9600,功能码 03 读保持寄存器。发01 03 00 00 00 03 05 CB,应该返回01 03 06加 6 字节数据加 CRC。
第一次联调时遇到返回数据 CRC 错误,查了半天发现是 RS485 收发器的使能引脚控制时序不对。发送时 DE 拉高,发送完要等最后一个字节移出移位寄存器才能拉低,否则会截断最后一个字节。STM32 的 USART 有 TC(传输完成)标志,用 TC 中断来拉低 DE 最可靠,不能用 TXE。
数据验证方面,我拿标准温度计做对比,把传感器和温度计放同一个恒温环境里,等 10 分钟热平衡后读数。实测偏差在 ±0.3°C 以内,满足 HVAC 的一般要求。如果要更高精度,可以做单点或多点校准,把偏差写到 Flash 里做补偿。
Modbus 轮询周期我设的是 1 秒,实际温度变化慢,1 秒足够。如果节点多,可以拉长到 2 到 5 秒。轮询太快没必要,还增加通信冲突概率。RS485 是半双工,同一时刻只能一个主站,轮询间隔要留够从站响应时间。
5. 常见问题与排查技巧实录
5.1 温度读数异常问题速查
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 读数恒为 0 或满量程 | I2C 通信失败,读到默认值 | 用示波器看 SCL/SDA 波形 | 检查上拉电阻、地址配置、供电 |
| 读数偏高 3-5°C | 传感器受板载热源影响 | 红外测温枪对比板面温度 | 挪远传感器或加隔离槽 |
| 读数跳动大 | 电源噪声或地线干扰 | 示波器看 VDD 纹波 | 加去耦电容、单点接地 |
| 读数偏差固定值 | 分辨率配置或移位错误 | 核对数据手册移位位数 | 修正移位和乘法系数 |
| 报警引脚不动作 | 阈值寄存器未写入或极性反 | 回读阈值寄存器 | 读-改-写配置寄存器 |
这个表是我实际调试中总结的,基本覆盖了 90% 的温度读数问题。其中读数偏高是最隐蔽的,因为通信完全正常,数据也稳定,就是值不对。后来用热成像仪一看,传感器正上方就是 LDO,热量直接烤上去了。
5.2 低功耗模式下的典型故障
低功耗模式最容易出的问题是唤醒后外设不工作。常见原因有三个:时钟没恢复、外设时钟没重新使能、GPIO 状态没恢复。我的做法是在唤醒后统一调用一个SystemClock_ReInit()函数,重新配置时钟树,然后重新初始化用到的外设。
另一个问题是 RTC 闹钟不触发。检查 RTC 时钟源是否配置正确,LSE 还是 LSI。LSE 精度高但起振慢,LSI 起振快但精度差。HVAC 场景对时间精度要求不高,我用 LSI,省一个晶振。但 LSI 频率随温度变化,闹钟周期会有偏差,10 秒可能变成 9.5 到 10.5 秒,这个可以接受。
还有一次遇到 Stop 模式进不去,查出来是串口没关。USART 在 Stop 模式下如果时钟还开着,会阻止进入低功耗。进 Stop 前要把 USART 时钟关掉,或者用HAL_UART_DeInit()反初始化。这个在 STM32L0 系列里特别要注意,L0 的低功耗管理比较严格。
5.3 通信干扰与数据丢包处理
RS485 在工业现场受干扰是常态。我遇到过电机启动时通信中断,后来在 RS485 总线上加了共模电感和 TVS,问题明显改善。软件上也做了防护:每帧数据校验 CRC,CRC 错就丢弃,不回复。主站超时重试 3 次,3 次都失败就标记该节点离线。
数据丢包方面,从站缓存最多存 64 条记录,如果通信中断时间太长,缓存满了会覆盖最旧数据。我的建议是根据上报周期和缓存深度算一下最大中断容忍时间。比如 60 秒上报一次,64 条缓存能存 64 分钟。如果现场网络可能中断几小时,就要加大缓存或者用外部 Flash。
还有一个细节:Modbus 从站响应时间。STM32L081CB 在 32MHz 下处理一帧 Modbus 请求大约 1 到 2ms,加上 RS485 收发器切换时间,总响应在 5ms 以内。主站超时设 100ms 足够。如果从站响应慢,先查是不是在低功耗唤醒过程中,唤醒加初始化可能要 10ms 以上,主站超时要相应放宽。
6. 实际部署中的经验与扩展思路
这套方案我在几个不同的 HVAC 节点上部署过,有新风机组、有风机盘管、也有管道温度巡检。实际跑下来,最深的体会是:硬件设计决定下限,软件策略决定上限。传感器选得再好,布局不对照样测不准;MCU 功耗再低,调度策略不合理照样费电。
扩展方面,这套架构很容易加节点。I2C 总线上再挂 PJ85718DM,改一下地址和轮询逻辑就行。远程通信如果从 RS485 换成无线,只需要替换通信层,应用层和驱动层基本不用动。我后来做过一个版本,把 RS485 换成 LoRa 模块,Modbus 协议保留,只是物理层变了,软件改动不到 20%。
另一个扩展方向是加湿度监测。HVAC 里温湿度通常一起测,PJ85718DM 只管温度,湿度可以再加一颗湿度传感器,挂同一条 I2C 总线。数据打包上报时,把湿度和温度放同一个 Modbus 寄存器块里,上位机解析也方便。
最后分享一个标定小技巧。如果对精度要求高,不要只做单点校准。至少在 0°C、25°C、50°C 三个点做校准,用最小二乘法拟合出增益和偏移。PJ85718DM 的线性度很好,三点校准后全量程偏差能压到 ±0.2°C 以内。校准数据存 Flash,上电时加载做补偿。这个在批量生产时特别有用,因为每颗传感器的个体差异可以通过校准消除。