做嵌入式温控或者 HVAC 控制器的人,迟早会碰上一个绕不开的问题:板上要测温度,柜外风管、盘管、回风口也要测温度。前者是“本地”,后者是“远程”。如果每个测点都放一颗独立传感器,成本、PCB 面积、走线复杂度都会跟着上去。这篇我用 PJ85718DM 加 STM32F071VB 的组合,聊一套同时覆盖本地与远程温度监测的完整方案:从传感器信号链、MCU 资源对接,到固件滤波和现场布线的实际坑。适合正在做楼宇自控、变频末端、冷库控制器或者嵌入式温控模块的工程师参考。
先说结论:这套方案的思路,不是简单把两颗温度传感器拼在一块板上,而是用一颗带双通道测量能力的传感器,同时读回“芯片自己周围的温度”和“远端探头位置的温度”。STM32F071VB 这边只负责 I2C 通信、阈值中断、滤波和上报。整条链路下来,省了一个器件、省了一路 I2C,还让两个温度点天然保持时间同步。
1. 温度测点的物理分布,决定了“本地和远程”不是可选项
1.1 控制器里那枚“板上温度”到底有什么用
很多人觉得,板上温度是最没用的一个参数,板子自己发热,测出来有什么意义?实际恰恰相反。在 HVAC 控制器里,本地温度至少承担三个任务:第一个是 PCB 热保护,MOS 管、变压器、继电器密集的区域,温度异常往往是器件失效的前兆;第二个是作为远程测温的参考基准,因为传感器的绝对精度有限,本地和远程在同一颗芯片内完成测量时,系统性误差可以互相抵消一部分;第三个是判断风机或者散热结构是否正常工作,风机堵转、风道堵塞,最先反映出来的就是板温而不是远端温度。
我在调试某组温控样机时就遇到过这样的情况:远程风管温度读数一直正常,但板上温度开始爬升,从 48℃ 一路到 70℃ 左右。如果不是本地通道一直在后台监测,这个问题可能要等整块板子过热保护动作之后才暴露。所以本地通道不是“凑个数”,它是整个温控系统里最早感知异常的传感器。
1.2 风管、回风、盘管表面:远端探头放在哪更合理
远程温度测点,才是 HVAC 系统真正关心的对象。风机盘管需要测回风温度来推算送风温差;冷媒管路需要贴管测表面温度来防止结露或者判断过热度;新风机组要在风管内布探头感知室外新风温度。这类测点的共同特点是:离主控板远,环境差,走线长。
跟本地测温不同,远程测点的位置选择有几个原则。第一,尽量不要放在气流死区,比如风管弯头内侧,那里空气基本不流动,测出来的温度滞后严重;第二,贴管测量时要加导热硅脂或者导热衬垫,空气间隙造成的高热阻会让读数比真实管壁温度差出 3~5℃;第三,探头线要避开强电回路,尤其是变频器输出侧的电缆,干扰会直接耦合进传感器的远程测量引脚。
1.3 为什么不用两颗独立温度传感器,而选双通道方案
这个问题的答案,做硬件的人能立刻反应出来:两套器件意味着两套退耦电容、两套 I2C 地址处理、两路不同的电源噪声特性,还有两颗器件之间的批次偏差。双通道方案的好处在于,本地和远程温度是在同一颗裸片、同一个转换周期内完成的,本身就保证了采样的同步性。对于温差计算这类逻辑,同步性比绝对精度更重要。另外,远程通道只靠两颗引脚引出去,外部探头只需要一个二极管接法的三极管加一根双绞线,比单独一颗传感器的完整外围简单太多。
在成本上,双通道传感器可能比两颗独立传感器贵一点,但把 PCB 面积、贴片费用、供货管理成本算进去,双通道方案往往是更划算的选择。尤其是量大的暖通控制器,少一个料号就是少一份供应链风险。
2. 信号链拆解:PJ85718DM 从一个电压到两个温度值
2.1 远程通道的工作原理:二极管正向压差与绝对温度的关系
PJ85718DM 这类带远程测温能力的传感器,核心测量原理是半导体 PN 结的正向压降温度特性。给二极管接法的探头施加两个不同大小的电流,分别测出两个正向压降 V1 和 V2。由于 PN 结的饱和电流 Is 随温度变化,但差值 ΔVBE 与 Is 无关,ΔVBE 正比于绝对温度 T,比例系数是 k/q 乘以 ln(电流比)。芯片内部把这一小段电压做放大和 ADC 转换,就能恢复出探头位置的温度值。
本地通道的原理也一样,只是测温的 PN 结直接做在芯片内部,不需要外接器件。所以这颗传感器很有代表性——两条通道的物理原理相同,信号链架构一致,区别只在于“测温结”放哪里。这也是双通道传感器能做到高一致性的根本原因,而不是像很多人想的那样“内部装了两套 ADC”。
2.2 通道分配与硬件连接:传感器引脚、探头的接法、去耦
PJ85718DM 的典型接法并不复杂。传感器本身由 3.3V 供电,SDA、SCL 接 MCU 的 I2C 外设,板上各加 2.2kΩ 左右的上拉电阻到电源。本地通道不需要额外元件,直接读寄存器即可。远程通道从传感器的 D+、D- 引脚引出,到探头端接一个 SOT-23 封装的小信号三极管,基极和集电极短接起来当二极管用,发射极连 D-,集电极连 D+。
这里有一个很容易犯的错:远程探头的引线不能随便用普通跳线或者杜邦线。测温引线的线阻会在芯片内部激励电流下产生额外的压降,直接影响测量结果。引线越长,电阻越大,读数偏得越离谱。比较稳妥的做法是用双绞线,并且控制在 1 米以内;如果非要超过 1 米,就需要在走线成本和精度要求之间做权衡,必要时考虑带自动消除线阻功能的方案,或者改用电流环形式的变送器作替代。
电源去耦方面,传感器电源引脚放一个 0.1μF 陶瓷电容贴近放置,再并联一个 1μF 的电容,前者滤高频,后者稳定瞬态。D+、D- 走线要避开时钟线、PWM 输出线和功率级驱动信号,因为远程通道测量的是微伏级差分信号,哪怕小的耦合噪声都会被放大到几摄氏度的读数波动。
2.3 初始化配置与数据读取
PJ85718DM 的控制方式跟大多数温度传感器类似,通过 I2C 寄存器读写。上电后首先要配置转换模式和测量周期。如果 HVAc 系统对实时性要求不高,建议用单次转换模式,需要时触发一次,读完就关,既省电又避免自发热影响精度;如果系统需要持续监控,可以配置为连续转换,转换周期根据系统响应要求设成 1 秒或者 500ms。
配置中断阈值也很关键。本地过温阈值,比如 85℃;远程测点的上下限要看实际工艺,比如回风温度正常在 10~35℃ 之间,超过这个区间就可以触发告警。很多现场误报是因为阈值设成了固定值,没有考虑季节变化,冬天开机时风管初始温度本来就在零下,一开机就触发低温告警,这是典型的配置问题。
初始化时,大概要写以下几类配置(实际寄存器地址以数据手册为准):
/* 伪代码,仅示意配置项,不代表实际寄存器地址 */ void pj85_init(void) { uint8_t cfg1 = PJ85_CONFIG1; cfg1 |= (1 << 4); // 连续转换模式 cfg1 |= (3 << 0); // 转换周期 1s uint8_t cfg2 = PJ85_CONFIG2; cfg2 |= (0x03 << 5);// 分辨率 12bit cfg2 |= (1 << 2); // 远程通道使能 cfg2 |= (1 << 1); // 本地通道使能 pj85_write_register(0x01, cfg1); pj85_write_register(0x02, cfg2); pj85_write_register(0x03, LOCAL_LIMIT_HIGH); // 本地过温阈值 pj85_write_register(0x04, REMOTE_LIMIT_LOW); // 远程低温阈值 pj85_write_register(0x05, REMOTE_LIMIT_HIGH); // 远程高温阈值 }读温度时需要按寄存器顺序连续读回 16 位数据。注意:芯片通常用 0.0625℃ 作为 LSB,直接把寄存器原始值乘以 0.0625 就是温度值;如果寄存器里有符号位,负温度不要直接当成正数计算,要先判断符号扩展。
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| 转换模式 | 连续 1s | 兼顾响应与功耗 |
| 分辨率 | 12bit | 0.0625℃ LSB,足够暖通用 |
| 远程通道 | 使能 | 外部探头接 D+/D- |
| 报警阈值 | 按工艺定 | 冬天避免固定低温误报 |
| 中断引脚 | 开漏输出 | 接 MCU 的 EXTI 引脚 |
3. STM32F071VB 的最小系统与接口对接
3.1 MCU 侧资源盘点:I2C、GPIO、中断控制器
STM32F071VB 是 Cortex-M0 内核的 MCU,主频 48MHz,100 引脚封装,Flash 有 128KB,做这类温度采集和上报任务绰绰有余。它自带两个 I2C 外设、一个 12 位 ADC、若干个定时器和丰富的 GPIO。在整套温控系统里,MCU 不只是读温度,还要管继电器、风机、数码管显示和通信,所以资源得留出余量。
温度传感器的 I2C 挂在 I2C1 上比较合适,因为 I2C1 的引脚可以复用成其他功能,布线灵活。传感器的 ALERT 或者中断输出引脚,接一个带有外部中断功能的 GPIO,这样 MCU 不需要一直轮询温度值,只有在温度越限时才被唤醒。这是低功耗设计的基础,也是现场抗干扰的一个关键点——减少总线访问次数,就减少出错概率。
3.2 传感器挂到哪条总线上:I2C 地址、速率、上拉电阻
PJ85718DM 的 I2C 地址通常由地址引脚的电平决定,常见做法是把 ADDR 引脚接地,得到一个固定地址。如果你的板子上还有其他 I2C 器件,比如 EEPROM 或者 RTC,记得在枚举设备时扫描一遍,避免地址冲突。我之前就遇到过传感器和 EEPROM 地址撞车的情况,总线通信时好时坏,排查半天才发现两颗器件响应同一个地址。
总线速率建议先用 100kHz 跑通,再评估要不要提到 400kHz。HVAC 现场有变频器、接触器、继电器这类强干扰源,I2C 的走线又可能比较长,速率越高对沿陡峭和噪声容限的要求越苛刻。很多人一开始就用 400kHz,结果现场偶发通信失败,降到 100kHz 之后问题消失,白白折腾。上拉电阻的选择也要看总线电容,普通板级走线 2.2kΩ 起步,如果线上挂了多个器件,可能需要降到 1.5kΩ;但也不要太小,否则低电平拉不低,协议都满足不了。
3.3 供电与地平面的处理
MCU 采用 3.3V 单电源供电,传感器也共用这一路。设计时要把模拟区域和数字区域分开走线,传感器尽量靠近 MCU 的 I2C 引脚,避免 I2C 信号穿过功率继电器或者开关电源的环路区域。传感器供电引脚还要注意顺序:如果 MCU 的 I2C 引脚先上电,传感器后上电,泄露电流可能把 SDA/SCL 拉到一个不确定电平,导致总线锁死。稳妥的办法是让传感器和 MCU 同一个电源轨,或者加一个简单的上电时序控制。
地平面方面,远程探头的返回地线不能直接借用功率地的裸铜区走长线。探头地最好从传感器 D- 引脚独立连接,不要和继电器、风机的地线并在一起。远程测温通道测量的是微小差分信号,对地噪声非常敏感,现场如果出现读数周期性波动,先怀疑是不是地线环路引入了共模干扰。
4. 固件层设计:轮询、中断、滤波与报警逻辑
4.1 第一版轮询代码:能跑,但问题明显
最直接的做法,是主循环里每隔一段时间读一次温度寄存器:
while (1) { int16_t local_temp = pj85_read_local(); int16_t remote_temp = pj85_read_remote(); if (remote_temp > REMOTE_HIGH_LIMIT) { alarm_on(); } display_temperature(local_temp, remote_temp); delay_ms(1000); }功能上没问题,但实际用起来有两个不舒服的地方:第一,主循环被阻塞,一旦 I2C 通信拉长,其他任务比如继电器动作就会被延迟;第二,持续轮询意味着 I2C 总线一直被占用,现场强干扰下总线出错时,程序不一定能及时恢复。轮询代码作为工程验证够了,但作为产品固件,需要把结构改得更干净。
4.2 改造为中断触发:阈值报警与总线恢复
更合理的设计是让传感器主动“汇报”异常。PJ85718DM 的报警输出引脚在温度越限时拉低,MCU 这边用 EXTI 上升沿或下降沿中断来唤醒。正常温度时,MCU 按照低频率,比如每 5 秒读一次温度用于显示和记录;一旦报警引脚触发,马上变成高频读取,确认是真实越限还是干扰误触发,再做相应动作。
这个设计还有一个好处:如果传感器和主控之间因为干扰产生通信故障,报警引脚的状态可以作为一个粗判依据。另外,固件里要留 I2C 总线恢复逻辑:检测到总线忙超时后,先尝试发送 9 个时钟脉冲把从机状态复位,再重新初始化。这个技巧在工业现场非常实用,不然偶尔的一次总线卡死,可能要让设备断电重启才能恢复。
4.3 温度滤波:HVAC 现场的温度突变要压多久
温度信号本身是缓变量,但 HVAC 现场充满了“假突变”。最典型的是风机启动瞬间,风管里静止的空气突然被吹动,探头感受到的气流温度可能在几秒内跳好几度;还有压缩机启动时冷媒管表面温度骤降,这些变化是真实的物理过程,但是否需要立刻触发告警,得看系统逻辑。
工程上常用一阶惯性滤波,即 output = output_old × (1 - α) + input × α。α 越大,跟随越快,但波动也越大;α 越小,输出越平滑,但滞后加大。HVAC 温度控制回路里,我通常把 α 设在 0.1 到 0.3 之间,对应时间常数大约 10 到 30 秒。对于告警判断,则不建议直接用滤波后的值,而是用“原始值连续超过阈值 N 秒”作为确认条件,既避开滤波带来的延迟,也能有效滤掉瞬时抖动。
4.4 温差与过温判断的滞回设计
本地和远程温度都拿到之后,HVAAC 系统真正关心的是温差。比如盘管表面温度和回风温度的差值,如果拉得过大,可能是风量不足或者盘管结垢。温差判断一定要加滞回:进入报警状态的阈值是 8℃,退出报警状态则是 5℃。没有滞回的后果就是温度在阈值附近摆动时,继电器反复吸合断开,触点寿命严重缩短。
实现滞回逻辑时,用状态机而不是简单比较。每个测点维护 current_state 和 last_reliable_temp,只有状态改变时才产生事件。这样上层协议只需要处理“进入告警”“退出告警”两个事件,而不是处理每一秒的原始温度波动,整个系统的事件负载会小很多。
typedef enum { TEMP_NORMAL, TEMP_ALARM } temp_state_t; temp_state_t remote_state = TEMP_NORMAL; void remote_temp_monitor(int16_t temp) { switch (remote_state) { case TEMP_NORMAL: if (temp > REMOTE_ALARM_HIGH_C) { remote_state = TEMP_ALARM; alarm_event_send(EVENT_REMOTE_HIGH); } break; case TEMP_ALARM: if (temp < REMOTE_RECOVER_HIGH_C) { remote_state = TEMP_NORMAL; alarm_event_send(EVENT_REMOTE_RECOVER); } break; } }5. 现场实测中容易翻车的四个细节
5.1 远程探头引线电阻与引脚寄生电容
调试中见过最典型的读数异常,是远程温度整体偏高。原因是探头引线用了普通平行软线,长度 2 米左右,线阻大约 0.5Ω,对于传感器几毫安量级的激励电流来说已经足够产生误差。另外,线间的寄生电容会和传感器内部的电流开关形成充放电边沿,导致测量时序抖动,反映为读数跳字。
这个问题的解决方式不复杂:远程信号线必须采用双绞结构或者屏蔽线,屏蔽层单端接地;探头三极管那两个引脚不要留长腿,尽量贴根剪短,引脚本身就是几十 nH 的电感和几 pF 的电容,高频干扰下会变成天线。我把探头改成短引脚贴片安装之后,同一个位置的读数波动从 ±2℃ 降到了 ±0.3℃。
5.2 标定没有基准源时怎么办
很多实验室没有恒温槽,标定工作就容易糊弄。实际可以用一个简单的替代方案:找一个密闭泡沫箱,把远程探头和一支经过校准的工业级测温探头绑在一起放进去,等待温度平衡,读取两者差值。分别在室温、加冰水、以及暖风加热后做三点比对,就能大致评估传感器在目标温度区间的线性度。
要注意的是,绑探头时不能让金属外壳直接接触,否则热传导路径不统一,标定结果没有意义。正确做法是同时浸入同一块导热硅脂或者同一杯水里,保证两个传感元件处于完全相同的热场。标定后的偏差可以在固件里做查表修正,或者直接写在系统参数区,方便产线按器件个体微调。
5.3 I2C 总线被干扰的现场处理
HVAC 柜子里有接触器,动作瞬间能拉出很高的尖峰。I2C 总线如果离这些动力线太近,总线上会出现毛刺,轻则字节错误,重则造成 SDA 锁住。除了前面说的降速和加强滤波外,通信协议层也要有容错:每次读取做 CRC 校验或者简单的重复读校验,两次读数不一致就重试;连续失败超过 3 次,主动复位外设。
我还发现一个现场特别有用的技巧:温度读取这类低实时性数据,可以采用“读两次取平均”的策略,第一次读到的数据丢弃,第二次校准到正确的寄存器地址后再读。代价是总线流量翻倍,但换来的是稳定性的大幅提升,对于低速 I2C 场景完全可接受。
5.4 不要忽略传感器的自发热
传感器芯片自身工作会发热,尤其是连续转换模式下 ADC 持续工作,芯片温升可能达到 1~2℃。如果本地通道要作为保护阈值,这部分自发热必须考虑进去。不要追求“测到芯片表面真实温度”,而是要把自发热修正项写进保护逻辑。比如板卡允许最高 85℃,传感器本地读数到 86℃ 时再报警,留 1℃ 的裕量。
把探头放在气流路径上也会有类似问题,气流会带走探头引脚上的热量,让读数偏低。固定探头最好用卡簧加导热硅脂的方式,而不是用热熔胶把整个探头包起来,热熔胶的热阻会让响应时间变慢很多,完全看不到真实温度变化。
这套本地加远程的测温方案,我前后调了两轮才稳定下来。第一轮犯的错误基本集中在引线处理和总线速率上,第二轮集中在阈值配置和滤波参数上。现在回头看,PJ85718DM 和 STM32F071VB 的组合真正解决的,不是“能不能测到温度”这种基础问题,而是“两个温度点如何低成本、高一致性地同时可信”。如果下次再调类似系统,我会把远程探头的连接器也做成屏蔽端子,并且把 I2C 地址冲突检查写进自检流程,省得现场再花半天排查通信故障。