☰
基于PJ85718DM与PIC32MZ的本地远程温度采集系统设计
2026/10/22 1:08:59 网站建设 项目流程

前阵子接了一个和暖通相关的控制器改造需求,需求表里最不起眼的一行写着:本地能看温度,远程也要能看温度。等我把这一行拆开做完整套东西后才发现,温度采集这件事本身不难,难的是把“本地读数”和“远程数据”在同一个系统里协调好。最终选型落到了 PJ85718DM 和 PIC32MZ2048EFM100 这两颗芯片上。

PJ85718DM 是数字温度传感器,挂在 I2C 总线上直接读寄存器就能拿到温度值;PIC32MZ2048EFM100 是带以太网的嵌入式 MCU,负责把读到的温度处理成能显示、能上传、能告警的数据。整套系统既可以用在嵌入式设备内部做板级温度监测,也可以用在 HVAC 的送风管、回风管、水管等现场测点,配合本地人机界面和远程监控平台一起工作。

这篇文章会围绕这个组合,把设计思路、硬件连接、软件实现、滤波标定和现场调试的完整过程都梳理一遍。如果你也在做温度测点分散、又需要集中上报的嵌入式项目,这篇可以作为一份直接的参考。

1. 项目整体设计与思路拆解

1.1 标题拆开看:两个名词,一条链路

PJ85718DM 是一颗数字温度传感器,典型输出是 I2C 格式,可以直接读到 16 位原始温度数据,换算成摄氏度只需要乘一个固定系数。PIC32MZ2048EFM100 则是一颗主频很高、资源很足的 MCU,内置浮点运算单元、大容量 Flash 和 RAM,并且带以太网 MAC 控制器,稍微加一颗 PHY 芯片和网络变压器就能跑 TCP/IP。

“本地和远程温度”在项目里有两层意思。

第一层是有两个实际测点:一个测点在控制箱内部,用来监测嵌入式电控板的散热情况;另一个测点放在 HVAC 机组的送风管道或水路管路上,距离控制器几米到几十米。两个测点都用 PJ85718DM,通过地址引脚分成不同地址,挂在同一条 I2C 总线上。

第二层是数据的两种消费方式:本地现场人员通过屏幕看实时温度;远程值班人员通过局域网页面、Modbus 或者 MQTT 随时查看同一组温度数据。这两个需求本质上是一条链路:传感器采集 → MCU 处理 → 本地显示 / 远程传输。

1.2 这类系统最常落地的场景

我在这套方案上测过的场景大概有四类:

  • 空气处理机组:做回风温度和送风温度监测,超温时联动风机变频和电动阀动作。
  • 嵌入式设备机柜:监测控制器 PCB 附近环境温度,防止夏季高温导致继电器、电源模块老化。
  • 冷热水系统:在供回水管路表面贴装远传测点,配合流量信号计算热量或者判断水泵运行状态。
  • 实验室环境箱:用本地传感器做箱体内部温度测量,用远端传感器测环境温度,再通过通信接口把数据交给上位机。

这些场景共同的特点是:测点分散但数量不多,数据不能只在一个地方显示,对实时性要求不算极端,但对稳定性和抗干扰能力要求比较高。PIC32MZ 的资源和接口能力在这个定位下非常合适。

1.3 为什么不用一颗小 MCU 单独测温

如果需求只是“测一个数然后显示”,那确实没必要用这么大一颗芯片。但实际项目里,MCU 往往还要承担好几件事:屏幕刷新、按键处理、继电器动作、通信协议解析、日志记录。把这些全部堆在一颗 8 位 MCU 上,程序会变成一团乱麻,后期加一个功能就得重构一次。PIC32MZ 的选择更像是在留余量:温度采集只占用它一小部分算力,剩下的资源用来跑协议栈和业务逻辑。

还有一个现实原因:PIC32MZ2048EFM100 内置的以太网 MAC 让远程传输变得非常轻量。不用外挂串口转网络模块,不用单独再做一块协议转换板,整机结构简单很多,故障点也少。

2. 器件选型为什么是这两颗

2.1 PJ85718DM 这颗传感器到底强在哪

项目资料里给出的典型参数是这样的:测量范围覆盖 -40°C 到 +125°C,分辨率可以配置到 16 位,温度转换结果经过数学换算后精度在 ±0.5°C 左右,工作电压兼容 3.3V 系统。它的接口是标准的 I2C,引脚里除了电源和地,主要就是 SCL、SDA 和地址选择引脚。

这些特性放在 HVAC 现场有很实际的意义。

传统方案里,测温度经常用 NTC 热敏电阻或者 PT100。NTC 需要分压电阻和查表,同一个分压电阻的精度直接影响最终读数,而且线一长压降就来了;PT100 需要恒流源、放大器和 ADC,结构和标定步骤更多。PJ85718DM 直接把温度转换成数字量,MCU 这边只需要一条 I2C 总线,省了模拟前端一大块电路。

我再把三者放在一起对比过:

测温方案信号形式所需外围标定复杂度典型适用场景
NTC 热敏电阻模拟电阻分压电阻、ADC中等,需查表消费电子、简单家电
PT100/PT1000模拟电阻恒流源、运放、ADC较高,需线性化工业过程、精密测量
PJ85718DMI2C 数字信号上拉电阻、去耦电容低,寄存器直接出温度嵌入式、HVAC、环境监测

另一个加分点是可以多颗并联。PJ85718DM 的地址引脚允许同一总线上挂多颗芯片,我这次就是两颗一起挂的。只要给它们配不同地址,读任何一颗对另一颗完全没有影响。

需要提醒的是,工程里一定要以实物数据手册为准。不同批次、不同封装的数字温度传感器在寄存器名、转换时间和精度定义上可能都有差别,代码里的延时参数和换算系数不要凭感觉写死。

2.2 PIC32MZ2048EFM100 在这里承担什么角色

初步来看,PIC32MZ2048EFM100 身上的几个数字非常关键:主频 200MHz 级别、Flash 容量达到 MB 级、RAM 有 512KB,还带硬件浮点。这意味着我可以放心使用浮点运算做温度换算和滤波,不用为了性能去写一堆定点数恶心代码。

它内置的以太网 MAC 是整个远程监控方案的核心。实际电路只需要外接一颗百兆 PHY 芯片、网络变压器和 RJ45 座,协议栈跑起来之后,设备就拥有了一个 IP 地址。远程端通过浏览器、Modbus 调试工具或者 MQTT 客户端就能读到实时温度。

它还带着多条 UART、SPI、I2C 和 CAN 接口。这些接口在 HVAC 系统里能派上大用场:一条 UART 接触摸屏,一条 UART 留给 RS485 扩展,CAN 可以用来接多台机组互联。外围设备的扩展空间很大,后面想加新功能基本不用换主控。

2.3 选型算不算性能过剩

“杀鸡用牛刀”这个说法我听过很多次。但从整个产品的角度看,如果 MCU 选得太小,为了塞下 TCP/IP 协议栈、Web 服务和业务逻辑,代码要写得极其拧巴,调试周期会拉得很长。PIC32MZ 看似资源多,实际上是把那些“看不清的隐性成本”提前付掉了。

而且温度监控类项目往往不是一次性产品。今天只测两个温度点,明天客户可能就要加湿度传感器、加电量采集、加历史曲线。主控资源留足,后续升级就是加外设的事,不用推翻重做。

3. 本地与远程温度的数据链路设计

3.1 本地测点怎么走:传感器到处理器

本地的传感器放在控制器内部,通过短距离排线连接到 PIC32MZ 的 I2C 通道。整个读取链路是:

  1. MCU 向对应地址发送“准备转换”指令,或者读取上一次转换结果。
  2. 等待规定的转换时间,这个时间典型在几十毫秒级别。
  3. 从传感器寄存器读取 16 位原始数据。
  4. 用补码转换把原始数据变成有符号数,再乘上温度分辨率系数,得到摄氏度。
  5. 数据进入滤波环节,之后既送到显示模块,也进入上传缓冲区。

这颗通路看起来短,但因为运行在嵌入式环境里,必须考虑一种情况:I2C 总线正处于等待状态时,恰好有一个硬件中断进来。所以我用了循环状态机而不是简单的 for 循环阻塞读取,避免读数据时把其他任务卡死。

3.2 远程测点和远程访问的两种理解

很多 HVAC 项目里,“远程温度”并不是通过网络实现的,而是传感器本身安装在远处。例如控制箱在配电间,测点在几十米外的风道里,这就需要一颗独立的 PJ85718DM 以短引线就近测量,再通过屏蔽线把 I2C 或转换后的数字信号传回控制器。这种情况下,本地和远程指的是物理距离。

另一种“远程”是数据上传:同一传感器可能就在设备旁边,但用户在中央控制室、手机 App 或者云平台上查看数值。这种情况更依赖通信链路。我在项目里提供了三种上行通道:

上行方式特点适用场景
以太网 Web 页面无需专用软件,浏览器输入 IP 即可局域网内人机查看
Modbus TCP数据寄存器标准,工控软件直接轮询接入 DDC、PLC、楼宇自控
MQTT发布订阅,数据可推送到多端云平台、手机 App、多客户端订阅

三种通道共用的是一份经过滤波和非法值剔除的有效温度数据,保证不管从哪个入口看,读数都是一致的。

3.3 数据刷新周期和报文设计

温度在 HVAC 系统里的变化相对缓慢,没必要用毫秒级周期去刷。我实测定下来,1 秒采集、5 秒上报一组完全够用。上报太快反而会把网络线程占满,导致其他协议处理被饿死。

报文格式上,我没有直接用浮点发送。浮点传输涉及大小端和精度问题,在工控环境中容易埋坑。实际做法是把温度乘以 100 转成整数再打包,例如 23.45°C 就表示成 2345,接收端再除以 100。这样数据紧凑、可读性高,也规避了不同编译环境下浮点数二进制布局不一致的问题。

4. 硬件电路:从传感器到 MCU 再到远方

4.1 接线关系用一个表说清楚

两个测点的接线方式相同,下方以其中一个 PJ85718DM 为例:

传感器引脚信号连接到说明
VCC电源正3.3V 电源并联 100nF 去耦电容
GND电源地系统地单点接地
SCLI2C 时钟MCU 的 I2C SCL4.7kΩ 上拉到 3.3V
SDAI2C 数据MCU 的 I2C SDA4.7kΩ 上拉到 3.3V
ADDR地址选择接 GND 或 VCC同一总线区分不同测点
ALERT超温提示MCU 的外部中断脚可选功能,轮询模式可悬空

如果总线上挂了两颗 PJ85718DM,另一颗的 ADDR 引脚接到相反电平即可。这样 MCU 就能通过不同地址命令分别读取两个温度值。

4.2 I2C 上拉电阻不是随便抄的

I2C 总线要有上拉电阻才能保证信号正确的电平。很多参考设计图直接写“4.7kΩ”,好像这个值天然正确,实际上它要考虑总线电容和目标频率。

上拉电阻和总线电容一起决定了信号上升时间,估算公式是:

tR ≈ 0.8473 × R × C

如果总线不长,SDA/SCL 线加传感器引脚本身,分布电容估算在 150pF 级别,用 4.7kΩ 上拉时:

tR ≈ 0.8473 × 4700 × 150 × 10⁻¹² ≈ 0.6μs

这个值在 100kHz 标准模式下是够用的。如果我把传感器引线拉得很长,总线电容可能涨到 500pF,同样 4.7kΩ 算出来:

tR ≈ 0.8473 × 4700 × 500 × 10⁻¹² ≈ 2.0μs

这就有点压着规范线了。遇到这种情况,要么换成 2.2kΩ,要么适当降低总线速率。所以项目里的做法是:短距离用 4.7kΩ,超过 30cm 的引线就重新估算电容,不偷懒。

4.3 强电侧的隔离和布线细节

HVAC 设备里最不缺的就是强电干扰。继电器通断瞬间、变频器输出侧、交流接触器吸合,都会通过空间耦合给传感器线带来毛刺。我在这套系统里做了几层处理:

  • 传感器信号线和 220V/380V 动力线分开走线槽,不交叉、不平行。
  • 传感器供电从 MCU 主板上单独的 LDO 引出,不和继电器驱动共用同一个电源轨,避免继电器动作时电压跌落影响上拉电平。
  • 远程传感器如果确实要走相对较长的距离,优先选择带屏蔽层的线材,屏蔽层在控制器侧单端接地,避免形成地环路。

如果现场必须做电气隔离,可以在 I2C 信号中间加数字隔离芯片。但隔离会增加成本和功耗,普通短距离测点其实没必要。判断标准只有一个:传感器线缆经过的区域有没有变频器、接触器、大电流开关。有,就老实加隔离;没有,常规处理足够。

5. 软件实现:读取、滤波、上报

5.1 底层读温度函数是怎么写的

底层驱动逻辑在硬件访问层,用统一的 i2c_write 和 i2c_read 封装,方便替换驱动库。先给传感器发一条读取命令,等待转换完成,再连续读两个字节。

#define TEMP_ADDR_A 0x4F /* 本地测点地址 */ #define TEMP_ADDR_B 0x4E /* 远程测点地址 */ #define REG_TEMP 0x00 int temp_read(uint8_t addr, uint16_t *raw) { uint8_t reg = REG_TEMP; if (i2c_write(addr, &reg, 1) != I2C_OK) { return ERR_I2C_WRITE; } /* 数字温度传感器转换需要时间,这里根据手册留足延时 */ delay_ms(20); uint8_t buf[2]; if (i2c_read(addr, buf, 2) != I2C_OK) { return ERR_I2C_READ; } *raw = ((uint16_t)buf[0] << 8) | buf[1]; return ERR_NONE; }

这段代码看起来简单,但有些细节值得注意。第一,I2C 读写函数都要检查返回值,不能假装一定成功,因为传感器掉线、地址冲突、总线被拉死都是真实会发生的事。第二,延时的长短要按传感器手册的允许转换时间来,不是固定 20ms 就能包打天下,换型号必须先改这里。

5.2 原始数据换算成摄氏度

读取到的 16 位数是补码格式,所以先把无符号 raw 转成有符号数,再乘分辨率系数。

float temp_calc(uint16_t raw) { int16_t signed_raw = (int16_t)raw; /* 符号位在高位 */ return (float)signed_raw * 0.0625f; }

有的传感器把高字节和低字节分别放在不同寄存器,读取顺序不能弄反,否则温度会直接变成一个很奇怪的值。调试时可以先固定给传感器一个已知环境温度,例如 0°C 冰水混合物,看换算出来是否在合理范围内。

5.3 主循环还是状态机

我最初是用顺序执行的方式写的,一个温度读取过程从头走到尾,结果发现只要 I2C 总线出一点问题,整个程序就卡在那里。后来改成状态机:

typedef enum { ST_IDLE, ST_CONVERT, ST_READ, ST_FILTER, ST_REPORT, ST_ALARM } temp_state_t; temp_state_t temp_state = ST_IDLE; void temp_task(void) { switch (temp_state) { case ST_IDLE: temp_state = ST_CONVERT; break; case ST_CONVERT: if (temp_read(addrA, &rawA) == ERR_NONE) temp_state = ST_READ; else temp_state = ST_ALARM; break; case ST_READ: tempA = temp_calc(rawA); temp_state = ST_FILTER; break; case ST_FILTER: tempA_filtered = filter_update(tempA); temp_state = ST_REPORT; break; case ST_REPORT: protocol_push(tempA_filtered); temp_state = ST_ALARM; break; case ST_ALARM: alarm_check(); temp_state = ST_IDLE; break; default: temp_state = ST_IDLE; break; } }

状态机的好处是每个状态只做一件小事情,就算某一步失败,也能退回到错误处理分支,不会让整个系统死锁。配合定时器以固定周期调用,温度任务始终以稳定节奏执行。

5.4 远程数据封装

远程报文最终要经过 TCP 链路发送。我把温度值先转成整数,再拼成可读的字段:

uint16_t t_int = (uint16_t)(tempA_filtered * 10); /* 转成一位小数整数 */ uint16_t whole = t_int / 10; uint16_t frac = t_int % 10; snprintf(buf, sizeof(buf), "t=%u.%u", whole, frac);

这里乘 10 是为了只保留一位小数,在暖通场景已经足够。一天下来温度变化通常只有几度,没有必要为了零点零几度的精度把报文搞复杂。

6. 数据滤波与校准:让读数看起来可信

6.1 为什么温度值会跳

温度传感器本身转换出来的数据一般是稳定的,但实际系统里还是会看到跳变。最常见的来源有三个:一是传感器放在风口或者发热元件旁边,空气流动导致温度快速起伏;二是 I2C 读取偶尔出错,某个采样点读到 0xFFFF 或 0x0000;三是继电器动作时电源出现瞬时波动,影响了传感器内部 ADC 的参考基准。

解决办法不是把每个原始值都当真理,而是让数值经过一个足够平滑的滤波器再显示和上传。

6.2 中值和平均组合起来用

我在项目里用了“先取中值,再做一阶惯性滤波”的组合方式。中值滤波可以干掉毛刺,惯性滤波可以把快速抖动抹平。温度数据连续取 5 个样本,排序后取中间三个求平均,再把结果跟上一次滤波值做加权:

#define FILTER_DEPTH 5 float history[FILTER_DEPTH]; uint8_t hist_idx = 0; float filter_update(float new_val) { history[hist_idx++ % FILTER_DEPTH] = new_val; /* 取中值,对温度突变做一个初步抑制 */ float sorted[FILTER_DEPTH]; memcpy(sorted, history, sizeof(history)); sort_ascending(sorted, FILTER_DEPTH); float median = sorted[FILTER_DEPTH / 2]; /* 一阶惯性滤波,系数alpha越小越平滑 */ static float filtered = 0.0f; const float alpha = 0.3f; filtered = alpha * median + (1.0f - alpha) * filtered; return filtered; }

HVAC 系统时间常数大,几秒内的毛刺本来就不代表真实温度变化,所以滤波系数可以选得保守一些。如果用在需要快速响应的嵌入式散热保护上,alpha 就要调大,否则传感器已经到 85°C,滤波后还显示 82°C,报警就会变慢。

6.3 两点校准能弥补传感器误差

数字温度传感器出厂精度已经不错,但贴装位置、线缆电阻、散热条件还是会带来系统性偏差。我在现场做过一次校准:把传感器和一支经过检定的温度计放在同一个恒温环境里,记录两个点的偏差,然后算斜率修正。

校准公式是:

temp_correct = k × temp_raw + b

k 和 b 通过两个校准点解出来。校准数据写入非易失存储区,上电后自动加载。实际操作中要注意一个细节:标定前让传感器先通电至少 10 分钟,让自热稳定下来,否则前面采集的温度还没稳定,校准点本身就不准。

6.4 断线和不合理数据不能硬算

就算传感器安装得很结实,也总有断线的时候。程序里对合理性一定要做检查:

  • 温度在 -40°C 到 125°C 范围内之外的,判定为非法数据。
  • 连续 3 次读取失败后,寄存器标记为“数据无效”,界面显示 “--”,不再更新。
  • 上报帧里带一个质量字,0x00 表示有效,0xC0 表示无效,让远端知道当前值不能参与控制。

这个处理非常重要。如果断线后还继续把上一个值重复上传,远程监控系统会以为温度一直正常,可能引发连锁误判。

7. 常见问题与排查实录

7.1 I2C 总线一直报 NACK

现象是在温度任务里返回 ERR_I2C_READ,连续读取失败。排查顺序是先确认传感器供电是否正常,再检查 SCL/SDA 有没有接反。最有效的定位方法是写一个 I2C 地址扫描工具,把总线上 0x08 到 0x77 的地址全部扫描一遍,看传感器实际在哪个地址响应。

大概率问题出在 ADDR 引脚的接法和代码里的地址不对应。ADDR 接 GND 和接 VCC 对应的地址不同,代码里写死之前先查手册。

7.2 温度读数整体偏高 2°C

这种情况通常不是传感器坏了,而是自热效应。传感器贴近 PCB 上的电源芯片或者其他发热器件时,本身就会感知到周围环境的热量。解决办法是让传感器稍微离开热源,或者在软件上直接把偏移量减掉。但最简单的做法是在结构设计时就留出空气间隙,别让传感器压在功率器件上。

7.3 远程页面刷新不出来

网络相关的排查有固定套路。先看 PHY 芯片的 link 灯有没有亮,再检查 MCU 拿到 IP 地址没有。如果 DHCP 获取失败,大概率是网线质量问题或者交换机的 VLAN 配置。如果地址能拿到但页面打不开,基本可以锁定是 HTTP 服务线程没有运行起来,或者内存被其他任务占用过多。

我在项目里把本地地址固定成自带的静态 IP,避免 DHCP 租约到期导致设备消失。现场环境网络结构越简单越稳。

7.4 运行几天后设备死机

嵌入式设备长时间运行最怕“隐性死锁”。常见原因是某个任务在等待 I2C 释放期间被高优先级中断打断,而中断里又碰了同一条总线的资源。解决方法分两步:一是 I2C 访问统一放到同一个任务里,不让中断和主循环抢资源;二是独立喂狗,把 I2C 故障恢复做成状态机的正常分支,而不是靠复位强行续命。

配合一张速查表:

现场现象可能原因处理办法
I2C 读不到 ACK地址写错、传感器没供电扫描 I2C 地址,检查供电引脚
温度读数跳变总线被毛刺干扰、滤波不足缩短线缆、加中值滤波
整体读数偏高传感器自热远离热源,软件偏移修正
远程页面超时DHCP 异常、内存不足固定静态 IP,精简网页资源
长时间运行死机I2C 资源互锁、任务阻塞统一总线访问,喂狗留恢复路径

7.5 比较隐蔽的时序坑

读传感器的时候,如果在转换尚未完成时就去读结果寄存器,拿到的可能是上一次的旧值或者半更新状态。有些芯片会在温度寄存器更新过程中对总线上锁,必须按手册里的转换时间等待完整周期。调试时看到读出来的数据偶尔和上次数值完全一样,就要怀疑是不是时序没对齐。

8. 项目落地后的几点实战心得

这套系统从原型到现场运行,给我留下的最深印象是:最难的不是把传感器读回来,而是让读取过程在复杂的现场条件下依然可靠。温度本身是个变化缓慢的信号,系统里真正要做的是把高速的采样转换成稳定的、有意义的业务数据。

我建议后来的人在硬件设计时,把 I2C 总线的扩展点留出来。一个项目今天挂两个温度测点,下次可能就要挂湿度、门磁、流量计。如果 PCB 上只留了一组排针,扩展时只能飞线或者改板,非常被动。

第二个建议是显示值和上报值不要用同一套滤波策略。本地屏幕要求响应快,稍微有一点点抖动没关系,所以短窗口滤波就够了;远程上报要求稳定,给监控中心的数据最好是 1 分钟级别的平滑值,避免值班室看到一条上下乱窜的曲线。我后来把两套滤波参数分开配,各自独立调优,效果立刻好了很多。

另外关于固件版本和传感器参数,建议在烧录时一并写入设备信息区。现场换一台控制器,必须知道当前版本对应的是哪一版标定系数、哪一个 I2C 地址表。没有这些记录,后面维护的人只能拿着示波器从头查,非常痛苦。

最后说一句我经常提醒自己的话:宁可让数据少更新几个周期,也不要给远端传一个假温度。HVAC 系统里温度是控制逻辑的输入,一个错读数可能让风机满转、让阀门全开,设备本身没事,能耗却被白白拉高。稳定性永远排在花哨功能前面。这套 PJ85718DM 加 PIC32MZ2048EFM100 的组合理清了“本地采集”和“远程上报”两条主线,后续再扩展其他测点,基本就是照着同一套思路继续加地址而已。

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

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

立即咨询