做嵌入式或者HVAC开发这些年,我一直觉得“温度”这个量特别有意思。看起来人人都能测,真正想在项目里用得稳、用得准,却涉及一堆容易被忽略的细节:传感器离主控远不远、走模拟还是数字、需不需要校准、异常了怎么处理。这几个问题不提前想清楚,后面调试大概率会返工。这次分享一个实际做过的方案,用STM32L432KC做主控,搭配PJ85718DM这颗数字温度传感器,同时监测本地温度和远程温度,应用方向是嵌入式控制器和暖通空调设备。整套方案不复杂,但里面有不少值得记录的选型逻辑、固件写法和踩坑经验。
如果你正准备做一个需要“多点测温加上位控制”的项目,不管是空调风管温度监测、机房温度告警,还是板级过热保护,这篇文章应该能给你一个可以直接参考的框架。
1. 需求拆解与方案选型
1.1 这个项目到底要解决什么问题
在HVAC设备里,最常见的温度需求有两类。一类是“设备自身安全相关”,比如控制板是否过热、散热风扇有没有失效,这类通常需要测的是控制器附近的板级温度。另一类是“环境控制相关”,比如风管内温度、房间回风温度、室外冷凝器温度,这类测量点往往离主控板有一段距离,甚至在几米开外的设备内部。
这两类需求放一起,就天然形成了“本地温度”和“远程温度”两个概念。本地温度可以理解为主控自身能感知到的温度,远程温度则是通过传感器从远端采集回来的温度。做设计的时候,最忌讳的是把两种温度混在一起处理,因为它们在精度要求、响应速度、故障模式上都不一样。这个项目里,本地温度直接用STM32L432KC内部的温度传感器,远程温度则通过I2C总线读取PJ85718DM。这样分工清晰,后续写控制逻辑时也不会糊涂。
1.2 为什么本地温度选STM32L432KC内部传感器
很多人一听MCU内部温度传感器,第一反应是“这东西不准吧”。确实,内部温度传感器测的是芯片结温或者说管芯温度,跟真实环境温度差距可能很大,用来做精确的环境测温并不合适。但回到需求本身,本地温度要的是“板卡本身是不是过热”,那么内部传感器恰恰是最合适的,因为它直接反映了芯片实际的热状态。
STM32L432KC这颗MCU是Cortex-M4系列,低功耗性能都不错,内部集成温度传感器,也预留了工厂校准数据。把这些校准值读出来,做线性换算,能得到一个可用的板级温度参考。更关键的是,这个传感器零成本、零布线,不用额外占用PCB面积,在HVAC这类成本敏感的产品里非常有吸引力。实际使用中,我用它监测主控板温升,一旦温度超过阈值就触发降额保护,效果很直接。
1.3 远程温度传感器为什么选PJ85718DM
PJ85718DM是一颗I2C接口的数字温度传感器。选它主要看中三点。
第一,抗干扰能力强。HVAC设备里常有风机、压缩机、继电器等干扰源,模拟输出的NTC方案在这种环境里需要做滤波和校准,而数字传感器直接在探头端完成模数转换,通过I2C传回来的已经是数字量,传输过程基本不受模拟噪声影响。
第二,精度和分辨率足够。这颗芯片在常用温度范围内精度能做到±0.5℃左右,分辨率最低位是0.0625℃,对HVAC控制来说已经非常够用。温度趋势判断很灵敏,控制逻辑不容易因为量化误差来回抖动。
第三,使用灵活。它支持地址可配,一条I2C总线上可以挂多颗,用于多个区域测温。另外还带ALERT告警输出,可以接MCU中断引脚,温度越限时主动上报,不用主循环一直轮询。
对比过NTC分压方案和模拟运放调理方案之后,数字方案虽然在传感器单价上稍微贵一点,但省掉了校准流程、分压电阻和滤波元件,整机综合成本反而更低,生产一致性也更好。
2. 硬件连接与调试准备
2.1 电源、I2C引脚和上拉电阻
PJ85718DM供电直接用3.3V,和STM32L432KC同一个电源轨,地线必须共地。I2C的SDA接到PB7,SCL接到PB6,这是STM32L432KC上I2C1的常用映射。两条线都要接上拉电阻到3.3V,我第一次用的上拉是4.7kΩ,工作在标准模式100kHz和快速模式400kHz都比较稳定。
上拉电阻的取值不能随意。阻值太大,信号上升沿会变缓,在长线缆场景下容易误码;阻值太小,总线驱动功耗增大不说,还可能把低电平电压拉太高,导致从机无法正常拉低。对大多数板内走线和1米以内的线缆,4.7kΩ到10kΩ一个很好的起点。如果总线设备特别多,还需要根据总电容重新计算。
2.2 长距离布线时的抗干扰处理
HVAC应用里,远程传感器经常要装到风管内部或者空调机组里,和主控板的距离超过1米非常普遍。这种时候,杜邦线加面包板的方式只能用在实验室验证,实际安装必须用双绞线,最好带屏蔽层,屏蔽层单端接地。
I2C本身是为板内通信设计的,线拉长之后信号完整性会下降。我实际用的线缆长度大约2米,采取了三个措施保证通信稳定:一是把I2C速率从400kHz降到100kHz;二是传感器供电线和信号线分开走,不要跟220V驱动线捆在一起;三是在主控端加一个很小的RC低通滤波,滤掉高频耦合噪声。
2.3 地址、ALERT中断和总线扩展
PJ85718DM的地址由地址引脚电平决定,单个传感器时默认地址一般就够了。如果系统里要挂多颗传感器,那么每颗的地址跳线必须提前规划,PCB上要留出可改的电阻位或者焊盘。我见过一个项目因为多颗传感器地址没区分,I2C应答冲突,数据读出来时好时坏,排查了整整一天。
ALERT引脚建议一定要接。STM32L432KC的PA0可以配置为外部中断输入,PJ85718DM温度越限时会在ALERT脚上产生一个低电平信号,MCU收到中断后立刻读取状态寄存器,再决定是否进入保护逻辑。这样做的好处非常明显:温度异常时不需要等主循环跑到轮询点,响应延迟可以做到毫秒级。
3. 本地温度采集:STM32L432KC内部传感器
3.1 CubeMX里的ADC配置
STM32L432KC内部温度传感器是通过ADC采集的。打开STM32CubeMX,选择ADC1,在通道配置里选择温度传感器通道,同时使能内部参考电压VREFINT。ADC分辨率设成12位,采样时间尽量设长一些,这样可以减小内部采样电容带来的测量噪声。温度采样的实时性要求不高,我没开DMA,直接用轮询方式,代码简单,问题也容易排查。
配置完成后生成工程,HAL库会自动初始化对应的ADC、GPIO和配置寄存器。这里有个特别容易遗漏的点:只开启了ADC通道,但忘记使能温度传感器的内部电源,读回来的值会一直是0或者异常值。检查CubeMX中的ADC配置页面,确认温度传感器使能位已经勾上。
3.2 校准数据读取和温度计算公式
STM32L432KC出厂时存储了两个温度校准点,分别是30℃和110℃下的ADC读数,存放在专门的信息存储区。计算温度时用线性插值:
温度 = (TS_DATA - TS_CAL1) × (110 - 30) / (TS_CAL2 - TS_CAL1) + 30
代码里把两个校准值读出来后存成全局变量,开机初始化阶段做一次读取就够了。不要用数据手册里的典型电压值硬算,不同芯片个体差异会导致好几度的误差。校准值读取是一段比较绕的代码,因为要从特定Flash地址读取,需要转换成const指针,但原理其实就是读两个32位无符号整数。
3.3 ADC读数稳定化和降噪
实际调试中我发现,MCU在运行负载比较重的时候,内部温度传感器的读数会跳动,最明显的是每隔几百毫秒出现一个±1℃以上的毛刺。这类毛刺不是传感器坏了,而是芯片内部发热和供电噪声叠加引起的。
我采用的方案是滑动平均:连续采样16次,去掉最大和最小值,剩下的取平均。这样处理之后,温度曲线平滑很多,HVAC逻辑也不会因为瞬时毛刺出现误动作。如果对响应速度有要求,可以缩短到8次取中位数,但实时性和平滑度要自己权衡。内部温度检测的是热趋势,慢一点没关系,平滑比快速响应更重要。
4. 远程温度采集:PJ85718DM读取与解析
4.1 寄存器结构和读取命令
PJ85718DM上电后默认开启连续转换,所以我并没有做太多配置,直接读取温度寄存器就行。核心操作是先写寄存器指针到0x00,然后发起读操作,连续读取两个字节温度数据。如果项目里需要调整分辨率或者打开告警阈值,还要操作0x01配置寄存器,设置对应的阈值数据和比较模式。
这里建议把地址和寄存器指针这些常量用宏定义写清楚,后面维护代码的时候不会一坨数字满天飞。温度读取是个高频操作,代码结构上我只是写了一个独立函数,返回浮点温度值和通信状态,主循环里每100ms调用一次,逻辑清晰。
4.2 HAL库读取代码和温度转换
下面是我实际使用的读取代码,按HAL库风格写的,可以直接在这个芯片上跑通:
#define PJ85718DM_ADDR 0x48 #define PJ85718DM_TEMP_REG 0x00 uint8_t buf[2]; int16_t raw; float temperature; if (HAL_I2C_Mem_Read(&hi2c1, (PJ85718DM_ADDR << 1), PJ85718DM_TEMP_REG, 1, buf, 2, 100) == HAL_OK) { raw = (int16_t)((buf[0] << 8) | buf[1]); raw >>= 2; // 13位分辨率下低2位是无效位 temperature = raw * 0.0625f; // 1 LSB = 0.0625℃ } else { // 通信失败处理,见4.3 }一个容易出错的细节:buf[0]是高位字节,符号位就在里面。要先拼成int16_t再右移,不要先拼成uint16_t再移位。否则负温度下会变成很大的正数,符号位扩展完全错误。这个坑我踩过一次,调试低温测试时发现温度显示成了几十度的高温,最后检查就是数据类型没转对。
4.3 断线、异常值和故障安全策略
HVAC系统里,传感器故障引发的误动作比温飘更讨厌。线缆断了、接口氧化、传感器损坏,读回来的数据往往是一些极限值,比如-300℃或者异常跳变。如果控制逻辑不加以判断,系统可能以为环境温度极低,持续开启加热,长时间运行后果很严重。
我做了两道防护。第一,每次I2C读取都检查返回值,连续5次通信失败就认为传感器掉线,置故障标志。第二,对温度值做合理性检查,超出PJ85718DM量程或者突变量超过每秒5℃的数据直接丢弃,使用上一次有效值。故障期间,温控逻辑切换成安全模式:不参与自动调节,风机维持预设低速,同时上报告警。这套策略看起来简单,但实际价值极大,能避免很多现场事故。
5. HVAC控制逻辑与温度数据融合
5.1 滞回控制:避免执行器频繁启停
空调温控最经典的控制方式就是滞回控制。目标温度25℃,设置回差1℃,远程温度高于26℃时开启制冷,低于24℃时停止制冷。这里的核心思路是给启停阈值之间留一个缓冲区,避免温度在设定点附近微抖时,继电器或接触器频繁吸合。
滞回控制实现起来不难,但要注意参与比较的温度值到底是哪个。房间舒适度取决于远端风道温度,所以主控制量用的是PJ85718DM的读数;本地STM32内部温度则作为设备保护条件,比如本地温度超过70℃时,即使远端温度没超限也要强制降功率或者停机。两层温度各司其职,控制逻辑就非常清晰。
5.2 转速调节:简单PID整定思路
如果控制对象是变频风机或电动风阀,光靠滞回就不够了,需要连续调节。我用的是一个很常见的PID结构,输入量是远端温度与目标温度的偏差,输出量是PWM占空比或0到100%的阀门开度。
参数整定上,我习惯先只用比例项,让系统出现临界振荡,记录振荡周期,再根据经验公式引入积分和微分项。温度对象惯性大,微分作用加得很保守,加太多反而容易在设定点附近来回振。我最终用的参数大概是P=8、I=0.02、D=0.2,实际运行还算稳定。调参时要把采样周期固定下来,我固定是500ms,参数不一致会直接影响整定结果。
5.3 数据上报接口和协议约定
系统里温度数据最终要传给上位机或者楼宇管理平台,我用的方案是UART加Modbus RTU。温度值乘以100转成整数,保留两位小数,存进两个寄存器,这样一个16位寄存器就能表达完整温度,上位机解析也方便。
协议约定要提前写清楚,谁做主站、寄存器地址怎么划分、报警状态放在哪个位,这些必须在软件编码之前定义好。实测中100ms上报一帧温度数据,上位机曲线刷新很平滑,基本看不出量化阶梯。如果需要更快响应异常,可以把ALERT中断事件单独做成优先级高的报警帧,这样温度越限到上位机收到告警的延迟能控制在几十毫秒内。
6. 调试中的常见问题与排查心得
6.1 I2C读数异常或卡死
I2C问题排在第一位的原因是总线没上拉。SDA和SCL必须通过电阻接高电平,漏了上拉的话波形不完整,读回来的数据全是0xFF或者直接卡死在等待应答。
排查方法很简单:用示波器看SCL和SDA波形。如果时钟线有方波、数据线拉低后回不来,基本就是上拉电阻没焊好或者阻值太大。另一种情况是总线上挂了两颗同地址的设备,一旦同时应答,数据就乱了。这时候要检查组件的地址设置。还有一次现场问题,是传感器VDD接到5V,I2C高电平超过主控容忍上限,通信时好时坏,最后按规格书改成3.3V才恢复。
6.2 内部温度读数持续偏高
STM32L432KC内部传感器读数比环境温度高,是正常现象,不是故障。芯片运行功耗越大,结温和环境温度的差值就越大。判断散热设计是否正常,应该跟芯片允许的最大结温比,而不是跟环境温度比。
但这个现象可以反过来利用。我在项目里同时采集内部温度和PJ85718DM远端温度,两者差值能间接反映风道散热状态。开机初期记录差值,运行时间长了差值变大,说明散热在恶化,风扇老化或者风道堵塞时这个特征很明显。这算是一个不用增加硬件就能做状态监测的技巧。
6.3 传感器的校准和多点一致性
两个传感器测同一个热源,读数差1℃以内是正常的。如果要求更高的一致性,必须做校准。我的方法是把传感器探头放到冰水混合物和恒温水槽里,分别记录读数和参考温度计的差值,再做偏移修正。PJ85718DM可以在配置寄存器里设置偏移量,也可以在应用层加一个校准值。STM32内部传感器同样处理,把“本地读数减参考温度”的偏移存到EEPROM,开机自动加载。
经验上,校准值应该用差值保存,不要直接改温度转换公式里的系数。改公式很容易把传感器本身的线性偏差和系统噪声混在一起,越调越乱。差分校准更加直观,也方便在生产线上逐台写入。
把本地温度交给MCU内部传感器,把远端温度交给PJ85718DM,这套组合用下来最大的感受是,系统结构简单,故障定位也清楚。之后我在项目里把PJ85718DM装到风管中段,再用内部温度和远端温度做差值来监测风机效率,运行效果一直不错。如果你也在做类似的嵌入式或HVAC温度监测项目,建议原理图阶段就把传感器地址、ALERT中断、应用层校准偏移这三个事情留好位置,后面的开发调试会省掉很多麻烦。