搞过嵌入式温控的人基本都遇过这个问题:本地板温好测,远端探头信号一拉长就飘;数字传感器方便,但一条总线上带多了又怕地址冲突;模拟NTC便宜,可校准麻烦。PJ85718DM这颗I2C数字温度传感器,配合STM32F373VC这颗自带16位Sigma-Delta ADC的Cortex-M4 MCU,恰好能把本地温度和远程温度两路监测一起做扎实。这篇文章就把这个组合从需求拆解、硬件设计、固件实现到调试排障的完整思路写透,适合做嵌入式温度监控、HVAC控制器或者设备健康管理的开发者参考。
1. 这个选题要解决什么问题
1.1 这两种温度,监控思路完全不同
先说清楚“本地温度”和“远程温度”这里的含义。它指的不是“通过网页看云端温度”那种远程,而是在同一个控制系统里的两个物理位置:一路是控制器本身的板级温度或回风温度,通常距离MCU只有几厘米到十几厘米;另一路是装在风道、水管、机柜远端或设备表面的温度探头,距离可能从几十厘米到几十米不等。
这两路温度看起来都是“测个温度”,实际工程难度差很多。本地温度离主控近,信号路径短,干扰好控制,用数字传感器走I2C直接读即可,精度和一致性都不错。远程温度则要面对长线压降、电磁干扰、地电位差、接插件接触电阻等一系列问题,许多项目在这里翻车:读数来回跳、布线后标定值漂移、换一根线结果差两度。
这也是为什么HVAC行业里到现在还有大量系统坚持用模拟量变送器或者NTC探头做远程测温——数字传感器拉长线不稳定,模拟链路只要调理电路设计得当,抗干扰反而更好。但模拟方案有个让人头疼的地方:每一路都要做放大、偏移、标定,出厂校准的工作量很大。
所以,一个好的温度监测方案应该这样分工:本地温度走数字I2C传感器,稳定、免校准、直接出摄氏度;远程温度走MCU片内高精度ADC加NTC或变送器,用差分测量把长线干扰压下去。这个思路既不牺牲精度,又能把硬件做简单,还留出了很大的灵活性。
1.2 为什么是这个传感器加这颗MCU的组合
先说PJ85718DM。这类I2C数字温度传感器在市面上很多,选它的核心原因是几个特性组合起来适合做HVAC和嵌入式:内部集成温度ADC,出厂做过校准,上电就能读到稳定的温度数据,不需要像NTC那样自己标定;I2C接口支持地址引脚配置,一条总线上可以挂好几个,适合做多点多路测温;典型精度能做到±0.5℃级别,对楼宇自控、设备监控这些应用绰绰有余。除此之外,很多这类芯片还带ALERT报警输出引脚,可以当独立过温中断用,不用MCU一直轮询。
再来看STM32F373VC。选择这颗MCU不是因为它是多高性能的旗舰,而是它的模拟外设在中等价位MCU里非常能打。F373系列最大特色是集成了三路16位Sigma-Delta ADC,也就是SDADC。这种ADC支持差分输入,还带了可编程增益放大器(PGA)。差分输入意味着可以同时采样正负极信号,直接抵消两端的共模干扰,这正是远程NTC长线测温最需要的特性。普通MCU的12位逐次逼近ADC要处理一个0.5℃误差的远程温度,通常需要外部加仪表放大器,F373片内的SDADC就能把这件事干了,外围电路省掉一大半。
另外一个经常被忽略的点是,F373的主频只有72MHz,在今天看来不高,但它是带FPU的Cortex-M4内核。跑HVAC控制里常见的PID、温度查表、滤波算法,浮点运算都是硬指令,CPU占用率非常低。再加上256KB Flash和48KB SRAM,用Modbus RTU、私有协议、历史记录存储都够用。这颗MCU不是算力派,但“模拟采集+浮点计算+通信外设”的综合能力,特别适合做温控类产品。
2. 硬件设计:把本地和远程两路温度接进来
2.1 PJ85718DM 的接线、地址与使用要点
PJ85718DM的接线本身不复杂,但如果照着“随便接一下”的心态来,后面调试大概率要吃些苦头。供电上,通常支持3.3V或5V,推荐在VDD和GND之间放一个100nF陶瓷电容,再并一个1uF电容。很多工程师只放100nF,结果传感器供电上叠了高频噪声,温度读数在小数点后面不停跳。电容不是玄学,是给传感器内部ADC一个干净的电源基准。
I2C上拉电阻需要认真选。标准模式下用10kΩ问题不大,但如果你需要跑400kHz快速模式,或者总线上挂了两个以上传感器,建议换成4.7kΩ甚至2.2kΩ。上拉电阻太小会让总线灌电流偏大,太大则上升沿变缓,线一长就出现通信偶发错误。还要注意在一个PCB上如果同时挂了多个I2C设备,上拉电阻是统一的,如果其中某颗器件的输入电容特别大,整个总线时序都会被拖慢。
地址引脚A0、A1、A2的组合决定传感器在总线上的地址。以常见的寄存器布局来说,地址格式是1001加三位引脚状态,全部接地时是0x48,其他组合依次递增。通过三个引脚最多挂8颗同型号传感器,对绝大多数HVAC应用足够了。做板子时,这三个引脚最好用跳线或0欧电阻配置,别直接焊死,否则现场改地址非常麻烦。
ALERT输出引脚值得接出来。这个引脚是开漏输出,传感器内部温度超过上限或下限时它会拉低,可以直接接到MCU的外部中断引脚。HVAC系统里需要做超温保护时,这种硬件中断比软件轮询可靠,MCU死机或者任务延迟了,保护逻辑依然能触发。
传感器摆放位置比很多人以为的更重要。芯片自己会发热,虽然工作电流很小,但如果把它放在大功率电阻、线性稳压器或者变压器旁边,PCB铜皮会把热量传给它,测出来的“环境温度”比真实温度高好几度。我做过一版采集板,把传感器放在一个7805旁边,结果所有温度都稳定偏高3℃左右,后来移动位置才正常。放传感器之前先想一下板子上哪儿发热,这是省事最直接的办法。
2.2 远程模拟通道:用SDADC读NTC分压
远程温度最常用的低成本方案还是NTC加精密电阻分压,然后进ADC。市场上也有很多4-20mA温度变送器,那是远距离工业场景,这里先说NTC。
NTC选型很关键。推荐用10kΩ、B值3950的NTC,这是工业设备里最通用的规格,采购容易,替换方便。分压电路可以简单用上拉精密电阻加NTC到地的方式。以3.3V供电、上拉电阻10kΩ为例,25℃时NTC也是10kΩ,中点电压是1.65V;温度升高,NTC阻值下降,中点电压升高。这个单端接法虽然简单,但线一长就会受干扰。更好的做法是桥式差分接法:两条线从NTC两端引回MCU,接到SDADC的差分输入对上,让ADC直接测量NTC两端电压而不是对地电压。这样即便远端的地和控制器的地之间有电位差,这个共模电压也会被差分输入抑制掉,读数稳定得多。
STM32F373的SDADC配置有几个参数要理解。分辨率可以设成16位,但实际硬件有效位数约14位,不要指望真能到小数点后三位。PGA增益根据传感器输出电压范围选,NTC分压中心点电压在1.65V左右,增益选1基本够了。滤波配置上,SDADC内部有数字滤波器,可以选SINC3或快速滤波器。SINC3对噪声抑制更好,但响应稍慢;快速滤波器响应快,适合需要快速采样的场景。HVAC里温度变化本来就慢,用SINC3更合适。
从分辨率角度看,3.3V参考电压除上65536,一个LSB约50μV。NTC在25℃附近的灵敏度大约是每摄氏度变化440Ω,换算到分压输出大约每摄氏度36mV。也就是说ADC的理论分辨率能达到0.0014℃量级。实际会差一些,但做到0.01℃以上是没问题的,这对于HVAC的需求来说非常充裕。
2.3 板级布局与长线抗扰的三条经验
第一,强电和弱电要分开走。HVAC控制器里一般都有继电器、接触器、变压器这些强电元件,它们的开关动作会产生很大的瞬态干扰。温度传感器信号线严禁和强电线平行走长距离,实在避不开就用90度垂直交叉的方式跨越。PCBA上最好把数字电路、模拟电路、功率驱动三块区域分清楚,每一块单独铺地,再通过磁珠或0欧电阻单点连接。
第二,远程NTC引线用屏蔽双绞线。屏蔽层在控制器端单点接地,不要两端都接。两端接地容易形成地环路,工频干扰会通过屏蔽层感应进信号线,噪声反而更大。双绞的作用是让两条信号线受到的电磁干扰尽量一致,这样差分输入才能把干扰抵消掉。
第三,如果传输距离超过十米,就别再用NTC直接分压了。这么长的线,线路电阻会进入测量回路,而且工业现场可能还有变频器之类的强干扰源。这种情况老老实实上4-20mA温度变送器,在控制器端用一个精密电阻把电流转成电压再进SDADC。4-20mA环路的抗干扰能力是经过几十年工业现场验证的,省心得多。
3. 固件实现:从读寄存器到稳定控制
3.1 I2C读取流程与寄存器配置
PJ85718DM这类芯片的寄存器布局大同小异,有几个关键寄存器需要了解:温度寄存器地址0x00,配置寄存器地址0x01,上限和下限报警寄存器地址0x02和0x03。上电后默认通常是连续转换模式,可以直接读温度寄存器拿结果。如果需要更低功耗,可以在配置寄存器里改成单次转换模式,需要时发命令触发一次转换,读完再让它睡。
读取温度寄存器的代码看起来简单,但有一个细节容易错:I2C的7位地址在HAL库函数里要左移一位变成8位地址再传。很多人直接传0x48,结果一直返回读写失败,排查半天才发现是这里的问题。
#define PJ_ADDR 0x48 // 7位地址,由A0/A1/A2决定 #define PJ_REG_TEMP 0x00 // 温度寄存器 HAL_StatusTypeDef pj_read_temperature(float *temp) { uint8_t reg = PJ_REG_TEMP; uint8_t buf[2]; if (HAL_I2C_Mem_Read(&hi2c1, (PJ_ADDR << 1), reg, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) != HAL_OK) { return HAL_ERROR; } int16_t raw = (buf[0] << 8) | buf[1]; raw >>= 2; // 去掉低两位无效位 if (raw & 0x2000) raw -= 0x4000; // 14位有符号数扩展 *temp = raw * 0.0625f; // 默认12位精度,LSB=0.0625℃ return HAL_OK; }读取周期要合理设置。传感器的转换时间一般在几十毫秒量级,HVAC控制回路200ms到1s读一次足够,没必要每10ms读一次。频繁读取除了浪费CPU,还会让传感器一直处于活跃状态,自热略增大。低功耗设备建议1s采样一次,甚至更长。
总线稳定性方面,I2C一次读取失败不一定是硬件坏了,可能是总线时序被偶尔干扰。正确做法是连续读三次都失败才判定通信故障,单次失败做一次重试就行,不要一失败就把系统复位,否则现场会看到设备莫名其妙重启。
3.2 温度数据的滤波、校准与单位换算
传感器读回来的原始温度值不建议直接用。任何温度传感器都带有不同程度的随机噪声,直接送到控制逻辑会让继电器频繁动作。最简单有效的办法是滑动平均或者一阶低通滤波。一阶低通实现起来更省内存,代码量也小:
static float last_temp = 25.0f; float temp_filtered(float new_temp) { const float alpha = 0.2f; // 系数越小越平滑,但响应越慢 last_temp += alpha * (new_temp - last_temp); return last_temp; }alpha的选择要权衡响应速度和稳定性。HVAC里温度变化速率很慢,alpha取0.1到0.2都没有问题。但如果是做电机过热保护这类需要快速响应的场景,alpha就要放到0.5以上,或者干脆检测到突变时立刻旁路滤波。
系统标定这块经常被忽略。远程NTC的分压电阻即使精度标称1%,加上NTC本身的离散性,实测温度和真实温度之间可能差1到2℃。对于HVAC应用,这个误差会让用户体感不舒服。解决办法是在产线做两点标定:用一个标准温度探头,分别在约10℃和约50℃的环境里记录传感器的原始计算值与标准值的差值,然后拟合成一条直线,把校准系数存到Flash里。这样软件计算公式就变成:实际温度 = 滤波后温度 × 斜率 + 偏移量。
本地数字传感器也要考虑板级温升。它测的是芯片附近的PCB温度,不是空气温度。如果传感器装在密闭控制器内部,PCB自身发热会让它比设备外部环境温度高3到5℃。在软件里可以做一个固定的offset配置项,或者根据负载电流动态补偿。没有这个概念,很容易出现“设备显示温度不下降,但房间实际已经凉了”的尴尬状况。
3.3 HVAC控制判据:滞环与温差诊断
温度采集只是第一步,真正体现系统价值的是控制逻辑。HVAC里最基础也最重要的控制策略是滞环控制,它解决的问题是:如果温度到了设定值就停,稍微偏差又启动,继电器和压缩机就会频繁启停,寿命大打折扣。正确的做法是让控制目标有一个回差带。
#define HYSTERESIS 0.5f void hvac_control(float temp, float target, uint8_t mode) { if (mode == HEAT) { if (temp < target - HYSTERESIS) relay_on(); else if (temp > target + HYSTERESIS) relay_off(); } else if (mode == COOL) { if (temp > target + HYSTERESIS) relay_on(); else if (temp < target - HYSTERESIS) relay_off(); } }这个滞环带宽0.5℃是经验值,制冷制热都适用。带宽太小会让设备频繁起停,太大则温度波动明显,用户会觉得忽冷忽热。实际产品里这个值最好做成可配置,给售后调试留空间。
把本地和远程温度放在一起看,还能做很实用的诊断功能。以中央空调系统为例,本地温度检测回风温度,远程传感器检测出风温度。正常情况下,制冷模式出风温度应该比回风温度低8℃以上,如果出风温度和回风温度差很小,说明换热效率下降、滤网堵塞或者系统冷媒不足。这个温差判断逻辑写不了几行代码,却能在故障发生之前提前预警,是HVAC产品很实用的卖点。
4. 实际调试中碰到的坑和排查记录
4.1 I2C总线上SDA被拉死
这是I2C调试最常见也最让人头疼的问题。现象是系统运行一段时间后,传感器不再响应,用示波器一量发现SDA线一直低电平。原因通常是通信过程中被干扰打断,或者从机在传输中发生异常状态,导致总线状态机卡在某个位,SDA被从机持续拉低。
复位MCU不一定能解决,因为SDA锁死是总线状态问题,MCU复位后它还是不理你。正确做法是对总线做一次“时钟恢复”:把SCL和SDA都配置成普通开漏输出,手动拉高释放,然后给SCL打9个以上的脉冲,让卡住的从机把剩余数据位走完,最后发一个STOP条件。这相当于强行把总线状态机清回空闲状态,代码逻辑大约是:
void i2c_bus_recover(void) { // 把SCL和SDA临时配置为开漏输出,并置高 // 循环9次:SCL拉低、延时、SCL拉高、延时 // 结束后再发一个STOP条件 // 最后重新初始化I2C外设并恢复读操作 }如果环境干扰非常严重,时钟恢复也不管用,就要在硬件上给传感器增加硬复位手段。最可靠的办法是用MCU的一个GPIO控制传感器的电源,发现I2C连续通信失败时断电100ms再上电。设计的时候就把这个控制引脚留出来,不要图省事把传感器电源直接接在3.3V上。
平时写代码也要做好超时保护。HAL库的I2C函数都带超时参数,别填一个很大的值,建议100ms以内。一旦超时就调用总线恢复函数,然后把本次温度数据标记为无效,用上次的有效值继续控制。这样即使总线瞬断,系统也不会产生剧烈动作。
4.2 本地传感器读数被“自热”污染
有段时间我在调试一块控制板,PJ85718DM读出来的温度一直比用红外温度计测得的环境温度高了1.5℃左右。起初以为是传感器不准,后来发现是PCB布局问题。那颗传感器旁边正好是一个给继电器线圈供电的驱动器,芯片持续工作了几十分钟之后,那块区域的PCB温度明显升高,传感器测的是板温而不是空气温度。
数字温度传感器的自热通常很小,数据手册上一般会给出热阻参数,在静止空气中大约有零点几度的自身温升。但当传感器紧贴发热器件时,这个误差会被放大十倍。解决办法有几个层面:第一,PCB布局时传感器尽量远离功率器件和电源模块,最好悬空在板边;第二,如果必须靠近热源,传感器正下方的PCB不要铺大铜皮,避免热量直接传导;第三,在传感器上方加一个小散热帽或者导热泡棉,让它接触被测空气而不是被周围元件烤热。
软件侧也要留一手。产品出厂前做温度对比测试,在恒温箱里用标准探头和传感器在同一位置读温度,把差值做成一个校准曲线存进固件。这种方式不能完全抵消局部温度梯度,但能把系统误差控制在用户可接受范围内。
4.3 远程NTC长线噪声与引线电阻误差
远程NTC最常见的调试现象是温度读数在小数点后乱跳,或者与实际温度明显偏低。先说说偏低的问题:如果用的是两线制接法,引线电阻会和NTC串联,温度越高NTC阻值越低,引线电阻占据的比例越大,读数误差越明显。100米0.5平方毫米的铜线来回就有接近7Ω电阻,在10kΩ的NTC电路里,这个阻值会造成大约0.5℃到1℃的误差。
解决引线电阻的办法是换成三线制接法。三线制用一根额外的引线去测量线路电阻,软件里做补偿。当然,最彻底的四线制对温控应用来说成本太高,一般不用。如果只是几米内的短引线,用精密一点的NTC分压电阻,线阻影响不大,可以不处理。
温度跳动的根源大多是电磁干扰。HVAC机箱里继电器动作的时候,触点火花会通过空间辐射和电源线传导,在长线上感应出几百毫伏的尖峰。这时即使NTC信号进了SDADC,噪声依然存在。我是这么处理的:尽量缩短模拟采样线在PCB上的路径;在差分输入端各加一个小电容到地做高频滤波,容值在1nF到10nF之间;软件里采样不要只采一次,连续采32次或者64次再做平均。STM32F373的SDADC采样率很高,多采样几次平均并不耗时。
另一个容易被忽视的点是屏蔽层接地。一定要记住,屏蔽层在控制器端单点接地,不要两端都接。我见过两端的屏蔽层被接出明显干扰的情况,屏蔽层反倒变成了天线。
5. 方案还能延伸到哪里
5.1 从单机测温到RS485/Modbus组网
一个控制器带几路温度只是起点,实际工程项目里,数据经常会需要汇总到上位机或者触摸屏。STM32F373VC有多个USART,硬件上接一个RS485收发器就能组成Modbus RTU网络。把本地温度、远程温度、传感器状态作为保持寄存器开放出去,上位机就能随时读取,掉电后设置参数也能存在Flash里。
这类组网方案在楼宇自控里非常成熟,需要注意的是RS485总线两端各接一个120Ω匹配电阻,总线长度超过几百米时要在软件里适当降低波特率。9600波特率在工业现场是最稳妥的选择,虽然速度慢,但Modbus RTU一次读几十个字节也没多少数据量,9600完全够用。
5.2 低功耗:让系统只在关键时刻醒来
很多测温设备不是一直插电的,比如冷链运输记录仪、电池供电的温湿度传感器。这类设备要把功耗抠到几微安级别,方案就要调整。STM32F373在低功耗模式下电流很低,思路是这样:MCU进Stop模式,传感器休眠,定时器每30秒唤醒一次,给传感器发单次转换命令,等转换完成读温度,然后继续睡。如果传感器带了ALERT报警功能,还可以让它在温度越限时直接唤醒MCU,平时MCU根本不启动。
这种低功耗策略对HVAC产品意义稍微小一些,但对嵌入式监控设备很有价值。产品经理说要电池用半年还是用两年,往往不是电池容量差异,而是固件有没有把功耗管理做到位。
5.3 用温度趋势做简单的设备预诊断
当系统能把本地和远程两路温度历史记录下来,这些数据可以做一些很实用的趋势判断。比如散热风扇老化后,同一负载条件下远程温度会缓慢升高;风道滤网堵塞时,进出风温差会逐渐变小;电机轴承磨损摩擦增大,表面温度会有规律地波动。这些模式用不到什么复杂的机器学习算法,在MCU里做几个滑动窗口的统计判断就行。
温度本身就是设备最诚实的“体检报告”。把监测数据和设备维护流程结合起来,系统就从“坏了再换”变成“快要坏之前提醒”,这对冷链、机房、暖通设备这类不能停机的应用价值非常大。我在实际项目中体会最深的是,这套方案真正的优势不在某一项指标多极致,而是把数字传感器的便利性和芯片级高精度模拟采集结合到了一起,让本地和远程温度能用同一套简单可靠的架构去处理。如果再让我重新选一次组合,我还是会这样配,只是第一版板子上的传感器位置,我一定会更早去认真规划。