做嵌入式和暖通空调系统的人,都会碰到一个绕不开的问题:温度监测。不管是PCB板级散热管理,还是风管、压缩机、电机绕组的温度监控,都需要实时、准确地采集温度数据。最近我基于数字温度传感器PJ85718DM和STM32F207VGT6微控制器,完成了一套既能测本地温度、又能测远程温度的双通道监测方案,并把它用在了某小型HVAC控制箱和一块大功率嵌入式主板上。整套系统从硬件到固件再到远程上报,跑了一个季度,稳定性和精度都符合预期。这篇内容我尽量把设计和踩坑过程完整记录下来,给正在做类似设备或者准备选型的读者一些参考。
很多朋友看到"本地温度"和"远程温度"会疑惑:不都是温度计吗,有什么差别?实际在嵌入式和HVAC场景里,这两个概念代表的是完全不同的测温路径。单靠一套方案同时覆盖两路,能省不少硬件成本,也能让控制器对现场热状态有更全面的判断。下面我会从需求拆解、器件选型、硬件电路、固件逻辑、远程通信以及问题排查这几个方向展开,每个环节都会给出我实测过的具体做法。
1. 项目需求与整体方案设计
1.1 本地温度与远程温度,到底在监测什么
先澄清一个很容易混淆的概念。这里说的"本地温度",指的是传感器芯片自身所在位置的温度,也就是PCB表面或控制盒内贴近芯片的空气温度。它反映的是设备整体工作环境的热负荷。比如功率板上大量MOSFET和变压器产生的热量积聚,哪怕板子有风扇,传感器周围的空气温度也会明显上升。本地测温的意义在于判断设备内部散热是否正常,避免箱体内持续过热导致电子器件加速老化。
"远程温度"则不是靠传感器自己接触热源测量,而是通过外接一只测温二极管(或三极管的PN结),把测温点引到传感器外部,远距离监测某个特定热源的温度。这种测温方式在各种电源管理芯片、服务器主板上很常见:被监控的CPU、GPU或者功率器件表面装一颗小信号二极管,温度变化会引起PN结正向压降变化,传感器通过精确测量这个电压差换算出温度。
在HVAC应用里,远程温度的点位通常包括:风机盘管上的回风温度、送风管道表面的温度、冷水机组压缩机的排气温度、甚至室外机散热翅片温度。这些点位分布在控制板之外,如果用常规NTC热敏电阻,要么占用独立的ADC通道和分压电路,要么布线复杂、抗干扰差。采用PJ85718DM这类带远程二极管测温功能的数字传感器,只需要一对细线引到远程PN结上,传感器就能同时读回两路温度,硬件上非常简洁。
1.2 为什么选择PJ85718DM + STM32F207VGT6
先说传感器。PJ85718DM是一颗I2C/SMBus接口的数字温度传感器,支持一路本地测温加一路远程二极管测温,测量分辨率可以达到0.0625摄氏度,本地和远程通道的精度在正负1摄氏度左右。还带ALERT报警输出引脚,可以设置高低温阈值,一旦超限立刻拉低中断脚,不需要主控持续轮询比较数据。对于HVAC控制器来说,这个特性很实用,因为它可以直接接硬件看门狗或者作为紧急停机信号源。
再说主控。STM32F207VGT6是Cortex-M3内核,主频最高120MHz,有1MB Flash和128KB RAM,外设非常齐。它内置了多个I2C接口、UART串口,而且带以太网MAC。这意味着同一颗单片机既能通过I2C读传感器、通过UART接RS485收发器跑Modbus RTU,又能直接配合外部PHY芯片做以太网远程通信,甚至还能跑轻量级协议栈上传MQTT数据。对一个小型监测终端来说,这个算力绰绰有余,不需要单独再挂一颗通信协处理器。
选型阶段我也对比过热电偶和PT100方案。热电偶测温范围广,但需要独立的冷端补偿芯片,而且信号是微伏级的,长距离传输容易受干扰。PT100精度高,但要配恒流源或精密电桥,ADC电路复杂,成本并不低。PJ85718DM这种数字传感器把模拟前端都集成在芯片内部了,直接输出数字量,现场抗干扰能力明显更好,特别适合装在变频器、接触器密集的HVAC电气柜里。
1.3 系统整体架构
整套系统的数据流是一条单向链路:PJ85718DM通过I2C总线把本地温度和远程温度连续送到STM32F207VGT6,主控在内部完成滤波、阈值判断、数据缓存,然后把结果通过两路物理信道输出:一路是RS485,跑Modbus RTU协议,供给现场楼宇自控主机周期轮询;另一路是以太网MBus(预留),可以主动向本地监控软件推送JSON数据。此外,ALERT硬件报警信号被单独连到MCU的外部中断引脚,实现超温快速响应。
这样的架构把数据采集和通信解耦了。主控的温度采集逻辑始终以1秒周期运行,不依赖外界查询。Modbus从机响应从缓存里取数据,不会因为要临时发起I2C读操作而阻塞通信。同理,以太网推送也通过独立任务处理,即使网络暂时断掉,温度采集和本地报警照常工作。远程监控只是数据使用者,不是数据源,这是工业控制端比较稳妥的设计方式。
2. 硬件设计要点与电路实现
2.1 PJ85718DM 引脚与接线
以我手头这颗PJ85718DM为例,芯片常见引脚包括电源VCC、地GND、I2C时钟SCL、I2C数据SDA、报警输出ALERT,以及远程测温专用的DIODE+和DIODE-。部分型号还会有I2C地址选择引脚,可以把地址从默认的0x48改到0x49/0x4A,方便在同一条I2C总线上挂多颗传感器。我设计中用了一颗传感器,直接使用默认地址,没有做扩展。
I2C总线的上拉电阻需要注意。STM32F207VGT6的I2C接口是开漏输出,必须靠外部上拉电阻把SCL、SDA拉到高电平。标准模式下上拉电阻可以是10kΩ,但FM+模式400kHz时,如果总线电容较大、上拉电阻太大,上升沿会变缓,导致通信不稳定。我实测400kHz时用4.7kΩ比较合适。上拉电阻的计算可以按最小电流考虑:上拉电阻最小值 = (VCC - VOLmax) / IOLmax,其中VOLmax是逻辑低电平上限,IOLmax是引脚灌电流能力。3.3V系统下,一般2.2kΩ到4.7kΩ都在安全范围。我这块板最终选了4.7kΩ,总线长度不到10厘米,波形没有畸变。
电源去耦方面,传感器的VCC脚和GND脚之间放了0.1μF陶瓷电容,尽量靠近芯片引脚。这颗芯片功耗很低,动态电流很小,但如果去耦电容离得太远或者根本没有,I2C通信瞬间的电流冲击会产生小幅电源塌陷,严重时会导致读到的温度值偶尔跳一个LSB。这种问题在手册里不会写,现场排查时却容易走弯路。
2.2 远程测温对传感二极管的要求
远程温度测量原理是利用PN结正向电压与温度的近似线性关系。工程上最省事的做法是用一只小信号三极管,比如常见的2N3904或9013,把集电极和基极短接,这样半导体的发射极和集电极之间就等效成一个二极管,接到传感器的DIODE+和DIODE-两端。注意极性不能反:通常三极管的发射极接DIODE-,集电结短接后接DIODE+。如果接反了,读出来的温度要么完全是错的,要么传感器会报远程二极管开路。
远程二极管的走线是这套硬件最容易出问题的地方。PN结本身的电压变化率大约在每个摄氏度量级约负2mV左右,传感器要分辨0.0625摄氏度,意味着要分辨大约125μV的电压变化。如果你把远程线束布在变频器输出或接触器旁边,又没有做任何屏蔽,噪声很容易淹没这个微小信号。我的实践是:远程线用双绞线,把DIODE+和DIODE-作为一对差分走线,屏蔽层单端接地。线长控制在30厘米以内,如果必须超过这个长度,最好在远端二极管附近并联一个2.2nF的陶瓷电容滤除高频干扰,但容量不要盲目加大,否则会拖慢信号建立时间,导致温度读数滞后。
另外,芯片一般会对远程二极管的短路和开路状态进行检测。我第一次打样时没有仔细布置远程二极管的位置,有一条走线穿过了一个大电流电感的磁力范围,导致远程温度偶尔出现-127或者+127这种极端值。后来查状态寄存器才发现是远程故障标志被拉高。重新布线并增加屏蔽后,故障状态再也没出现过。
2.3 STM32F207VGT6 最小系统与接口设计
主控侧的最小系统并不复杂:3.3V供电、外部25MHz晶振、复位电路、SWD调试接口、以及必要的去耦电容。有一点必须提醒,STM32F207VGT6虽然有内置以太网MAC,但物理层PHY芯片必须外接。如果项目中需要以太网上报,晶振最好选25MHz,这样可以和PHY芯片共用同一个参考时钟,省掉独立的25MHz无源晶体,也能减少EMI风险。
在主控与传感器的接口上,我直接使用3.3V单电源,传感器大约工作在3.3V,MCU的I2C引脚也是3.3V电平,不需要额外电平转换。在工业现场,RS485接口则必须做隔离处理。我为RS485收发器选了带隔离电源的模块方案,A/B端口还加了对地的TVS管和120Ω终端电阻,放在总线最远端。这样即使现场有长达几十米的通信线,遇到雷击感应或者共模电压突变时,也不会倒灌烧坏MCU的串口引脚。
远程控制柜里往往有24V交流电和几路接触器线圈,电磁环境比较恶劣。我在电源入口处加了一级共模电感和一个大容量电解电容,再经DC-DC降到3.3V,独立给MCU和传感器供电。调试时发现,如果不加共模电感,RS485通信偶尔会误码,温度数据本身倒没问题,但通信稳定性显著变差。这算一个隐性成本,但在HVAC场景下不能省。
3. 固件实现与协议解析
3.1 I2C 驱动与传感器初始化
固件部分我采用寄存器和标准库混合编程,没有全部套HAL库,主要考虑到I2C时序的灵活控制。初始化I2C时设置400kHz速率,7位设备地址使用0x48,随后读一字节设备ID寄存器确认芯片在线。很多温度传感器都有一个唯一ID寄存器,这一步虽然简单,但能筛掉大量焊接不良或地址配置错误的问题。
PJ85718DM的初始化流程通常包括:配置转换周期为每秒8次、使能本地和远程通道、设定ALERT引脚为比较模式并允许外部中断触发。注意ALERT阈值是带符号的定点数,和温度寄存器格式保持一致,我直接左移4位后写入上限和下限寄存器。初始化完成后,我把ALERT引脚配置为MCU的下降沿外部中断。这样即使芯片内部转换频率很快,也不需要MCU每毫秒去轮询一次状态寄存器。
给一段简化的I2C读取代码,演示读取本地温度寄存器和远程温度寄存器的方式:
uint8_t sensor_write_reg(uint8_t addr, uint8_t reg) { // 发送起始条件、设备地址+写位、寄存器地址 } int16_t sensor_read_temp(uint8_t addr, uint8_t reg) { sensor_write_reg(addr, reg); // 重新发送起始条件、设备地址+读位 // 读取高字节和低字节 uint8_t hi = i2c_read_byte(); uint8_t lo = i2c_read_byte(); return ((int16_t)(hi << 8 | lo)); } float temp_lsb = 0.0625f; int16_t raw_l = sensor_read_temp(0x48, 0x00); float local_temp = raw_l * temp_lsb;实际操作中,远程温度寄存器不止2字节,还会伴随一个状态字节,用于区分数据是否有效、是否发生短路或开路。我在读取远程温度后,优先检查状态寄存器,如果远程故障标志为1,就不更新温度缓存,保留上一次有效数据,并上报故障状态。这样不会因为偶发干扰导致控制逻辑突然认为温度飙升,执行了错误的停机动作。
3.2 温度数据换算与滤波算法
PJ85718DM输出的原始温度数据是带符号的16位字,其中高12位为有效数据,低4位通常读出为0,所以每个LSB对应0.0625摄氏度。换算很简单:local_temp = raw / 16 * 0.0625。数值为负时,十六进制原码按补码解释,符号位会自然扩展到16位。比如-10摄氏度会显示为0xFF60,除以16后是-1600,乘以0.0625也就是-10。
但直接使用单次测量结果做控制并不合适。HVAC现场的风机振动和继电器吸合会产生瞬态干扰,有时单次转换值会跳个0.25甚至0.5摄氏度。如果这个跳动恰好接近阈值边界,就会造成报警频繁翻转。我做了两级软件滤波:先对一个周期的原始样本做5点中值滤波,去掉最大和最小两个毛刺,再对中值结果做8次滑动平均。实际效果是,稳态温度波动从原来的±0.3摄氏度压缩到了±0.1摄氏度以内,而报警响应延迟只增加了约1秒,完全可接受。
滤波的代价是数据滞后。我测试过一种极端场景:把远程二极管用热风枪加热,温度每秒上升约2摄氏度,原始读数几乎实时能跟上,而滤波后的数据大约滞后1.5秒。对HVAC这种大热惯性系统来说,这个滞后无所谓,但如果把同一套代码放到快速瞬态的功率板保护上,就要适当减小平均窗口,甚至直接用原始数据触发硬件ALERT中断,不用软件滤波。
3.3 本地与远程温度数据处理流程
我把温度采集设计成了一个1秒的软件周期定时任务。定时器每500ms触发一次中断,置标志位;主循环检测到标志位后发起I2C转换启动,再等待转换完成标志,然后读取本地温度和远程温度。读取的数据送入滤波链更新缓存,同时检查ALERT中断标志。
在保护逻辑里,我并没有完全依赖软件阈值。PJ85718DM的ALERT引脚会直接根据芯片内部的上下限寄存器比较阈值,一旦超限,硬件上立即拉低MCU的外部INT引脚。这个响应时间在毫秒级别,不受软件循环或协议栈阻塞影响。这相当于给功率板买了一份硬保险。实际调试时,我用加热枪将远程温度升到设定阈值,ALERT引脚从正常到拉低的延迟大约只有几毫秒,MCU中断服务函数里立刻把PWM输出钳到了安全值。
数据缓存部分,我定义了一个温度结构体,保存本地温度、远程温度、温度状态字、最后更新时间戳。RS485通信任务和以太网任务都从这个结构体里取值。这样数据生产者只有定时任务一个,数据消费者有多个,从设计上避免了并发写入和读取互相踩内存的问题。
3.4 远程通信的实现思路(Modbus RTU / MQTT)
RS485远程通信我选择了Modbus RTU从机模式。主控串口波特率9600,8位数据、无校验、1停止位。MCU收到查询帧后,先做地址匹配,再计算CRC16校验,校验通过则按照寄存器映射表返回数据。寄存器映射我做了精简:本地温度放从机地址0x1001,远程温度放0x1002,状态字放0x1003,这样上位机不需要理解我们的内部协议就能整合进楼宇自控系统。
Modbus从机响应时间需要特别注意。上位机通常以几十毫秒为周期轮询多个从机,如果单从机响应太慢,整个总线周期会被拖长。我的做法是:温度数据全部预存在内存中,Modbus请求到来时直接查表返回,不执行任何I2C读取。这样哪怕总线上同时挂16台设备,每台响应时间都稳定在10ms以内。
以太网远程上报这部分,STM32F207VGT6留了丰富资源。我把采集到的温度按照简化版JSON格式发送到本地监控服务,格式类似:{"local_temp": 32.5, "remote_temp": 58.2, "alarm": false}。通过TCP连接定时上报,网络断开时自动重连。由于数据量很小,以太网负担极低,主控的负载率始终控制在20%以内。在后续升级中,这套框架还可以直接换成MQTT协议上云,逻辑不变,只是将网络库替换一下。
4. 常见问题与排查技巧实录
4.1 传感器读数异常:0xFF、乱跳
第一个容易踩的坑是I2C读不到设备,返回0xFF。我一度怀疑芯片损坏,后来用示波器测量SCL和SDA波形,发现SDA在通信过程中始终呈高电平,没有拉低,最后定位到是I2C地址写错了。PJ85718DM的地址不是0x48,我当时错误地以A0=0配置计算成了0x49。改回0x48后一切正常。所以新拿到一颗芯片,第一步务必读一次ID寄存器,不要主观猜测地址。
第二个问题是温度读数跳变。如果你看到本地温度在0.25摄氏度范围内随机跳动,先检查MCU和传感器共地的地回路是否足够低阻抗。HVAC柜里接地线较长,数字地上的噪声会造成测温不准。另外在传感器VCC和GND之间直接加一个1μF电容,也能明显改善读数稳定度。我试过把传感器放在一块强力开关电源附近,不加电容时温度漂了0.2摄氏度,加上后恢复正常。
4.2 远程温度测量偏差过大
远程温度偏高或偏低是很常见的。首先核对远程二极管的连接极性。有一次我把三极管的基极和集电极短接后接反,读出来的温度始终比实际低5摄氏度左右,这个偏移并不明显,很容易被当成校准误差。其次注意远程线束的电阻,如果串联电阻超过几十欧姆,传感器内部的电流源会把额外压降算进PN结电压,造成读数偏高。
如果做了结构确认后还是存在固定偏差,可以考虑软件补偿。我用恒温槽做了三点标定,分别测0、25、60摄氏度,然后用最小二乘法拟合偏移曲线。对于HVAC场景,这种简单线性补偿已经足够,精度能做到±0.5摄氏度以内。远程测温本身更偏重趋势监控和超限保护,不建议用它的绝对值去做精确计量。
4.3 系统通信不稳定
RS485通信偶尔丢帧或误码,排查顺序应从物理层开始。第一,确认总线两端是否有且只有两个120Ω终端电阻;第二,检查所有从机的供电地和总线地是否有电位差,如果使用了非隔离电源,共模电压会沿着通信线形成回路;第三,调试收发器方向控制,我最初在发送完数据后立即关闭发送使能,导致最后一个字节被截断,后来改成发送结束后延时半个字节时间再关闭,问题就消失了。
还有一个容易被忽视的点:MCU的UART接收采用DMA模式时,如果接收缓冲区没有及时清空,新的帧进来会和旧数据粘连。我建议在DMA半满和全满中断里及时搬运数据,或者采用逐字节中断接收配合状态机解析。对9600波特率来说,逐字节中断完全来得及,而且逻辑更简单,不容易出错。
4.4 经验速查表
| 现象 | 排查方向 | 解决方法 |
|---|---|---|
| I2C读不到应答 | 地址、上拉、供电、焊接 | 先读ID寄存器,用逻辑分析仪抓波形 |
| 温度乱跳 | 电源纹波、远程线干扰 | 加去耦电容、远程线屏蔽、软件中值滤波 |
| 远程温度偏移大 | 二极管极性、串联电阻 | 校验接线,软件线性校准 |
| RS485丢帧 | 终端电阻、共模电压、方向切换 | 加终端电阻、隔离电源,发送后延时关断 |
| 以太网重连慢 | 不支持自动协商 | 固定为100M全双工 |
| ALERT不触发 | 阈值寄存器格式错 | 确认温度阈值与数据格式一致,左移4位写入 |
我在实际项目里踩过最痛苦的一次是这个组合问题:传感器数据偶发跳变、RS485偶发丢帧、以太网偶发断连,三个问题同时出现,排查了整整两天。最后发现根因是现场电源中存在高频开关纹波,通过DC-DC传导到了MCU和传感器,导致所有外围信号质量都下降。在电源入口加了一级π型滤波和屏蔽后,三个问题同时消失。所以遇到多路信号共同异常,先怀疑源头电源,不要各自孤立地查传感器和通信接口。
最后分享一个调试技巧:给温度采集任务加一个调试计数器,每次I2C通信失败或状态异常就自增,通过串口打印。这个计数器的价值远高于"最后一次读取值"本身,再配合Modbus的状态字反馈到上位机,现场运维人员才能快速定位是通信断开还是传感器本身故障。我在新项目的软件框架里把这个经验直接固化了,也算这次实践留下的一个长期收益。