做HVAC控制器那段时间,我几乎每天都泡在温度数据里。风管机回风温度差个两三度,压缩机的启停逻辑就会乱套;嵌入式设备里MCU自身发热飘个三四度,误报警也会跟着来。最近帮一个项目做机组控制板,主控选了STM32F410RB,外部温度采集用上了PJ85718DM数字温度传感器,在同一个I2C总线上把本地和远程温度一起解决掉了。这篇文章把这次从硬件选型、传感器布局、驱动实现,到现场调试验收踩过的坑全部整理出来,如果你也在做类似嵌入式设备或HVAC系统的温度监测,可以直接参考这套思路。
1. 需求定位:这套温度监测到底在解决什么问题
1.1 嵌入式和HVAC对温度数据的要求其实不一样
嵌入式设备里的温度监测,本质上是给系统自我保护用的。主控芯片结温过高、板上某个功率器件局部过热,就需要尽快降频、切断负载或者触发报警。这类场景对温度采样的绝对精度要求不算苛刻,正负两三度都能接受,但要求响应快、读数稳定、不能丢数据。
HVAC系统则完全相反。回风温度、出风温度、盘管进出口温度这些数据直接参与控制逻辑,压缩机启停、电子膨胀阀开度、风机风速都要根据温度来定。回风温度差个两度,房间的舒适度就会明显跑偏。更重要的是,HVAC的温度传感器很少紧挨着控制板,通常装在风管里、房间内或者机组外部,走线距离从几十厘米到几十米都有可能。传感器离得远,精度和稳定性还不能打折扣,这就是整个方案的难点所在。
我这次的项目就是把两类需求放在同一块控制板上解决。本地温度用STM32F410RB内部温度传感器直接读芯片结温,提供快速的过热保护;远程温度用PJ85718DM通过I2C总线采集,装在风管探头里,负责给HVAC控制逻辑提供准确的环境温度数据。一个系统同时覆盖了嵌入式保护和HVAC环境监控两类典型场景。
1.2 为什么选STM32F410RB加PJ85718DM这套组合
选主控的时候,需求其实很具体:要有足够多的I2C和UART、内部自带温度传感器、支持低功耗模式,最好还带硬件浮点单元,方便后面做滤波算法。STM32F410RB在这些条件上基本都能满足。它跑在100MHz,配Cortex-M4F内核,带FPU,做温度换算和滑动平均非常顺手,容量对这类控制板也够用。
选PJ85718DM的思路也很直接。它是一颗数字输出的温度传感器,I2C接口,测温范围覆盖HVAC常用区间,分辨率和精度对标常见的LM75系列,而且功耗很低。对比NTC方案,省掉了ADC通道和查表标定;对比热电偶方案,不需要冷端补偿和信号调理电路;对比PT100,不需要恒流源和高精度仪表放大器。用在HVAC这种需要长期稳定运行的环境里,数字传感器明显更省心。
我也在硬件设计初期反复犹豫过要不要直接用NTC,后来算了笔账:一路NTC要加分压电阻、滤波电容、去耦电容,还要在产线逐台标定,BOM成本看着低,实际工时的隐性成本很高。PJ85718DM这边外围就两个上拉电阻和一个去耦电容,出厂校准,精度直接写在手册里,量产时省掉了标定环节。这一项就省了不少事。
1.3 常用温度采集方案的对比与选型逻辑
把常见的温度采集方案放在一张表里看,选型逻辑会清晰很多:
| 方案 | 接口 | 精度 | 走线距离 | 成本量级 | 适合场景 |
|---|---|---|---|---|---|
| NTC热敏电阻 | ADC | 中等,需标定 | 短,模拟信号易受干扰 | 低 | 近距离、大批量、精度要求不高 |
| PT100/PT1000 | ADC加调理 | 高 | 中,需三线/四线补偿 | 中高 | 工业控温、高精度场合 |
| 热电偶 | 专用调理芯片 | 中高 | 长 | 中高 | 高温、宽温区测量 |
| 单总线类传感器 | 单总线协议 | 中 | 长(几十米) | 低 | 多点测温、远距离布线 |
| PJ85718DM这类数字传感器 | I2C/SMBus | 高 | 中短(1至2米内可靠) | 低中 | 板级测温、设备内远程测温 |
这次没选单总线方案,原因是单总线时序对延时比较敏感,尤其MCU中断多的时候容易抖动;I2C有硬件外设支持,配合中断和超时处理更稳。PJ85718DM这种I2C传感器在电压范围、精度、长期稳定性方面都比较契合嵌入式加HVAC的场景,所以我最终敲定了这套方案。
2. 硬件搭建与传感器布局
2.1 PJ85718DM最小电路怎么接
PJ85718DM的接线非常简洁:VCC、GND、SDA、SCL,外加地址选择引脚和ALERT引脚。电源用3.3V,与STM32F410RB同一路,VCC对地放一颗100nF陶瓷电容,紧挨芯片引脚。SDA和SCL分别通过上拉电阻接到3.3V,我最初选的是4.7kΩ,后来调试400kHz时会觉得余量不够,最后一版换成了2.2kΩ。
上拉电阻的计算其实不复杂。I2C的上升沿时间由RC决定,R是上拉电阻,C是总线电容,上升时间大概等于0.85乘以R乘以C。快速模式要求上升时间不超过0.3微秒。假设总线上挂两三个器件,走线几十厘米,总线电容在100到150pF左右,4.7kΩ算下来上升时间接近0.5微秒,余量不足;换成2.2kΩ以后就降到0.3微秒以内,波形干净很多。
地址选择引脚A0和A1可以接VCC或GND,组合出4个地址。我用了两颗PJ85718DM:一颗A0、A1都接地,7位地址是0x48,放在控制板上测环境温度;另一颗A0接VCC、A1接地,地址是0x49,放在风管探头里测远程温度。同一根I2C总线上挂两路传感器,读取时只要指定不同地址就行。
ALERT引脚是漏极开路,我加了10kΩ上拉,并接到STM32F410RB的一个外部中断引脚。温度上限写入配置寄存器之后,温度超标时ALERT会自动拉低,MCU即使在睡眠状态也能被唤醒进入报警处理。这个功能在无人值守的HVAC机组里非常实用。
2.2 本地和远程温度在物理上怎么分布
硬件布局上我做了明确划分。STM32F410RB放在控制主板核心位置,它内部的温度传感器用来监控芯片结温,这部分算是最“本地”的温度,不占用任何外围器件。控制板上另外一颗PJ85718DM放在板边靠近接线端子的位置,用来测控制箱内的环境温度,属于本地的环境温度参考。
真正的远程温度点在风管探头里。第二颗PJ85718DM焊在一小块独立PCB上,探头伸到回风管道中间,通过三芯屏蔽线与控制板连接。VCC、SDA、SCL各走一根线,屏蔽层单端接地。连接长度控制在0.8米以内,这样400kHz的I2C仍然可靠。如果风管距离控制板超过两三米,就不能直接这么拉线了,这个我后面专门写一节。
“板边本地传感器加管内远程传感器”的布局,好处是能同时看到三组数据:芯片结温、控制箱环境温度、回风管道温度。做故障诊断时价值很大。比如夏天高温天系统报“房间温度过高”,可以看芯片结温是否偏高导致误判,也可以对比控制箱环境温度和风管温度来定位是探头问题还是真实高温。
2.3 布局走线里容易翻车的细节
温度采集看起来简单,layout里的坑却不少。
第一,探头小板不要靠近风机驱动板或者变频器的功率输出端。强干扰会通过空间耦合进入I2C信号,即使数字信号不容易被打翻,也会出现偶发的传输错误。我这版PCB调试时遇到过温度读数每隔几百毫秒跳一次的情况,后来把探头小板挪了5厘米,现象就消失了。
第二,传感器旁边不要走大电流走线。大电流铜箔本身就是发热源,如果紧贴PJ85718DM的焊盘,测出来的温度会比环境实际温度高好几度。HVAC控制板上继电器、驱动管都可能集中发热,这些元件附近的温度普遍偏高。远程探头基于I2C数字信号,走线长一点问题不大,但探头本体一定要和热源拉开距离。
第三,风管探头要放在管道的直管段,别放在弯头、阀门或者风机出风口正对的位置。弯头附近气流会形成涡流,温度读数波动很大;风机出风口气流速度太高,温度响应也有延迟。我的探头放在离风阀至少5倍管径距离的直管段上,读数稳定很多。
第四,湿度高的环境要提前做防护。空调风管里冷凝水很常见,探头PCB不防水处理的话,微量水分会让相邻焊盘之间形成漏电通路,I2C信号被拉偏是小事,严重的会直接把传感器电源短路。我在探头小板焊完后均匀涂了一层三防漆,只留焊盘接口区域,现场跑了一个多月没有再出问题。
3. 驱动代码与温度算法实现
3.1 I2C初始化与PJ85718DM温度寄存器读取
代码层面我用STM32的HAL库做I2C初始化,400kHz快速模式、7位寻址、主机模式,I2C1直接接两颗PJ85718DM。
读取温度的核心代码很直接。PJ85718DM的温度寄存器地址是0x00,连续读两个字节:
#define PJ85718DM_ADDR_LOCAL (0x48 << 1) #define PJ85718DM_ADDR_REMOTE (0x49 << 1) #define REG_TEMP 0x00 #define REG_CONFIG 0x01 uint8_t buf[2]; HAL_I2C_Mem_Read(&hi2c1, PJ85718DM_ADDR_REMOTE, REG_TEMP, I2C_MEMADD_SIZE_8BIT, buf, 2, 50); int16_t raw = (int16_t)((buf[0] << 8) | buf[1]); float temp_c = (float)raw * 0.0625f;这里有一个关键点:如果芯片被配置成9位分辨率,低7位无效,需要右移后乘0.5才能得到温度;我用的驱动把分辨率配置成12位,低4位就是真实数据,直接乘0.0625即可。如果你手头的是9位模式,一定要先把原始值右移3位再换算,否则精度上不去。
读回来的数据还要做异常判断。数字温度传感器在探头短路或开路时,往往会返回特征值,比如0x7FFF表示超量程。我在读取函数里明确判断:
if (raw == 0x7FFF) { temp_c = NAN; sensor_status = SENSOR_FAULT; }上层逻辑收到NAN就会知道传感器异常,不会把0x7FFF当成一个真实的极端温度去触发误报警。这种防御性写法一定要加上,HVAC现场探头线缆被老鼠咬断、端子松脱都是真实会发生的故障。
配置寄存器我也顺手做了设置。ALERT工作在比较模式、低电平有效、12位分辨率。因为不同批次的寄存器位定义可能略有差异,配置时一定要按手头芯片手册逐位确认,不要照抄网上代码。
3.2 STM32F410RB内部温度传感器的校准读取
STM32F410RB内部温度传感器和ADC共用,采集时要配置ADC的通道18。内部温度传感器输出电压与温度近似线性,但每颗芯片的偏置和斜率都有差异,所以芯片出厂时在系统存储器里固化了两个校准值:TS_CAL1是30度时的ADC值,TS_CAL2是110度时的ADC值。
读取校准值的位置是固定地址。我手上这块F410RB用的是0x1FFF75B8和0x1FFF75C0。必须提醒一句:STM32不同系列、不同封装的校准地址可能不一样,比如F40x系列就是另一个地址。移植代码时第一件事就是打开参考手册核对,不然算出来的温度可能偏出几十度。
采集代码先把ADC配好:
ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_18; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; HAL_ADC_ConfigChannel(&hadc1, &sConfig); HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 100); uint32_t adc_val = HAL_ADC_GetValue(&hadc1);换算公式是核心,两个校准点之间做线性插值:
#define TS_CAL1 ((uint16_t)(*(uint16_t *)0x1FFF75B8)) #define TS_CAL2 ((uint16_t)(*(uint16_t *)0x1FFF75C0)) float chip_temp = 30.0f + 80.0f * (float)(TS_CAL1 - adc_val) / (float)(TS_CAL1 - TS_CAL2);TS_CAL1对应30度,TS_CAL2对应110度,跨度80度。当adc_val等于TS_CAL1时结果为30度;当adc_val等于TS_CAL2时结果为110度。这种写法直接利用了出厂校准,比手册里的典型平均斜率算准得多。
ADC采样时间我选了480个周期。F4系列内部温度传感器对采样时间有要求,采样时间太短会读到明显偏高的值。如果你发现内部温度传感器读数异常,第一个检查项就是采样时间。
内部温度传感器还有一个特性:它测的是芯片结温,不是环境温度。MCU功耗越大、运行越久,结温越高。不要拿它去和PJ85718DM的环境温度做比较判断故障,用途不一样。
3.3 滤波与数据处理的工程化处理
温度数据本身会有噪声。HVAC环境里气流波动、控制板电磁干扰、ADC采样抖动,都会让原始数据出现毛刺。直接把毛刺送给控制逻辑,会导致误判和频繁波动。
我做了两级处理:第一级是5点中值滤波,把偶发的奇异值去掉;第二级是滑动平均,窗口取8个点。中值滤波负责去毛刺,滑动平均负责平滑趋势,两者组合后,数据既不会出现尖刺,也不会滞后太多。实测下来,原本偶尔跳0.5度的数据波动降到0.1度以内。
滑动平均的代码很简单:
#define TEMP_HISTORY_SIZE 8 float temp_history[TEMP_HISTORY_SIZE]; uint8_t temp_index = 0; float moving_average(float new_temp) { temp_history[temp_index] = new_temp; temp_index = (temp_index + 1) % TEMP_HISTORY_SIZE; float sum = 0.0f; for (uint8_t i = 0; i < TEMP_HISTORY_SIZE; i++) { sum += temp_history[i]; } return sum / TEMP_HISTORY_SIZE; }中值滤波我直接用5点数组排序后取中间值,窗口很小,排序开销可忽略。注意一点:不要对远程温度传感器做太重的平滑。风管温度应该快速响应变化,比如房间热负荷突然上升,传感器滞后太久,空调系统的PID控制会震荡。8个点的窗口带来的滞后时间不到一秒,这个量级在HVAC控制里完全可接受。
4. HVAC场景下的远程温度监测设计
4.1 I2C远程布线的距离极限与延长方案
I2C总线是开漏结构,通信距离受总线电容限制,不是因为信号电压不够,而是因为上升沿时间不够。总线电容主要包括芯片引脚电容、PCB走线电容和线缆电容。普通排线或双绞线的电容大约在每米50到100pF,I2C快速模式规定总线电容不超过400pF。这样算下来,4米以上的线缆就很危险了。
现场项目里风管探头距离控制板0.8米,400kHz完全没问题。但如果某天改造,探头要拉到10米远,不能直接加长线。最常见的处理办法有三个。
第一个是降速。把I2C从400kHz降到100kHz,信号沿的要求放宽,实际能跑的线长确实会长一些,很多情况下五六米也能将就,但误码率会随着干扰增大而上升,不推荐作为长期方案。
第二个是加I2C缓冲器或中继器。市面上有专门的I2C总线缓冲芯片,把总线的电容分成两段独立计算。这样远程侧线缆可以做到几十米,代价是增加一小块电路。HVAC控制板上如果空间允许,加一个这类芯片是最稳妥的。
第三个是彻底换总线。传感器这边仍然用PJ85718DM,但通过一个小型变送模块把I2C转成RS485,RS485在几百米内都没有问题。这种方案适合传感器在室外机、屋顶等特别远的位置。我在另一个项目里就是这样做的,MCU通过RS485发一条简单指令,远端模块把温度以ASCII文本回传,代码里多一层协议,但温度数据反而更干净。
4.2 多点温度监测与报警联动
HVAC系统往往不是只看一个温度点。除了回风温度,出风温度、盘管进出口温度都要看。PJ85718DM这类I2C芯片只要地址不同,就能挂在同一条I2C总线上。A0和A1两个引脚可以产生4种地址组合,一个I2C外设最多挂4颗这样的传感器。如果传感器数量更多,可以加I2C多路选择器继续扩展。
我实际用了两路:第一路测回风温度,第二路测盘管温度。盘管温度比回风温度变化更快,直接决定压缩机频率的调节方向。两条总线分时读取,互不干扰。
报警逻辑也做了两层。第一层是软件轮询,每个采集周期比较当前温度和阈值,超过就报警;第二层是硬件报警,PJ85718DM的ALERT引脚接STM32F410RB的外部中断。把温度上限写入配置寄存器后,传感器自己会判断温度是否超限,不需要MCU每时每刻盯着。MCU休眠时,ALERT信号可以直接拉低唤醒。这套机制在散热风扇控制的嵌入式场景里同样通用。
4.3 低功耗与长期稳定运行的设计
STM32F410RB的功耗表现很好,HVAC控制器通常要求整机功耗低,MCU大部分时间可以进入睡眠,靠定时器周期性唤醒采集一次温度。每次测量的活动时间控制在几毫秒以内,其他时间睡在STOP模式,整机功耗就下来了。
PJ85718DM本身的静态功耗很低,长期供电没问题。但如果整机对休眠电流要求很苛刻,可以把传感器VCC用一个MOS管开关控制,只在采集瞬间上电。这里有个细节别忽略:I2C上拉电阻如果一直从3.3V连着,传感器掉电时SDA和SCL会被芯片内部ESD二极管拉到电源轨,MCU的GPIO会因此漏电。所以做低功耗时,上拉电阻最好也由同一个MOS管控制,或者改用GPIO模拟上拉。
长期稳定运行方面,我在固件里加了I2C总线恢复逻辑。如果现场强干扰导致I2C总线卡死,比如SDA被拉低,MCU会检测到超时,然后向SCL发送9个时钟脉冲,让总线上的器件释放SDA。这个操作在嵌入式I2C应用里非常实用,没有这一步,偶发的总线死锁只能靠重启控制板解决。
5. 实测数据与问题排查实录
5.1 实测数据表现
整套系统搭好后,我在实验室先做了一轮常温测试。控制板放在约25度的恒温环境中,PJ85718DM探头放在同一环境,两只标准水银温度计作为参照。
PJ85718DM读数稳定在24.8度,水银温度计显示24.7度,偏差0.1度。连续跑24小时,读数漂移控制在0.2度以内。STM32F410RB内部温度传感器测到的芯片结温比环境高了3.2度,这个数值符合芯片正常自热的预期。
把探头放进风管模拟装置,设定16度的低温气流,稳定后PJ85718DM读数是15.7度,水银温度计是15.5度。HVAC场景下这个精度完全够用。整套数据的波动范围,在应用层滤波之后基本落在正负0.1度以内。
这里再强调一次:内部温度传感器校出来的数据是封装结温,不是环境温度。如果项目要把“本地温度”当成环境温度去参与HVAC控制,得用控制板上的PJ85718DM;内部传感器更适合做芯片过热保护。
5.2 调试验收时最常踩的五个坑
第一个坑是I2C无应答。上电后读温度超时,示波器打上去发现SDA一直为高,SCL有脉冲,但就是没有ACK。查到最后是传感器探头的SDA和SCL线序接反了。HVAC现场经常有第三方提供的线束,颜色定义不统一,接之前一定要用万用表确认。
第二个坑是内部温度传感器算出来的温度离谱。我一开始按F405的校准地址写,算出来整整偏了几十度。后来翻参考手册发现F410RB的校准地址不同,改回来就正常了。这个坑属于典型的“手册没看仔细”。
第三个坑是温度读数周期性跳变。板子正常运行时,PJ85718DM读数每隔几百毫秒跳一次,幅度0.5度左右。用示波器看I2C波形,发现SCL上有明显的过冲和振铃,是长线和2.2kΩ上拉配合导致的反射。后来在探头侧串了两个33Ω的小电阻,波形干净了,数据也稳定了。这个属于信号完整性问题,在带长线的I2C测温项目里很容易被忽略。
第四个坑是风管探头放在弯头处,数据波动剧烈。风速变化时温度和参照值能差好几度,这不是芯片问题,是气流场问题。把探头挪到直管段后,波动立刻降下来。
第五个坑是湿度导致探头PCB表面漏电,I2C偶发错误。项目运行两周后报警频率变高,排查发现探头小板上有冷凝水。涂三防漆之后问题消失。这种环境问题在HVAC现场非常典型,提前做防护比事后检修省心太多。
5.3 现场问题速查表
| 故障现象 | 可能原因 | 排查顺序 | 解决办法 |
|---|---|---|---|
| I2C读取超时 | 地址错误、线序接反、上拉缺失、距离过长 | 量电压、示波器看波形 | 核对地址和线序、补上拉、加缓冲 |
| 温度读数大幅偏高 | 探头靠近热源、PCB热传导 | 红外温度枪现场比对 | 挪探头位置、远离发热器件 |
| 温度读数乱跳 | 气流影响、电源噪声、I2C振铃 | 看原始波形和滤波前数据 | 调整探头位置、加强滤波、串阻尼电阻 |
| 结温读数异常 | 校准地址错误、ADC采样时间不足 | 先做ADC自检 | 核对参考手册地址、加长采样时间 |
| 偶发总线死锁 | 强干扰、线缆过长 | 复现故障、抓总线状态 | 加总线恢复函数、换缓冲芯片 |
6. 后期扩展和一点个人体会
6.1 这套测温架构还能往哪些方向扩展
这套温度采集方案目前跑在HVAC机组控制板上,如果只看温度采集这一层,挪到其他嵌入式设备里也完全通用。比如储能设备的电池包温度监测、变频器功率器件结温监控、实验室恒温箱多点测温,逻辑都一样:主控选带内部温度传感器的低功耗MCU,外部按需挂几颗数字温度传感器,靠I2C组网,配好报警和滤波逻辑。
如果传感器数量超过4个,可以上I2C多路选择器,或者干脆换成RS485组网。PJ85718DM作为I2C节点,地址有限,但通过地址扩展芯片可以把节点数翻几倍。温度数据的上层协议也可以继续丰富,比如加Modbus寄存器映射,让上位机直接读到每个温度点的实时值。STM32F410RB的资源留了足够余量,后续加数据上报、远程诊断都不需要换主控。
6.2 一点个人体会
做完这个项目,我最大的感受是:数字温度传感器确实比模拟方案省心,但真正决定测量质量的不是芯片精度参数,而是传感器放在哪、怎么布线、怎么处理异常。芯片标称正负0.5度,那是芯片自身的性能;如果探头贴在发热元件旁边,信号线上又有强干扰,系统整体误差照样能到好几度。
所以在温度监测项目里,多花时间在探头位置和现场环境上,比纠结买哪颗芯片更有价值。这套PJ85718DM加STM32F410RB的方案,让我把“本地”和“远程”两个温度维度彻底理清楚了。以后再遇到HVAC控制板或者嵌入式设备需要多路测温,我基本可以直接照这套链路来设计,省掉大量试错时间。