☰
嵌入式温度监测实战:STM32与双通道数字温度传感器的本地远程测温方案
2026/10/22 16:58:22 网站建设 项目流程

温度监测这事儿,看起来简单,做进去才知水有多深。最近一直在调一个嵌入式 + HVAC 结合的监测项目:STM32F429ZI 做主控,一颗 PJ85718DM 同时负责本板温度和远端的探头温度。本来以为读个寄存器就完事,结果从 I2C 地址冲突、远程二极管走线干扰,到 HVAC 现场变频器把读数带飞,每一样都是坑。

这篇文章就把我整个调通这套“本地 + 远程”温度监测系统的过程写透。从硬件接线、寄存器配置、温度换算,到暖通现场怎么布点、怎么防干扰、怎么排查故障,全部展开。适合做智能温控器、空调箱控制器、冷链监测设备、或者嵌入式板卡测温的朋友参考。标题里的“本地与远程”有两层意思:第一层是指一颗芯片上有本地通道和远程通道,本地测板卡温度、远程接探针测风管或水管;第二层是指多台设备通过总线把温度数据汇聚到监控中心。下面先从第一层打通,再延展到第二层。

1. 项目思路:为什么这样组合

1.1 一个板卡温度和一个远端探头,用一颗芯片解决

嵌入式设备第一个要防的是自己先“发烧”。F4 主控跑 180MHz,旁边再放功率器件,板子局部温度经常比环境高十几度,单靠估算是猜不准的,所以本地测温通道必须实时读。而 HVAC 场景更刁钻:要测风管深处的送风温度、回风温度,测冷冻水管表面温度,或者测房间远端空气温度,这些位置都不可能把一整块控制板塞过去,只能把探头拉线接出去。

所以项目选型时我盯上了 PJ85718DM 这类“双通道数字温度传感器”。它自带一路本地测温,还有一路远程测温接口,可以直接接外部二极管或者热敏电阻做远端探针。这意味着控制板上放芯片,探头线拉到几米外的风管里,本地和远程温度都由同一颗芯片统一转换、统一上报,省掉一路 ADC 和一堆调理电路。

嵌入式这一侧的职责就是:初始化 I2C、周期读寄存器、做换算校准,然后通过串口或者以太网把数据交给上层。STM32F429ZI 干这个活非常稳,它主频高、存储大,F4 系列里常见接口几乎全带:4 个 USART、3 个 I2C、SPI、SDIO、以太网 MAC,LQFP144 封装也不难焊。用 HAL 库两三下就能把工程拉起来,没必要拿更低端的芯片强行抠资源。

1.2 性能指标和选型里的取舍

选 PJ85718DM,我主要看中这几条:工作电压 3.3V,和 F4 的 IO 电平直接兼容;I2C/SMBus 接口,一条总线最多挂好几颗,后期想扩展节点也方便;本地测温范围 -55°C 到 +125°C,远程通道经过合理设计可以测 -40°C 到 +150°C,覆盖 HVAC 常见工况没问题。分辨率默认 12 位、LSB 是 0.0625°C,本地精度能做到 ±0.5°C 左右,远程通道经过校准后也可以压到 ±1°C 以内。

当然 F4 的资源对这个项目来说确实有点“过剩”,别看标题写的是测温度,真正复杂的是上层协议和现场联网,F429ZI 的内部 2MB Flash、256KB RAM 跑 Modbus 协议栈、跑 RTOS、跑 LCD 界面都绰绰有余。我当时选它还有一个原因:项目后期可能要做本地显示和以太网网关,别的不说,F4 内置的 TFT-LCD 控制器和 MAC 都能用上,不用换平台。

2. 硬件搭建:从引脚到第一条可读温度

2.1 关键引脚怎么接

以 STM32F429ZI 的一块最小系统板为例,I2C1 的引脚最常用的是 PB6(SCL)、PB7(SDA)。PJ85718DM 的 SDA、SCL、VDD、GND 直接对应接上,电源用 3.3V,SDA、SCL 各加一个上拉电阻到 3.3V。传感器 ALERT# 引脚是可选的,我习惯接一个 GPIO 到 PA0,配置成外部中断,这样温度越限可以打断主循环处理。

芯片上如果有 ADDR0、ADDR1 地址选择引脚,全部接地就是默认地址 0x48。如果想一条总线上挂多颗传感器,就可以把这两根线组合成不同电平,比如接地或者接 VDD,常见配置能分出 8 个地址。总线上的 EEPROM、RTC 如果地址也是 0x48,就得赶紧把器件地址错开,这个坑我踩过,后面排查部分细说。

远程探头要接在远程通道的两个引脚上。如果用的是测温二极管,阳极接 REM+,阴极接 REM-,极性反了读数会直接变异。为了抗干扰,在 REM+ 和 REM- 之间可以放一个 10nF 左右的滤波电容,位置越靠近芯片端子越好。

2.2 上拉电阻的选择不是随便放的

I2C 是开漏结构,必须靠上拉电阻把电平拉高。电阻太大会导致上升沿过慢,高速通信时波形不过关;电阻太小功耗上升,电平可能拉不下来。400kHz 模式下,我常用 2.2kΩ 到 4.7kΩ,具体可以用上升时间公式粗略验算:tr = 0.8473 × R × C。假设一条线上总线电容约 200pF,用 4.7kΩ 上拉,tr 约 0.8µs,已经不太够用了;换成 2.2kΩ 大概 0.37µs,还算安全。如果总线上还挂了液晶、EEPROM 和电平转换芯片,电容更大,上拉电阻就往 1kΩ 靠近。

有一点必须注意:I2C 引脚绝不能配置成推挽输出。有同事图省事直接把 SDA 当普通 GPIO 推挽操作,结果总线直接卡死,还烧过芯片。开漏就是开漏,这个习惯要养成。

2.3 最小系统验证顺序

硬件焊好先别急着写应用代码。我一般先用 I2C 扫描工具确认总线通不通,比如 Linux 下用 i2c-tools:

i2cdetect -y 1

如果总线上只有一个传感器,应该能看到 0x48 出现。用 STM32 裸机工程的话,第一步只做一件事:读器件 ID 寄存器,打印出来。器件 ID 能读到,说明电源、复位、I2C 链路、地址配置全对了,再往下写读取逻辑才有意义。逻辑分析仪这时候也很有用,可以看 START、STOP、地址 ACK/NACK 波形,一眼就能分辨是传感器没响应还是主控侧配置错误。

3. 驱动代码:本地温度的读取与换算

3.1 寄存器映射和常用优先级

不同厂家芯片寄存器细节有差异,我这里给的是调试 PJ85718DM 时实际用到的映射,供参考。核心寄存器就这几个:

寄存器地址名称说明
0x00本地温度寄存器高 8 位 + 低 8 位,4 位小数
0x01远程温度寄存器高 8 位 + 低 8 位,4 位小数
0x02配置寄存器单次/连续模式、分辨率、报警使能
0x03~0x06高/低告警阈值本地/远程上限、下限
0x0F器件 ID固定值,用于自检

读取流程其实就是“写指针寄存器,再连续读两个字节”。我用的例程是这样的:

uint8_t reg = 0x00; uint8_t buf[2]; HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)(0x48 << 1), &reg, 1, 100); HAL_I2C_Master_Receive(&hi2c1, (uint16_t)(0x48 << 1), buf, 2, 100); uint16_t raw = (buf[0] << 8) | buf[1]; raw >>= 4; // 低 4 位是小数位外的无关位 if (raw & 0x0800) { raw |= 0xF000; // 12 位有符号数做符号扩展 } int16_t signed_raw = (int16_t)raw; float temp_c = signed_raw * 0.0625f;

这里有个新手容易搞混的点:0x48是 7 位从机地址,HAL 函数需要的是 8 位地址,所以一定要左移一位变成0x90(写)和0x91(读),我代码里统一写0x48 << 1,避免手动拼错。

3.2 温度换算的几个典型数值

12 位分辨率下,温度寄存器的数据格式很直观,数值乘以 0.0625 就是摄氏度。举例来说:

寄存器原值右移后数值温度结果
0x000000°C
0x100025616°C
0x41B0105165.6875°C
0xFF00-256-16°C

刚开始调的时候,我经常拿一只手捏住传感器芯片,看温度读数是不是慢慢往上走,松手后逐步回落。这个过程能同时验证转换时间、软件轮询间隔和数据稳定性。如果读数纹丝不动或者跳变到 -50°C,先检查寄存器地址有没有搞错,再检查 SDA 线上有没有干扰。

3.3 单次转换与连续转换的模式选择

PJ85718DM 默认一般是连续转换模式,主控随时读都能拿到新值。但如果项目对功耗敏感,比如无线节点靠电池供电,可以切换到单次转换模式:写一次配置寄存器触发转换,等待转换完成(常见 12 位转换需要几十到一百多毫秒,具体看手册),再读结果,读完自动回到低功耗状态。

我实际项目里更倾向于连续模式,因为 HVAC 控制本身就要求周期性刷新温度,主循环每 500ms 或 1s 读一次就够了。转换太快反而对滤波没好处,数据的有效位数并不会因为读得勤变得更高。

4. 远程温度通道:原理、布线与校准

4.1 远程测温到底在测什么

远程通道最常用的做法是接一个测温二极管,原理说白了就是用 PN 结的正向压降随温度变化来测温。PJ85718DM 内部不输出模拟电压,而是用不同电流激励探头,测两档电流下的 Vbe 差,再换算成温度。这种“ΔVbe 法”的好处是对导线串联电阻不敏感,几十欧姆的线阻基本不影响读数,所以我敢把线拉到几米远的地方。

但别随便拿普通整流二极管就上,这种用法对二极管特性有要求。我常用 2N3904 这类小信号三极管的 BC 结作为测温探头,把 C 和 B 短接,C/B 当阳极、E 当阴极,管子用金属壳封装的更好,热响应快、热阻小。封装在塑料灌封胶里的普通二极管也能用,就是响应慢一点。这地方要注意极性,阳极接 REM+、阴极接 REM-,反接后读出的温度曲线会很明显跑偏。

4.2 远程线的布线和滤波规则

远程探头引线是整套系统里最容易受干扰的一段路。HVAC 现场走线经常和 220V 电缆、变频器输出线挨得很近,如果不处理,读出的温度会在 ±2°C 甚至更大范围内乱跳。我做这块时的经验是:探头线用双绞对线或者屏蔽对绞线,屏蔽层在控制板一侧单端接地,不要两端都接地,避免地环路电流在屏蔽层上形成压降。

在 REM+ 和 REM- 之间加一只 10nF~100nF 的电容,位置放在芯片引脚旁边,配合走线可以滤掉大部分共模干扰。如果探头线要过连接器,连接器尽量用镀金端子,氧化导致接触电阻变化会直接影响小信号测量稳定性。线长控制在 3 米以内比较稳,超过 3 米要重新评估滤波器参数和电缆类型。

4.3 远程通道的校准:不要只看单点调零

远程二极管本身的一致性很好,但系统误差还是来自探头安装方式、线缆材料、连接器接触和滤波电容。我的做法是至少取两个温度点做两点校准。比如实测环境 30°C,传感器读出来的远程温度是 29.4°C;再放到 80°C 的恒温环境,读出 79.6°C,那么校准增益是:

gain = (80 - 30) / (79.6 - 29.4) ≈ 1.008 offset = 30 - 29.4 * gain ≈ 0.36

驱动里留一组校准参数:

typedef struct { float gain; float offset; } pj_cal_t; static float pj_apply_cal(float raw_temp, const pj_cal_t *cal) { return raw_temp * cal->gain + cal->offset; }

校准参数一般通过配置接口写入非易失存储,现场调试时也可以在线改。注意,两点校准假设传感器线性度较好,实际上远程二极管的非线性很小,用线性补偿完全够。

4.4 远程通道的轮询与报警处理

读远程温度寄存器的流程和本地通道完全一样,只是寄存器地址从 0x00 换成 0x01。我一般把两路读数封装成同一个接口,主动轮询时一次性把两个通道都读出来,避免中途被别的任务打断导致数据不齐。

typedef enum { PJ_CH_LOCAL = 0x00, PJ_CH_REMOTE = 0x01 } pj_channel_t; int pj_read_temperature(pj_channel_t ch, float *temp_c) { uint8_t reg = (uint8_t)ch; uint8_t buf[2]; if (HAL_I2C_Master_Transmit(&hi2c1, 0x48 << 1, &reg, 1, 100) != HAL_OK) return -1; if (HAL_I2C_Master_Receive(&hi2c1, 0x48 << 1, buf, 2, 100) != HAL_OK) return -2; uint16_t raw = (buf[0] << 8) | buf[1] >> 4; *temp_c = (float)(int16_t)(raw | (raw & 0x0800 ? 0xF000 : 0)) * 0.0625f; return 0; }

ALERT# 引脚建议配合外部中断使用,触发后不要只读一次数据,因为寄存器锁存的原因,第一次读到的可能是触发前的缓存值,要先读配置寄存器或者状态寄存器清标志,再读一次温度,才能拿到实际越限时刻的数据。这个细节很容易被忽略,调试时注意一下能少耗很多时间。

5. HVAC 部署:多点布设与数据汇聚

5.1 暖通现场最典型的几个测温位置

一个空调箱控制系统里,温度测点通常包括:回风温度、送风温度、盘管出风温度、冷冻水供水/回水温度,还有控制柜内部板卡温度。控制板附近放 PJ85718DM 本地通道,测柜内温度是最自然的做法,而风管和水管上的测点就交给远程探头。

风管内的探头安装要顺着气流方向,探头尽量悬在气流中间而不是贴管壁,管壁温度会干扰真实风温。水管测温不一样,探针要贴在管壁外表面,用弹簧夹或金属抱箍固定,探头和管壁之间要抹导热硅脂,减少接触热阻。如果管内有保温层,最好是穿透保温层把探头压在金属管壁上,不然读数滞后非常严重。

5.2 从单板到多节点:RS485 + Modbus RTU

单板测完本地和远程温度,只是第一步。真实 HVAC 现场有很多台控制器、很多个测点,汇总到中控是刚需。我做的方案是每个现场控制单元用 STM32F429ZI 做数据采集,再通过 UART 外接 RS485 收发器组网。

Modbus RTU 是暖通行业最通用的协议。我一般用保持寄存器来映射温度数据:寄存器 40001 放本地温度,寄存器 40002 放远程温度,温度值放大 100 倍以整数形式传输,比如 25.43°C 就存 2543,这样中控读写都不会出现浮点格式不一致的问题。CRC16 校验必须严格按 Modbus 规范算,很多现场数据乱跳、“偶尔读到错误值”的问题,基本都是 CRC 实现不完整导致的。

如果现场已经有以太网环境,F429ZI 自带的 MAC 也能派上用场,配合外部 PHY 芯片走 Modbus TCP。协议映射逻辑可以共用,只是传输层从串口换到 TCP socket,代码结构调整不大。

5.3 防干扰和电源处理不能省

HVAC 现场真正害人的是变频器和电磁接触器。风机电机变频器的开关频率会产生大量噪声,如果传感器电源和控制板电源没有做处理,ADC 结果和 I2C 通信都会被干扰。我给传感器供电用的是独立 LDO,前面再加磁珠和 10µF + 100nF 电容,等于给传感器一个相对干净的“小水池”。

远程探头引出控制柜后,如果接线盒在电柜内部,一定避开变频器出线端子;如果必须交叉,用金属管穿线并接地。远程线超过 3 米时,最好把双绞线套上铁氧体磁环,有条件就换更高屏蔽等级的电缆,这个成本花得值。

6. 问题排查:我踩过的坑汇总

6.1 最常遇到的几类问题速查

现象可能原因处理办法
I2C 扫描不到器件供电缺失、上拉缺失、地址引脚错接先测 VDD,再测 SCL/SDA 静态电平
总线上出现重复地址与 EEPROM/RTC 地址冲突改 ADDR 引脚组合或换器件地址
本地温度正常,远程温度乱跳二极管极性反、线缆过长、滤波不足查极性,加滤波电容,缩短远程线
温度读数明显偏高传感器紧贴发热元件或通风不良远离热源,开散热孔,用延长线测远端
读数在几个值之间跳阶电源纹波大、远程线受变频器干扰加固电源滤波,远程线加屏蔽和磁环
单次转换时读到旧值没有等到转换完成就读检查转换状态标志或延时足够时间

6.2 I2C 通信排查的几个实操技巧

I2C 问题里,我遇到最多的是“地址对但一直 NACK”。先用逻辑分析仪抓波形,看主控发的地址字节对不对,这是 7 位地址左移后的 8 位值,不是裸 7 位,很多人容易在这里懵。其次是 SDA 线被拉低后恢复不了,大概率是某颗器件进入了总线错误状态,把总线时钟连续翻转几个周期可以恢复,实在不行只能断电重启。

还有一个容易被忽略的点:STM32 的 I2C 引脚要检查 Alternative Function 配置是否正确,GPIO 复用模式选错了,电平波形完全不对,通信自然跑不起来。初始化里还要看是否有外部的 I2C 电平转换器,如果有转换器,上拉电阻要在转换器两侧各放一个,否则总线时序会变得很怪。

6.3 远程温度漂移:一次现场调试的真实教训

去年做某个屋顶空调箱项目时,我遇到一个很头痛的问题:白天温度读数正常,一到傍晚变频器负载重起来,远程温度开始周期性跳变,幅度大概 0.8°C。最开始我以为是探头坏了,换了探头还是这样。

后来把示波器接到远程线上,发现开启强电设备瞬间,REM+ 和 REM- 线上明显叠加了高频脉冲。处理办法是两步:远程线换成了屏蔽双绞线,屏蔽层单端接地;同时在芯片端的 REM+/REM- 之间并了一个 100nF 的电容。改完后波动降到 0.1°C 以内。这个案例说明,远程通道看着简单,但现场物理环境远比实验室复杂,别把“软件漂移”的锅全甩给算法,很多时候问题出在布线和滤波上。

最后一点体感

调这种项目,我最大的体会是:教材里教的 I2C 时序和寄存器操作都好查,真正决定系统精度的是物理层问题——布线的抗干扰、探头的热接触、板卡的散热路径。代码写得再漂亮,探头没贴紧水管,读出来的也是假数据。所以我现在的习惯是先把单个节点的本地/远程通道打通,用两个已知温度点做校准,确认数据稳定了,再谈组网和协议栈。这套方法在做温控器、空调箱控制器、冷链箱或者任何嵌入式温度产品时都通用,省下的调试时间远比当初多花的验证时间多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询