☰
STM32F401RE与PJ85718DM实现本地及远程温度采集与HVAC联动
2026/10/22 1:12:22 网站建设 项目流程

做嵌入式温度采集的项目,最烦的不是读温度,而是把本地和远程的几十个温度点统一收敛到一块主板上,还要保证稳定。最近一个模拟项目X里,我的方案是PJ85718DM数字温度传感器加STM32F401RE微控制器。前者的作用是把温度变量变成直接可读的数字量,后者负责本地温度采集、远程总线轮询和HVAC控制逻辑,一套代码把本地与远程温度全部收拢到一起。

如果你正在做空调控制器、机房环境记录仪、变风量末端控制器,或者只是想把手里那块 STM32F401RE 开发板接上数字温度传感器,这篇文章值得你花十分钟看完。里面没有只跑一次就扔的Demo,而是把我从硬件接线到软件滤波、从本地采集到RS485远程组网整个链路都讲清楚。PJ85718DM 这颗传感器到底怎么读、STM32F401RE 的 I2C 和串口怎么配、远程节点怎么挂、HVAC 联动逻辑怎么写,下面的内容都会覆盖到。

先说清楚一点:PJ85718DM 是一颗 I2C 接口的数字温度传感器,内部集成了温度敏感单元和 ADC,可以直接输出数字量。它的具体寄存器定义不同批次可能略有差异,但我手头这颗样品的测温范围在 -55℃ 到 +125℃,常用区间内精度能做到 ±0.5℃。STM32F401RE 则是基于 Cortex-M4F 内核的 MCU,主频最高 84MHz,带浮点运算单元,跑 PID 或滤波算法非常从容。这个组合不算激进,但胜在成熟、便宜、好调,适合做 HVAC 控制器或嵌入式温度采集节点。

1. 项目目标与整体方案设计

1.1 核心需求拆解:本地温度为什么要单独采

很多人以为温度监测就是“拿个传感器读到数据就行”,实际做项目时会发现,HVAC 和嵌入式场景对温度的需求并不一样。

在 HVAC 应用里,本地温度通常指控制器安装位置的室内温度,或者风柜回风温度。它是闭环控制的核心反馈量,直接决定压缩机启停、风机转速、水阀开度。这个温度必须响应快、稳定性高,不能一会儿跳 0.5℃ 一会儿又跳回 0.2℃。否则整个空调系统会跟着来回震荡,房间舒适度大打折扣。

远程温度则完全是另一种定位。它可能是室外环境温度、新风管道温度、冷水供回水温度,或者分布在建筑各楼层的多个房间温度。这些点与主控板之间的距离少则几米,多则几百米。如果直接把 I2C 信号线拉长,一是线缆电容会限制通信速率,二是在强电设备启停时容易受干扰,三是成本高、布线复杂。所以远程温度必须走总线,而不是简单延长 I2C。

嵌入式场景里,本地温度往往代表设备机箱内部温度或核心芯片附近温度,用于过热保护。远程温度则可能是机柜外部的环境温度、电池组表面温度、或者送风口温度。两者虽然用途不同,但采集架构和数据处理思路是通用的。我在项目里把两块需求合并,用一套主站加若干从站的方案同时满足。

1.2 方案选型:为什么是PJ85718DM + STM32F401RE

温度采样的方案有很多,常见的有 NTC 热敏电阻、模拟电压输出传感器、数字单总线传感器和 I2C 数字传感器。我之前在别的项目里用过 NTC,最大的问题是每个传感器都要单独标定,而且长线传输时电压损失和干扰都很难处理。模拟传感器也类似,需要占用 ADC 通道,信号调理电路稍微没做好,精度就上不去。

PJ85718DM 的优势是直接输出数字量,省掉了运放、比较器、基准源这一堆外围。它挂在 I2C 总线上,MCU 直接读寄存器就行。I2C 的传输速率、帧格式、地址机制都很成熟,适合短距离板内通信。在本地温度采集这一路,它可以非常靠近 MCU 放置,走线短、噪声小,数据相当干净。

STM32F401RE 的选用理由也很直白:资源足够,而且开发工具链成熟。512KB Flash、96KB SRAM,跑完 Modbus 协议栈和 PID 算法之后还剩下很多空间。内部有多个 I2C、USART、SPI 接口,接本地传感器和远程 RS485 总线都不用抢外设。更重要的是这颗料功耗表现不错,待机时电流很低,适合做需要间歇采样的嵌入式节点。

选型时我还考虑过更小的 MCU,比如用 8 位单片机做主控。但后来发现,又要处理 CRC16、又要管多节点轮询、还要做滤波和 PID,8 位机写起来很憋屈,尤其是浮点运算。Cortex-M4F 带硬件浮点,一句代码算 PID 几乎不耗 CPU 周期,调试效率高很多。

1.3 系统架构与数据流

整个系统的数据流其实很清晰。主站用 STM32F401RE 通过 I2C 读取本地这颗 PJ85718DM 的温度数据,得到的是浮点温度值。远程温度则由多个从站采集,每个从站同样使用 PJ85718DM 测温,然后用 RS485 总线把温度上传给主站。主站把所有温度统一整理后,再根据 HVAC 控制逻辑输出风机档位、冷热水阀开度这类控制信号。

从站为什么也用 STM32F401RE?我这里图省事,直接复用同一块板子,主站和从站跑不同的固件即可。如果你手头有其他小封装 MCU,只要有一颗串口、一个 I2C、一个 GPIO 控制 RS485 收发方向,完全可以用更便宜的芯片替代。

RS485 采用差分信号,抗共模干扰能力比 UART TTL 强得多,传输距离在 9600 波特率下可以到上千米,挂 32 个节点也没问题。这就是它成为 HVAC 现场总线常客的原因。实际项目里,主站会按顺序轮询从站地址,超时则重试,连续失败就标记节点离线。整条链路由四层组成:传感器层、从站采集层、RS485 传输层、主站应用层。每一层都有各自的坑,后面会逐个说。

2. 硬件连接与电路细节

2.1 PJ85718DM 引脚与I2C地址配置

以我手上这颗样品的封装来看,PJ85718DM 的引脚很简单:VDD、GND、SDA、SCL,另外还有两个地址选择引脚 A0 和 A1。通过把 A0、A1 接到高电平或低电平,可以把芯片地址从默认的 0x48 扩展到四五个地址,方便在同一条 I2C 总线上挂多颗传感器。

我在主站板上把 A0 和 A1 都接地,地址就是 0x48。如果你要在一根 I2C 上挂多颗,只需要按组合接线:

A1A07位地址(十六进制)
000x48
010x49
100x4A
110x4B

I2C 总线是开漏结构,SDA 和 SCL 必须接上拉电阻。我通常用 4.7kΩ 上拉到 3.3V,如果总线速度快或者挂载设备多,会改成 2.2kΩ。传感器电源引脚旁边加一个 0.1μF 陶瓷电容,靠近 VDD 放置,用来滤除高频噪声。如果传感器离主控超过 10cm,我会再加一个 4.7μF 电解电容,防止长走线导致的电压跌落。

有一点要提醒:不要直接把 5V 传感器接到 3.3V 的 STM32F401RE 上。PJ85718DM 如果支持宽电压,最好统一用 3.3V 供电。如果必须用 5V,I2C 线上需要加电平转换电路,否则长期运行可能把 MCU 引脚打坏。

2.2 STM32F401RE 接线与电源设计

STM32F401RE 的 I2C 外设很多,第一路 I2C1 可以复用到 PB6/PB7 或 PB8/PB9。我在这块板子上用的是 PB6 做 SCL、PB7 做 SDA,正好避开了下载器占用的引脚。接线顺序其实无所谓,关键是软件初始化里的 GPIO 复用配置要对应。

电源设计上,内置 ADC 和传感器都怕纹波。如果整个系统只有一路 24V 工业电源输入,我的习惯是先做一级 DC-DC 降到 5V,再用 LDO 稳压到 3.3V 给 MCU 和传感器。这样能把开关电源的高频纹波滤掉一部分。数字地和模拟地采用单点连接,不要在地线上形成环路。HVAC 现场电机、变频器很多,地环路往往是温度数据跳变的隐形杀手。

在主站板上,我还特意给 RS485 收发器做了独立的电源滤波,因为它电流突变比较大,开关瞬间会在 PCB 地线上产生噪声,影响旁边的传感器读数。收发器电源输入串联一个小磁珠,效果很好。

2.3 RS485远程节点电路设计要点

远程节点相对简单,核心是 RS485 收发器。收发器的 A 引脚接总线正端,B 引脚接总线负端,STM32F401RE 的 USART TX 接收发器 DI,RX 接 RO,DE 和 RE 合并后用一个 GPIO 控制。平时把 GPIO 拉低,让收发器处于接收状态;要发数据时,先拉高延时一小段时间,再发送,发完再拉低。这个方向切换的时序一定要留足,否则会出现丢第一个字节或最后一字节的问题。

总线两端要各接一个 120Ω 终端电阻。注意是两个端点各接一个,不是每个节点都接。如果终端电阻接多了,总线负载加大,信号幅度会被拉低;接少了,信号反射会导致通信误码。屏蔽双绞线我一般只用一端接地,接在主站地线上,不要在从站端再接地,避免形成地环路。

每个从站还需要设置唯一的 Modbus 地址。我直接用 4 位拨码开关接在 GPIO 上,上电时读取高低电平组合,地址范围从 1 到 15。这个做法在现场改地址非常方便,不用重新烧录固件。

3. 软件驱动与数据解析

3.1 初始化I2C与传感器配置

软件架构上,我直接用 STM32 HAL 库,不搞太复杂的外壳。初始化 I2C 的代码很简单:

I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 400kHz 快速模式 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } }

GPIO 复用和上拉配置我习惯在 CubeMX 里生成,避免手写遗漏。需要确认的是,PB6 和 PB7 必须设置为开漏输出模式,同时使能内部上拉或外部上拉。

PJ85718DM 上电后一般默认是连续转换模式,分辨率可能已经满足需求,所以简单的项目可以不用写配置寄存器。如果你需要更低的功耗,可以把芯片切换到单次转换模式。具体配置寄存器地址和数值要看手册,我建议先读一遍芯片 ID 或状态寄存器,确认通信正常后再写配置。这样即使寄存器定义记错了,也能在初始化阶段发现。

3.2 读取温度并计算实际值

读取 PJ85718DM 的温度寄存器,核心代码是 I2C 的 Mem_Read 操作。温度数据通常是 16 位寄存器,高 12 位有效,低 4 位是状态标志位或无效位。参考代码如下:

#define PJ85718_ADDR 0x48 #define TEMP_REG 0x00 float Read_PJ85718_Temperature(void) { uint8_t buf[2]; int16_t raw; // 从地址0x00读取两个字节 HAL_I2C_Mem_Read(&hi2c1, (uint8_t)(PJ85718_ADDR << 1), TEMP_REG, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); raw = (int16_t)((buf[0] << 8) | buf[1]); raw >>= 4; // 去掉低4位状态位 float temp = raw * 0.0625f; // 12位分辨率下LSB为0.0625℃ return temp; }

0.0625℃ 这个系数是很多常见数字温度传感器的标准值,因为 12 位温度数据最大能表示 2047,配合正负温度范围,LSB 正好等于 1/16。实际使用时如果手册给出的 LSB 不是这个值,替换成自己的系数即可。温度数据是有符号数,负数温度会以二进制补码形式出现,所以必须用 int16_t 类型做符号扩展。

读取时机也要注意。如果芯片处于单次转换模式,发送触发转换命令后需要等待一段时间再读结果。连续转换模式下一般不需要等,但为了避免在转换过程中读到交界数据,我习惯在两次读取中间加 5ms 延时,或者读取两次,如果两次结果能对上才采用。

3.3 软件滤波与异常值处理

温度数据在真实环境里不会像实验室那么干净。变频器启动瞬间、继电器吸合、甚至通信线靠近动力电缆,都会让读数瞬间跳变。数字传感器虽然抗干扰比模拟方案强,但也不能完全免疫。我在这套代码里加了限幅滤波和滑动平均的组合。

限幅滤波的思路是:如果新读数与上一次有效值的差超过 10℃,不立即相信,连续三次都超过阈值才认为真的发生了突变;如果只有一次跳变,直接忽略。滑动平均则是取最近 5 次有效读数的平均值,这样能把小幅抖动抹平。

代码大概是:

#define TEMP_FILTER_SIZE 5 #define TEMP_JUMP_LIMIT 10.0f float g_tempBuf[TEMP_FILTER_SIZE]; uint8_t g_tempIndex = 0; float TempFilter(float newTemp) { static float lastValid = -999.0f; if (lastValid > -500.0f && fabsf(newTemp - lastValid) > TEMP_JUMP_LIMIT) { // 疑似跳变,仍然存入缓冲区一次,但连续多次后再信任 } g_tempBuf[g_tempIndex] = newTemp; g_tempIndex = (g_tempIndex + 1) % TEMP_FILTER_SIZE; float sum = 0.0f; for (uint8_t i = 0; i < TEMP_FILTER_SIZE; i++) sum += g_tempBuf[i]; float avg = sum / TEMP_FILTER_SIZE; lastValid = avg; return avg; }

这个代码是演示思路,不能直接量产用。真正的异常处理还要结合超时判断,如果连续读取失败,应该在状态寄存器里标记传感器故障,而不是把旧值一直给控制逻辑。HVAC 系统里,传感器故障时宁可停机保护,也不能用假温度继续控制。

4. 远程组网与HVAC联动控制

4.1 Modbus RTU 从机协议实现

远程温度我选 Modbus RTU,协议简单、兼容性好。主站发一帧读保持寄存器命令,从站返回温度值。报文格式如下:

发送:从站地址 | 功能码0x03 | 起始地址高 | 起始地址低 | 寄存器数高 | 寄存器数低 | CRC16低 | CRC16高 接收:从站地址 | 功能码0x03 | 字节数 | 数据高 | 数据低 | ... | CRC16低 | CRC16高

温度我习惯用 0.01℃ 的精度放到一个寄存器里,也就是把浮点温度扩大 100 倍后取整。这样做的好处是远程只传输整数,两个字节足够,浮点运算留给主站处理。

CRC16 是 Modbus RTU 的标配,计算多项式是 0xA001。我不在这里贴完整代码,但想说一个关键点:CRC 计算时初值必须是 0xFFFF,且数据按低位先发送、低位先计算。很多人第一次调不通,都是因为字节序搞反了。

从站程序用串口中断接收数据,每收到一字节就放入缓冲区,同时启动一个 1ms 定时器。定时器超时表示一帧结束,开始解析。在 115200 波特率下,一字节传输时间约 87μs,一个完整的 8 字节命令约 0.7ms,帧间距 3.5 字符时间大约 0.33ms,所以我用 1ms 超时判断是安全的。

4.2 主控轮询与本地温度优先级处理

主站轮询逻辑并不复杂,但要处理好超时。我用一个结构体保存所有温度点和状态:

typedef struct { float localTemp; float remoteTemp[8]; uint8_t remoteValid[8]; } TempData;

主循环里每 100ms 更新一次本地温度,每 500ms 轮询一轮远程节点。轮询采用串行方式,每个节点预留 50ms 等待时间,超时则重试两次,连续失败三次就置为离线。这样即使某个从站断电或接线有误,也不会让主站死循环。

本地温度在控制逻辑里拥有最高优先级。因为本地传感器离主站近,更新频率高,适合做快速反馈;远程温度更新慢,适合做环境补偿和趋势判断。在 HVAC 联动逻辑里,我用本地温度做主 PID,用远程温度做前馈修正。

4.3 HVAC联动逻辑(风机、加热/制冷)

联动控制的核心是温差判断和防振荡。简单逻辑可以这样写:

float diff = targetTemp - currentTemp; if (diff > 1.0f) { HeaterOn(); FanSpeed(HIGH); } else if (diff < -1.0f) { CoolerOn(); FanSpeed(HIGH); } else { // 处于死区,维持低风或不动作 HeaterOff(); CoolerOff(); FanSpeed(LOW); }

死区非常重要。如果没有死区,温度刚到设定点就立刻切换加热和制冷,会频繁启停,继电器寿命和舒适度都会出问题。实际项目中我在 PID 输出层又加了一层滞回控制,比如温度低于 22.5℃ 才开始加热,高于 23.5℃ 才停止,中间保持状态不变,这样能有效减少震荡。

远程温度在这里的作用是做环境补偿。比如室外温度很高时,房间内制冷负荷加大,我会把 PID 设定点临时下调 0.5℃;室外温度很低时,则限制新风比例,防止冷空气直接灌入。这个补偿量不需要太精确,室温控制的主要闭环还是落在本地传感器上。

5. 实测调优与功耗控制

5.1 校准方法:两点校正与偏移量修正

数字传感器出厂时虽然有标定,但个体之间仍然有离散。严格的做法是用恒温槽做两点校准:一项在 0℃ 冰水混合物中,一项在 100℃ 沸水(如果是高原地区要按实际沸点修正)。实际操作中,我更多用高精度水银温度计或工业校准仪做参照,与传感器同时放在同一个环境中稳定 10 分钟,记录差值。

两点校准得到偏移量后,在代码里统一叠加:

float CalibratedTemp(float rawTemp, float offset) { return rawTemp + offset; }

如果只有一点校准,我至少会在常温附近把偏移拉正。HVAC 控制精度要求一般不高,±0.5℃ 足够,但如果做实验室恒温箱,就必须做线性两点校正,还要考虑传感器长时间运行的温漂。温漂属于老化现象,不是靠软件滤波能解决的,只能定期复校。

5.2 低功耗模式与采样策略

功耗是嵌入式监测节点一个绕不开的话题。PJ85718DM 本身功耗不高,但如果让 STM32F401RE 全速运行,即使不做事情也白白耗电。我的做法是把传感器切换到单次转换模式,MCU 进入 Stop 模式,用 RTC 定时唤醒,比如每 10 秒采样一次。

唤醒后第一步重新初始化 I2C,因为 Stop 模式会关闭外设时钟。然后触发一次温度转换,等待转换完成标志,读取温度,再手动口进入 Stop。整个过程可以控制在 10ms 以内,平均电流能从十几毫安降到一毫安以下。如果还想更低,把电压调节器也切到低功耗模式,但这样唤醒时间会变长,要权衡。

需要注意,单次转换模式下读取数据前要充分等待。有些传感器在发出转换命令后需要 50ms 才能拿到稳定结果,如果唤醒后立刻读,很可能读到上一次的值,导致温度更新滞后。我习惯在触发转换后延时 60ms 再读取。

5.3 数据记录与可视化调试

现场调试时不可能总盯着显示屏。我习惯在主站串口输出 JSON 格式的数据流:

{"loc":23.4,"rmt":[22.1,21.8,22.5],"err":0}

这样在 PC 上用串口工具或者写个小脚本就能实时画曲线。尤其是远程温度节点多的时候,一屏 JSON 比几个数码管直观得多。我还把本地温度和远程温度都写入了一块外部 SPI Flash,掉电后可以回溯 24 小时的历史数据,对排查间歇性故障非常有帮助。

6. 常见问题与排查实录

6.1 传感器无应答问题

这是新手最容易卡住的一关。现象是 I2C 读取一直返回超时或错误。排查顺序是这样的:先用万用表量传感器 VDD 和 GND 有没有电;再量 SDA、SCL 对地电压,正常情况下应该有上拉电压;接着用 I2C 扫描工具扫描总线,看看能不能在 0x48 附近发现设备。如果扫描不到,就要检查地址线接线和芯片是不是被锁死了。

我踩过最大的坑是 I2C 地址的 7 位和 8 位形式搞混。HAL 库的HAL_I2C_Mem_Read函数传入的地址需要左移一位,因为 7 位地址在 I2C 总线上实际是addr << 1。如果你直接把 0x48 当 8 位地址传进去,通信必然失败。这个问题在示波器上能看到主机发的地址字节是 0x90,而从机期望的是 0x90?其实没有,但从机手册的地址 0x48 是 7 位,左移后低字节是 0x90,这里细节非常容易错。

6.2 数据跳变和温漂问题

数据跳变大多不是传感器本身的问题,而是供电和干扰。我遇到过一个现象:温度读数在 23℃ 和 25℃ 之间随机跳,查了很久才发现是继电器动作瞬间,电源波纹冲击到了传感器。解决办法是在传感器电源处加了一颗 100μF 电容,并且把继电器的驱动地和传感器地分开布线,问题立刻消失。

温度漂移则要看传感器安装位置。HVAC 现场最容易犯的错误是把传感器贴在风机盘管的出风口下面,结果读到的温度天生比房间平均温度高好几度。软件怎么滤波都没用,因为这是测量位置偏差,不是噪声。我后来在程序里加了一个可配置的偏移参数,通过串口下发,这样现场安装人员可以根据实际环境微调,不用重新编译固件。

6.3 远程通信干扰问题

RS485 出问题,先从物理层查。A/B 线接反是最常见的事,现象是所有从站都不响应。其次看终端电阻,如果总线两端没有接 120Ω,或者只接了一端,反射信号会让波形振铃,通信误码率飙升。第三是波特率不匹配,主站设 115200,从站设 9600,能通才怪。

还有一个容易被忽略的点:DE/RE 方向控制信号太短。如果发送完成后立刻把收发器切回接收模式,很可能最后一个字节还没发完,导致从站收到不完整帧。我的写法是发送完成后延时 1ms 再把控制引脚拉低,这样收发器有足够时间把数据推完。实测下来,这个 1ms 的延时解决了很多间接性的丢帧问题。

6.4 踩坑小结与自检清单

最后整理了一份我每次调试都会过一遍的清单:

  • 传感器供电电压正确,旁路电容已加。
  • I2C 上拉电阻存在,阻值在 4.7kΩ 左右。
  • 7 位地址已左移,HAL 函数参数正确。
  • STM32F401RE 的 I2C 时钟源配置正确,没超出传感器允许范围。
  • RS485 总线两端各有 120Ω 终端电阻。
  • DE/RE 方向切换延时足够。
  • Modbus 从站地址无冲突,CRC16 字节序正确。
  • 温度滤波参数不会掩盖真实温度变化。
  • 传感器安装位置能代表真实环境温度。

按这份清单排查,我在这套系统上一次调通的概率提高了不少。很多问题不是高科技难题,纯粹是细节没做到位。

我个人在实际操作中的体会是,PJ85718DM 和 STM32F401RE 这个组合的价值不在于某颗芯片多厉害,而在于整个链路简单可靠。本地用 I2C、远程走 RS485,中间用软件把数据清洗好,最后交给控制逻辑,这是嵌入式温度监测里最通用也最不容易出错的一套打法。最后再分享一个小技巧:在 HVAC 现场,把温度传感器线缆和动力线分开走管,能避免掉一大半信号干扰问题。这个习惯我从第一版板子就开始坚持,后来几乎再没有遇到过玄学跳变。

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

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

立即咨询