做嵌入式控制板的朋友应该都有这种经历一个系统里要同时测好几个温度点而且这些点的位置往往不在同一块板子上。之前接手某 HVAC 控制器项目时客户的要求其实很简单——控制板自身的环境温度要监控风管里回风口的温度也要监控两点之间大概隔了两米多的线距。我第一反应是老办法两路 NTC 加 ADC或者一根热电偶拉过去。但真正做起来才发现NTC 要逐个标定热电偶的信号又太弱冷端补偿、放大、调理一套流程下来工期和成本都压不住。后来换成 PJ85718DM 这颗带本地与远程双通道数字温度传感器配一块 STM32L4S5ZI 做主控一条 I2C 总线就同时拿下了本地和远端两个温度读数。这篇文章把硬件连接、固件读取、低功耗调度、校准方法以及调试过程中踩过的坑完整记录下来给后面做类似嵌入式测温或 HVAC 项目的朋友做个参考。1. 为什么要在 HVAC 里同时测“板子自身”和“远端风管”的温度1.1 本地通道和远程通道分别盯的是什么先说清楚“本地”和“远程”这两个概念。本地温度指的是传感器芯片所在 PCB 附近的温度PJ85718DM 内部自带测温二极管芯片通电后就能直接测出自身封装附近的温度。这个温度能反映控制板上电源模块、驱动器件、甚至 MCU 自身的发热情况一旦板卡局部过热最先报警的往往就是它。远程温度则完全不同芯片通过 D、D- 两个引脚外接一只二极管或三极管利用 PN 结正向压降与温度的线性关系把测温点延伸到传感器之外的地方。在 HVAC 设备里这种双通道结构非常实用。控制器主板一般装在电控盒里而真正需要监测的是风口、盘管、压缩机回气管这些“冷源”或“热源”。例如回风温度可以用来判断过滤网是否堵塞出风温度可以反映换热器工作状态盘管温度则能直接看出制冷剂流量是否异常。这些点距离主控板常常有几米远如果每个点都拉一根 NTC 回来线缆成本和接插件数量都会翻倍而用远程通道只需要一对信号线引到测温点附近的二极管上就行。1.2 双通道数字测温方案对比热电偶和 NTC 的优势很多工程师一看到“测温”就想到 NTC因为便宜、常见、资料多。但 NTC 的问题是离散性大批量生产时每个探头都要校准而且长线传输时线路电阻会直接叠加到测量结果里电源噪声也会通过分压网络进入 ADC信号调理做起来相当啰嗦。热电偶虽然测温范围宽但输出信号只有微伏到毫伏级别需要精密放大器、冷端补偿和滤波电路在小体积控制板里放这么一套模拟链路并不划算。数字双通道温度传感器把这些麻烦都省掉了。PJ85718DM 内部完成电流激励、电压采样和 ADC 转换直接通过 I2C 输出温度值MCU 拿到的已经是数字量。远程通道虽然走的是二极管测温原理但传感器内部对信号做了处理线缆上的共模干扰大部分被滤掉。我把三条路线放在一张对比表里优劣其实一眼就能看明白。方案测温点位数长线抗干扰校准复杂度系统成本NTC ADC每点一路传感器差线阻影响明显每个探头标定便宜但费工热电偶 调理电路每点一路热电偶中等需屏蔽需冷端补偿模拟链路成本高PJ85718DM 双通道1 本地 1 远程较强数字输出两点校准即可单颗芯片集成对 HVAC 这种大量存在风机、继电器、变频压缩机的现场环境来说数字输出本身就等于一层抗干扰能力。这也是我最后放弃 NTC 方案的根本原因。2. PJ85718DM 与 STM32L4S5ZI 的硬件连接细节2.1 最小系统与引脚分配PJ85718DM 是一颗 I2C 接口的远程温度传感器典型的引脚功能包括电源、地、SDA、SCL、地址引脚 A0/A1、报警输出 ALERT以及远程测温用的 D 和 D-。我这次在主控板上用了 STM32L4S5ZI 的 I2C1 外设SCL 和 SDA 分别接到 PB6、PB7A0 和 A1 都接地这样传感器的 7 位地址固定为 0x48换算成 8 位写地址就是 0x90、读地址 0x91。如果你的板子上有多颗同型号传感器可以用 A0/A1 的组合扩展出 4 个地址具体以器件手册为准。电源和上拉处理有几个细节值得注意。VCC 用 3.3V旁边放 100nF 和 1µF 两颗电容并联去耦电容尽量贴近芯片引脚。I2C 总线是开漏结构必须外部上拉STM32 内部虽然也能配置上拉但阻值偏大总线上挂了两颗芯片以后边沿变缓所以外部加 4.7kΩ 上拉到 3.3V 更稳妥。ALERT 报警输出是开漏引脚不使用时可以直接悬空或者接上拉我习惯把它接到 STM32 的一个 GPIO 并配置成 EXTI 外部中断这样温度超限时 MCU 能第一时间被唤醒不用靠轮询。I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance I2C1; hi2c1.Init.Timing 0x20404768; /* 需要按实际 APB 时钟调整 */ hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(hi2c1) ! HAL_OK) { Error_Handler(); } }Timing 参数在 CubeMX 里配置 I2C 时钟时可以直接生成手动填 0x20404768 的前提是 APB1 时钟是 80MHz、目标速率 400kHz。如果通信不稳定优先检查这个参数因为它直接决定了 SCL 高低电平时间配错的表现往往是从机没有任何 ACK。2.2 远程二极管的布线处理与抗干扰设计远程通道的硬件连接是整个方案里最容易出问题的地方。二极管可以选用小信号三极管 2N3904把集电极和基极短接当二极管用基极集电极端接 D发射极接 D-这样比直接用二极管电流能力更稳β 参数也相对可控。注意晶体管的极性一定不能接反否则远程通道读出来就是异常值。布线方面D、D- 是所有信号线里抗干扰优先级最高的两条。我在这两条线上预留了 RC 滤波位置D 串一只 4.7kΩ 电阻D/D- 之间并一颗 2200pF 电容既能限制高频噪声电流又能滤掉差分噪声。实际布线时让这两根线尽量贴在一起走最好双绞或平行包地不要穿过继电器、变压器、开关电源的功率回路。这个原则看起来简单但在 PCB 布局紧凑时特别容易被忽略后面调试跳变问题时有近一半的根因都出在这里。3. 固件第一步I2C 寄存器操作与温度计算3.1 寄存器映射与 I2C 初始化不同厂家的远程温度传感器寄存器地址不完全一样但总体风格非常接近。PJ85718DM 上电后默认工作在连续转换模式核心寄存器包括本地温度寄存器、远程温度寄存器、状态寄存器、配置寄存器、上限报警阈值 TOS 和回差阈值 THYST。基本操作流程就是先配置工作模式然后周期读取两个温度寄存器。下面这张表是我按常见寄存器风格整理的具体地址偏移量以器件手册为准。寄存器典型地址说明Local Temperature0x00本地温度16 位左对齐Remote Temperature0x01远程温度16 位左对齐Status0x02状态位指示远程断路/短路Configuration0x03转换模式、报警极性等TOS0x06上限报警阈值THYST0x07回差阈值I2C 初始化完成后建议先读一次状态寄存器确认芯片在线再读取温度。这一步看起来多余但批量生产时可以避免“板子焊完才发现传感器虚焊”的麻烦也能在调试初期快速区分通信问题和器件问题。3.2 本地/远程温度读取代码与转换公式读取温度我用的是 HAL 库的HAL_I2C_Mem_Read先指定要读的寄存器地址再读两个字节。PJ85718DM 的温度数据格式是 16 位左对齐低 4 位为无效位实际有效数据是高 12 位分辨率为 0.0625°C。把原始数据右移 4 位后得到有符号整数再乘以 0.0625 就是浮点温度值。float read_temperature(uint8_t reg_addr) { uint8_t buf[2]; if (HAL_I2C_Mem_Read(hi2c1, 0x48 1, reg_addr, I2C_MEMADD_SIZE_8BIT, buf, 2, 100) ! HAL_OK) { return NAN; } int16_t raw (int16_t)((buf[0] 8) | buf[1]); raw 4; return (float)raw * 0.0625f; }这里最容易被坑的是带符号数的处理。如果只把寄存器值当成无符号数负温度时读出来的值会变成一个很大的正数计算出的温度完全错误。HVAC 系统里不仅有夏天制冷还有冷库、冬季新风预热等场景远程测温点完全可能低于 0°C所以必须用int16_t做符号扩展。如果手册标注的数据格式是右对齐的 13 位那就不能右移 4 位而是直接按比例换算。格式问题一定要以手册为准我在这个细节上吃过亏。3.3 报警阈值设置与 HVAC 中的回差设计报警功能对 HVAC 设备来说不是可选项而是刚需比如盘管温度过高可能意味着冷媒泄漏电机温度过低可能意味着气流不足。设置超限报警的思路是先写 TOS 上限再写 THYST 回差。两者的差值决定了温度回落到多少度才解除报警。假如我把 TOS 设为 75°C、THYST 设为 70°C那么温度升到 75°C 触发报警后要回落到 70°C 以下报警才会解除。void set_alarm_threshold(float tos_degc, float thyst_degc) { int16_t tos_raw (int16_t)(tos_degc * 16.0f) 4; int16_t thyst_raw (int16_t)(thyst_degc * 16.0f) 4; uint8_t tos_buf[2] { (uint8_t)(tos_raw 8), (uint8_t)(tos_raw 0xFF) }; uint8_t thys_buf[2] { (uint8_t)(thyst_raw 8), (uint8_t)(thyst_raw 0xFF) }; HAL_I2C_Mem_Write(hi2c1, 0x48 1, 0x06, I2C_MEMADD_SIZE_8BIT, tos_buf, 2, 100); HAL_I2C_Mem_Write(hi2c1, 0x48 1, 0x07, I2C_MEMADD_SIZE_8BIT, thys_buf, 2, 100); }回差的意义在于防抖。如果阈值是 75°C、回差为 0温度在 74.9°C 和 75.1°C 之间波动时报警输出会不停翻转带动外部继电器频繁吸合释放触点寿命急剧下降。设置 5°C 左右的回差不仅对继电器友好也给控制系统留出了响应时间。4. 低功耗采样与调度让温度监测真正适合嵌入式设备4.1 连续转换模式的功耗账如果做的是 24V 供电的 HVAC 控制器功耗压力不大传感器一直开着连续转换完全没问题。但如果是电池供电的温控面板、无线传感器节点或者需要满足待机功耗要求的设备功耗账就得仔细算。PJ85718DM 在连续转换模式下典型工作电流在数百微安这个量级。单独看确实不大但在一个总电流预算只有几十微安的待机系统里这部分占比会非常扎眼。改用单次转换模式可以把平均功耗降一个数量级。原理很简单平时传感器处于关断或空闲状态MCU 需要读数时写一个启动位等待转换完成后读走结果然后继续休眠。采样周期越长平均电流越低。对 HVAC 应用来说回风温度和盘管温度的变化原本就很缓慢1 秒采样一次已经绰绰有余完全没有必要让传感器全速跑。4.2 STM32L4S5ZI 的 STOP2 模式与 RTC 定时唤醒STM32L4S5ZI 本身就是低功耗定位带多路 RTC 闹钟和多种 STOP 模式。我采用的调度方案是主循环平时进入 STOP2 模式RTC 闹钟每 1 秒唤醒一次唤醒后读取温度和报警状态然后再次进入睡眠。这样整机功耗完全由唤醒频率决定。void enter_sleep_and_wait(void) { HAL_RTC_SetAlarm_IT(hrtc, sAlarm, RTC_ALARM_A); __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); /* 唤醒后系统时钟会切换到低速源必须重新配置 */ SystemClock_Config(); read_temperature_and_process(); }唤醒后最容易被忽略的一步是重新配置系统时钟。STM32 从 STOP2 模式唤醒后系统时钟会自动变成 MSI 低速时钟如果直接接着跑 I2C通信时序完全不对表现就是读到的数据全是 0xFF 或者 HAL 超时。我最初做这个方案时踩过这个坑后来在每次唤醒后的第一件事就放SystemClock_Config()问题立刻消失。4.3 读数滤波与异常值识别远程通道温度读取偶尔会混入毛刺比如风机启动瞬间的电磁干扰在某些场景下可以穿透滤波电容。轮询程序里加一个简单的滑动中值滤波效果非常明显每轮采集 5 次温度排序后取中间值能同时滤掉正向和负向的尖峰。float median_filter(float samples[], int len) { /* 简单的选择排序只排 5 个点即可 */ for (int i 0; i len - 1; i) { for (int j i 1; j len; j) { if (samples[j] samples[i]) { float tmp samples[i]; samples[i] samples[j]; samples[j] tmp; } } } return samples[len / 2]; }中值滤波比滑动平均更适合温度采样因为温度本身是慢变量滑动平均会让阶跃响应变钝而中值滤波对真实台阶的响应几乎没有延迟。同时还要识别异常值远程二极管开路或短路时温度寄存器会读到接近满量程的极端值程序里要判断读数是否超出物理范围超了就直接丢弃并置一个通道故障标志而不是把错误温度拿去控制风机阀门。5. 精度校准把远程通道的误差追回来5.1 误差来源拆解偏置电流、串联电阻与 β任何远程温度传感器都有系统误差只是大小不同。PJ85718DM 的远程测温原理是芯片向 D 注入一个已知大小的偏置电流流过外部二极管后测量正向压降 Vbe再根据 PN 结电压与温度的近似线性关系反推温度。这个过程中存在三处主要误差偏置电流本身的绝对值偏差、外部串联电阻产生的欧姆压降、以及外部晶体管 β 值引起的等效串联电阻变化。串联电阻误差最典型。如果 D/D- 走线很长或者测温二极管本身带有较大的体电阻芯片测到的电压就包含电阻压降导致读数偏高。很多传感器为此提供了串联电阻补偿寄存器通过一个偏移量修正这部分误差。有些芯片还支持 β 补偿和温度系数补偿PJ85718DM 的具体特性以手册为准。但原理都是通用的误差不是随机噪声而是系统性偏移可以通过校准消除。5.2 两点校准操作流程要校准远程通道需要两个稳定且已知的参考温度点。最省事的做法是冰水混合物提供 0°C恒温箱或精密恒温水槽提供 50°C。校准前先把远程测温二极管放入参考环境等传感器读数稳定后记录下数值得到两组数据传感器读数 x_low/x_high参考真实值 y_low/y_high。然后按两点式直线校准公式计算斜率和截距k (y_high - y_low) / (x_high - x_low) b y_low - k * x_low 校正值 k * 传感器原始读数 b我实测过一批样机校准前在 0°C 时偏差约 2.1°C50°C 时偏差约 1.6°C用两点校准后残余误差可以压到 ±0.3°C 以内。不过要说明的是这一套流程主要适用于远程通道本地通道本身精度已经够高通常不需要额外校准。如果批量生产对精度要求不苛刻比如只要求 ±1°C完全可以省略校准流程出厂测试中做一次抽样验证就好。5.3 校准系数如何在固件里生效校准得到的 k 和 b 不能写死在代码里这样换一批板子又要改程序重编译。我建议把校准系数存储到内部 Flash 的最后一个扇区或者外部 EEPROM产品校准工装在校验完成后把系数写入固件启动时读取并应用到所有远程温度读数上。typedef struct { float k; float b; uint16_t crc; } cal_param_t; float apply_remote_calibration(float raw_temp) { cal_param_t cal; read_cal_param_from_flash(cal); if (cal.crc valid_crc) { return cal.k * raw_temp cal.b; } return raw_temp; /* 没有校准数据时退回原始值 */ }注意 Flash 擦写寿命有限不要每次开机都写一次一般只在出厂校准工装烧录时写。EEPROM 方案更灵活些但产品成本会略微增加。我在实际项目中用的是内部 Flash配合 CRC 校验只要校验失败就自动忽略系数避免因为 Flash 异常导致温度出现离谱偏移。6. 调试实录三个典型案例的完整排查链路6.1 远程温度读数周期性跳变噪声耦合项目联调第一天功能基本正常但远程通道在风机启动后开始抽风温度从 26°C 跳到 47°C来回摆动幅度夸张。一开始我怀疑是软件读寄存器时序问题但把本地通道同时打印出来本地完全稳定说明 I2C 通信本身没问题问题出在远程通道和外部环境的交互上。接下来用示波器抓 D/D- 的信号发现风机一启动线上就叠加了几百毫伏的高频共模脉冲。顺着 PCB 布线一查D/D- 有大概五厘米的走线从继电器输出线旁边经过继电器触点动作时把噪声耦合了进来。处理方案做了两步把 D/D- 走线换到远离功率器件的位置并给 PCB 增加了屏蔽地同时在 D/D- 之间加上 2200pF 差分电容D 串联 4.7kΩ 电阻。改完后复试风机启停和继电器反复动作的场景下远程温度读数都不再跳动。这个案例给我的教训是远程通道的滤波不是可选项而是干扰环境下的必选项。6.2 远程通道读数固定不变二极管接线反了第二块样机焊完后本地温度正常远程通道始终读到 0xFFFF 附近的极端值而且完全不变。第一反应是芯片损坏但换芯片后故障依旧于是开始读状态寄存器排错信息明确提示远程通路异常开路。用万用表二极管档测 D/D-正反测量压降都不对再对照 PCB 原理图检查才发现某个批次贴片时把远程二极管的 D 和 D- 接反了。PN 结反接之后传感器注入的偏置电流无法形成正常压降通道就表现为开路。修正焊接后远程温度恢复稳定。这个案例提醒我两点第一远程二极管极性在原理图和封装库阶段就要做防呆设计比如引脚命名尽量直观第二批量生产时出厂测试一定要跑远程温度通道的自检别只读本地温度否则这类焊接问题很容易漏过去。6.3 继电器动作瞬间 I2C 通信失败地弹与走线干扰第三个坑比较隐蔽。程序跑起来后大部分时间正常但只要外部继电器吸合I2C 通信就会偶发失败故障灯闪一下多试几次还会进入 HAL 超时。刚开始以为是 I2C 时序被长线拖慢把速率从 400kHz 降到 100kHz 有所缓解但没有根治。用示波器同时抓 3.3V 电源轨和 SDA 信号发现继电器吸合瞬间3.3V 上出现幅度不小的跌落和毛刺这就是典型的地弹效应。继电器线圈断电时产生反电动势如果线圈两端没有续流二极管反电动势会通过地回路干扰控制板电源进而影响数字信号电平。检查原理图发现继电器驱动电路确实漏了续流二极管。补上 1N4007 之后3.3V 电源毛刺明显消失I2C 通信恢复稳定。作为加固我又把 I2C 两条信号线包地处理并将上拉电阻从 4.7kΩ 调整为 2.2kΩ提高总线抗干扰裕量。这类问题在 HVAc 设备里非常典型因为继电器、接触器、压缩机是系统标配功率回路和控制回路天然存在耦合路径。排查的时候不要一上来就怀疑芯片或代码先看电源和地再看走线布局顺序对了问题基本上都能快速锁定。7. 这套方案的适用边界和我的一些补充建议7.1 什么时候适合用什么时候不适合PJ85718DM 搭配 STM32L4S5ZI 的双通道测温方案最合适的场景是需要同时监控控制板自身温度和几个较近处的远端温度点距离在几米以内温度范围没有超出传感器覆盖区间而且系统本身已经有 MCU 和 I2C 总线。这时候双通道传感器的优势最大一颗芯片解决两个点数字输出省掉模拟调理开发周期明显缩短。如果测温点数量很多比如一台大型风柜要同时测十几个点的温度双通道方案就不合适了。I2C 地址有限地址线最多组合出几个地址就算加 I2C 多路选择器也需要额外代码管理。这种场景不如直接用多路数字传感器或者 1-Wire 总线的方案一条总线挂几十个节点系统结构更简单。如果测温距离超过几十米数字 I2C 信号本身的传输距离上限就不够可以考虑转成差分或无线方式把数据先就地处理再上传。理解边界之后再做选型才不会把路走窄。7.2 如果重新做一遍我在选型和设计中会改什么回头复盘这个项目有几件事如果再给我一次机会我肯定会提前做。原理图阶段就把远程通道的滤波电阻电容、ESD 保护器件全部预留好位置而不是等到调试时再飞线补ALERT 中断引脚从一开始就接入 MCU而不是先轮询、后续再优化远程测温点的封装选择上优先用三极管因为它比裸二极管更好焊接极性辨识也更清楚。数据上报的接口值得提前留一个。HVAC 设备最终往往要接入楼宇自控系统或者运维平台温度数据只有变成 Modbus 寄存器或者上层网关能读的东西才算真正发挥价值。我这次在固件里预留了一个简单的数据缓冲区把本地温度、远程温度、报警状态打包成结构体后续接网关或者通信模块时只需增加一个透传入口不需要改动采集逻辑。这些改进都不复杂但每一条都能节省实际项目里少则半天、多则数天的调试时间。