1. 项目概述一套双通道温度监测方案的来由做嵌入式温度监控时手里同时拿一颗 PJ85718DM 温度传感器和 MK51DN512CLQ10 主控基本就等于把“本地板温 远程环境温度”这套很常见又很麻烦的需求简化到了一根 I2C 总线上。这种组合在 HVAC暖通空调设备里尤其好用本地温度负责看主板和功率器件远程通道则盯着风道、盘管或者散热器表面。这个项目不复杂但把器件选型、硬件连接、固件读取和告警联调串起来之后能省掉很多重复造轮子的工作。PJ85718DM 这类的双通道温度传感器最吸引我的一点是它把“本地温度测量”和“远端二极管测温”做到了一起。主控板上只需要放一颗传感器就能同时知道板卡自身温度和几十厘米甚至几米外的测温点温度。而 MK51DN512CLQ10 是一颗 Cortex-M4 内核的 MCU512KB Flash 和 128KB SRAM 对温度采集任务来说余量很足至少有两个独立的 I2C 控制器处理多路传感器、告警和协议上报都很顺手。1.1 嵌入式与 HVAC 中真实存在的温度监测痛点在 HVAC 设备里温度点并不是只在显示屏上看一个室温那么简单。一个典型的空调机组或风机盘管系统里可能需要测回风温度、送风温度、盘管进出口温度、电控箱内部温度和功率器件外壳温度。这些位置有的在主控板上有的在风道深处有的在铜管或散热器附近彼此之间可能有强电走线、风机干扰和结构遮挡。如果每个点都放一颗完整封装的数字温度传感器硬件成本会明显上升如果全部用 NTC 热敏电阻加 ADC又得处理分压电阻精度、非线性查表和长线抗干扰问题。更麻烦的是主控板上的 ADC 通道数量有限为了测三个点就可能把 ADC 通道全部占满还要在每个采样点前加滤波电容布板面积一下子就上去了。嵌入式设备这边同样有自己的痛点。很多控制板在正常工作时MCU、MOSFET、开关电源附近温度会明显高于环境如果不做区分很容易把“板子自身发热”误判成“环境温度变高”。反过来如果把传感器放在远离热源的地方又无法感知功率器件过温。这时候“本地温度”和“远程温度”分开来看比只看一个点要可靠得多。1.2 两颗芯片的分工传感器负责采集主控负责消化这套方案里PJ85718DM 负责所有和温度相关的模拟量处理。它内部集成本地温度传感器同时提供远程测温通道远端只需要接一个二极管接法的三极管或芯片内置的温度感应二极管传感器就会通过两次不同电流下的二极管压差算出远端温度最后把本地、远程两个温度值都放到寄存器里通过 I2C/SMBus 总线供主控读取。之所以把这个工作放在一颗专用传感器里而不是让 MK51DN512CLQ10 直接拿 ADC 去测二极管压差是因为远程二极管测温对电流源精度、测量时序和 ADC 分辨率都有要求。自己用通用 ADC 去做很容易被 mV 级的噪声干扰温度结果一米算下来偏几度都很正常。专用芯片把这些测量逻辑封装好了主控只需要做读寄存器、换算、报警和通信。MK51DN512CLQ10 在这套方案里的角色更像一个“消化端”。它上电后先把 GPI/O、I2C、UART 和中断抓紧初始化好然后周期性读取传感器的温度寄存器把原始数据转换成摄氏度再做阈值判断。遇到超温时可以点亮故障灯、断开继电器或者通过 Modbus/自定义协议把数据上报给楼上系统。这颗 MCU 的性能放在温度采集场景里其实是溢出的但正因为性能够后面想加 PID 控温、历史记录、多路 I2C 传感器扫描都还有充足资源。1.3 为什么把“远程通道”做成二极管测温远程温度通道不是接一个完整传感器而是接一个二极管结很多人第一次接触会觉得奇怪。实际上这和我们平时测 CPU、GPU 内核温度用的是同一个思路二极管两端在大小两个不同恒定电流下的正向压差和绝对温度之间存在良好的线性关系。传感器内部会依次施加两个不同的偏置电流测出两次 Vbe 的差值。根据 PN 结理论[ \Delta V_{be} \frac{k \cdot T}{q} \cdot \ln(N) ]其中 k 是玻尔兹曼常数q 是电子电荷N 是两个电流的比值。只要把这个电压差值测量准就能反推温度。正因为这个原理远程测温点可以做得非常小不需要在远端放一颗带电源、ADC、通信接口的完整芯片导电胶或夹具固定一个小三极管就可以当测温探头用。这种做法的工程价值在 HVAC 场景里非常实际。主控板放在电控箱里箱内温度和风道温度完全不同如果只测主板温度控制逻辑就没法根据风道温度调节风机和水阀如果单独拉一根 NTC 长线又会遇到引线电阻和干扰。用双通道传感器加远程二极管主控板上放 PJ85718DM远端引两条线接二极管两边各测各的数据都走 I2C 一份寄存器干净利落。2. 硬件搭建与信号链路设计2.1 PJ85718DM 的引脚连接与供电先讲硬件因为温度采集这类项目硬件决定下限固件只能在上限以内修修补补。PJ85718DM 一般提供 VDD、GND、SCL、SDA、ALERT、远程测温引脚和地址配置引脚。供电电压通常是 3.3V 级别最好和 MK51DN512CLQ10 的 I2C 电源域保持一致这样上下拉电平匹配不需要额外做电平转换。SCL 和 SDA 都是开漏输出必须在总线上加上拉电阻。上拉电阻选多大取决于总线上挂了多少设备以及走线长度。对于板内短距离、400kHz 左右的 I2C 总线常见做法是用 2.2kΩ 到 4.7kΩ如果总线上挂了三四颗传感器总线电容上去之后可以适当把电阻往小了调但不能太小太小会把开漏输出的低电平拉不下去。HVAC 场景里如果 I2C 走线超过 20cm我建议把通信速率降到 100kHz并保证上下拉供电纹波不要太大。地址配置引脚一般支持两到三种接法例如两个引脚都接地、一个接地一个接 VDD、全部接 VDD对应不同的 7 位 I2C 地址。如果器件只有 A0 和 A1 两个引脚常见的地址会落在 0x48 到 0x4B 这个范围内。具体哪个组合对应哪个地址不同批次可能有差异但基本逻辑是一样的先让单颗传感器独立连接用 I2C 扫描工具扫出地址之后再确认地址配置。2.2 远程测温通道三极管和走线的门道远程通道的接法我踩过不少坑。最常用的远端测温元件是一只 NPN 三极管比如 2N3904把集电极和基极短接再接传感器的一侧发射极接另一侧。这样做等于利用了三极管的 BE 结形成二极管而不是直接用普通二极管因为三极管的 BE 结在生产时一致性更好温度特性也更稳定。这里有一个关键点极性一定不能接反。远程测温芯片对二极管方向敏感接反之后读数可能变成负数或者固定在某个异常值。布线时远程端的两条线最好做成双绞线并且远离大电流开关节点、PWM 线和强电动力线。如果现场环境非常恶劣还可以在靠近传感器端加一个小容量的滤波电容但电容也不能加太大否则二极管压差建立速度变慢测量结果会滞后甚至偏差。远程测温距离不是越长越好。虽然比 I2C 长线更抗干扰但毕竟测的是 mV 级电压差线缆电阻和寄生电容都会影响精度。我在实际项目里一般把远程通道控制在 1 米到 3 米以内超过这个距离宁可在远端再放一颗 I2C 传感器或者换用对应的长线专用监测方案。远程二极管和 PJ85718DM 之间最好用屏蔽对线屏蔽层单端接地避免形成地环流。2.3 与 MK51DN512CLQ10 的 I2C 接线和地址规划MK51DN512CLQ10 的 I2C 引脚直接连到传感器 SCL、SDAALERT 可以接一个 GPIO并把中断配置成下降沿触发。上电顺序上我习惯先把 MCU 的电源准备好再把传感器电源打开避免传感器在 MCU 还没配置好 GPIO 时就开始访问总线。如果系统里只挂了一颗 PJ85718DM地址规划很简单直接把地址引脚按默认组合接好即可。但 HVAC 机组里经常需要多处温度监测比如同时测回风、送风、盘管进水、盘管出水这时候一颗传感器就不够用。我的建议是使用多颗 PJ85718DM每颗通过地址引脚配置成不同地址然后再挂到同一条 I2C 总线上。这样 MCU 只需要一组 I2C 引脚多路温度都能读到固件里用一个抽象函数区分地址和通道代码也容易维护。3. 固件实现从读寄存器到一份可上线的温度采集代码3.1 I2C 初始化与传感器配置流程固件部分我用 C 代码示例来展开。以下示例不绑定具体库重点是把流程写清楚。第一步是初始化 MK51DN512CLQ10 的 I2C 控制器设置时钟频率再配置传感器引脚。初始化 I2Cvoid mcu_i2c_init(void) { // 开启 I2C 时钟配置引脚为开漏功能 // 设置波特率寄存器目标频率 100kHz // 使能 I2C 控制器 }初始化完成后先不要急着读温度先把 PJ85718DM 的配置寄存器写一遍。我一般会设置成连续转换模式并把转换周期调到 125ms 左右。HVAC 控制环路通常在 1 秒到 5 秒刷新一次就够了125ms 的转换周期意味着每次读取到的都是比较新的数据不会明显滞后。#define SENSOR_ADDR 0x48 #define REG_CONFIG 0x02 #define REG_LOCAL_TEMP 0x00 #define REG_REMOTE_TEMP 0x01 #define REG_STATUS 0x06 void temp_sensor_init(void) { uint8_t cfg 0x00; // 连续转换模式 合适的报警滤波配置 // 具体位定义以器件手册为准 i2c_write_reg(SENSOR_ADDR, REG_CONFIG, cfg); }需要说明的是PJ85718DM 的具体寄存器偏移和告警位在不同批次里可能有差异我下面的代码是按常见双通道温度传感器的寄存器布局来演示的实际移植时一定要对着自己的 datasheet 确认。整体流程比寄存器值更重要先配模式再清状态最后周期读取。3.2 读取本地/远程温度并换算成摄氏度温度值通常以 12 位二进制补码形式存在两个字节里实际有效数据在高 12 位低 4 位一般是 0 或者不用。每个 LSB 对应 0.0625°C这个值和很多常见温度传感器一致。所以读取流程是先读出两个字节拼成一个 16 位整数再右移 4 位最后乘以 0.0625。int16_t temp_raw_to_celsius(uint16_t raw) { int32_t val (raw 4) 0x0FFF; // 如果符号位为 1转换成负值 if (val 0x0800) { val - 0x1000; } return (int16_t)(val * 100 / 16); // 等价于 val * 6.25单位为 0.01°C }这里我用固定小数点处理把温度扩大了 100 倍避免直接使用浮点数。比如原始值 0x0850右移后是 0x085十进制 133133 乘以 6.25 等于 831也就是 8.31°C。如果只想要 0.1°C 精度可以直接用查表方法HVAC 场景里 0.1°C 已经足够。读取本地和远程温度typedef struct { int16_t local_temp; // 单位 0.01°C int16_t remote_temp; // 单位 0.01°C } hvac_temp_t; hvac_temp_t read_temperature(void) { hvac_temp_t temp; uint16_t raw_local i2c_read_reg16(SENSOR_ADDR, REG_LOCAL_TEMP); uint16_t raw_remote i2c_read_reg16(SENSOR_ADDR, REG_REMOTE_TEMP); temp.local_temp temp_raw_to_celsius(raw_local); temp.remote_temp temp_raw_to_celsius(raw_remote); return temp; }这样写的好处是主程序里不用关心寄存器字节序读温度就是一次函数调用。如果需要做多颗传感器扩展只要把 SENSOR_ADDR 换成函数参数再维护一组地址表即可。3.3 阈值告警与异常数据判断温度数据读回来不能直接用还要做好两件事一是判断传感器本身是否异常二是把温度限值和故障逻辑接起来。这两个点如果漏掉HVAC 设备运行几天后就会暴露问题。先看异常判断。远程测温通道如果断开读出来的数值往往会非常离谱可能是全 1也可能是固定的极限值。最好的办法是先读 STATUS 寄存器确认有没有“远端开路”或“远端短路”标志位。如果状态位显示异常数据寄存器里的温度值就不要拿去参与控制逻辑了应该标记为故障并重试。可用代码如下uint8_t temp_sensor_status(void) { return i2c_read_reg(SENSOR_ADDR, REG_STATUS); }再看阈值告警。假设送风温度上限是 75°C换算成原始寄存器值[ 75 / 0.0625 1200 0x04B0 ]如果寄存器是 12 位有效数据写入限值时需要左移 4 位变成 0x4B00void set_remote_high_limit(int16_t temp_celsius) { uint16_t raw_limit (uint16_t)(temp_celsius / 0.0625f) 4; i2c_write_reg16(SENSOR_ADDR, REG_REMOTE_HIGH_LIMIT, raw_limit); }超过阈值之后ALERT 引脚会拉低MCU 收到下降沿中断再做故障处理。不要只在轮询里判断温度超限因为轮询周期可能长达 1 秒而 ALERT 中断能够更快捕捉到异常继电器保护动作也能更及时。4. 调试过程与常见问题排查4.1 远程通道读数乱跳或固定某个值的排查远程温度读数最容易出的问题是数值上下跳动幅度很大。之前我在调一块 HVAC 控制板时PJ85718DM 的 D、D- 走线穿过了一层继电器区域结果远程温度在 20°C 到 25°C 之间乱跳本地温度正常。后来把两条线改成紧贴在一起的双绞走线并在靠近传感器端加了滤波电容读数才稳定下来。如果远程读数固定在 -128°C 或者某一个极限值大概率是远端二极管没有正常工作。先确认二极管极性再确认三极管是否真的接成了二极管形式也就是集电极和基极短接。不要凭万用表二极管档就简单认为方向对了测温芯片驱动的电流时序和普通万用表不一样还是要严格按手册接法来。还有一个常见情况远程读数和实际温度相差几度。这种偏差可能来自远端二极管型号不匹配。不同制程的二极管 ideality factor 不完全一致很多双通道温度传感器允许配置这个参数对了之后偏差能明显减小。如果这是长期运行的设备我建议在出厂前做一次两点校准把误差曲线记录下来在固件里加线性修正。4.2 I2C 读数失败的典型原因I2C 读不到数首要是确认地址对不对。很多新手把器件地址直接照抄 0x48没想到自己把 A0、A1 接了 VDD地址其实变成了另一个。调试时我习惯先做一次总线扫描void i2c_scan(void) { for (uint8_t addr 0x08; addr 0x78; addr) { if (i2c_probe(addr) 0) { // 打印找到的设备地址 } } }找不到设备时先用示波器或逻辑分析仪看 SCL 和 SDA 有没有波形。SCL 一直低电平可能是 SDA 被某个设备拉死了对总线上的器件逐个断电排查SCL 有波形但 SDA 没反应很可能地址写错或者传感器供电没起来。另外不要在 I2C 总线上把传感器和风扇调速 PWM 信号离得太近。HVAC 设备启动瞬间风机和继电器造成的干扰可能导致 I2C 通信偶尔失败比较稳妥的办法是在设备驱动层加入重试机制连续失败 3 次才报警避免单次错误直接触发故障停机。4.3 校准偏差offset 和斜率修正的实际处理温度传感器不是装上去就能直接信尤其是远程通道多少都会有一点偏差。最常见的修正是加偏置也就是把读到的温度和标准温度计的差值做一次平移。比如标准温度计读 24.6°C传感器读 24.2°C那就在固件里给该通道加 0.4°C。如果测量范围很大比如从 -10°C 到 80°C只做平移不够还要做斜率修正。在低温和高温两个点做校准算出一次方程式[ T_{real} a \cdot T_{sensor} b ]HVAC 现场校准条件有限我一般至少做冰水混合物和人体体温两个点。更专业的做法是把整个温度采样链路放进恒温槽在几个标准温度点记录原始 ADC 值再离线拟合曲线。固件里保留一个校准表每次读取完原始温度后查表输出最终结果。5. 针对 HVAC 场景的部署建议与扩展方向5.1 供电、布线和防护温度传感器本身功耗不高但 HVAC 控制板电源环境比较恶劣开关电源、继电器、风机都会产生尖峰噪声。PJ85718DM 的电源引脚旁边必须放一个 0.1μF 电容如果空间允许再并一个 10μF 电解电容或钽电容。VDD 纹波太大会直接影响内部 ADC 基准本地温度也许还看不出来远程温度就会出现周期性跳变。远程测温线不要和 220V 交流线捆在一起至少保持 5cm 以上的距离。如果必须跨越强电区域用屏蔽线加金属过线管。主控板上的 I2C 上拉电阻电源最好来自经过滤波的 3.3V不要直接从开关电源的 5V 分压过来否则总线上可能会有较大的共模噪声。5.2 多温度点扩展与上位机协议HVAC 系统一般不止两个温度点。我的做法是用多颗 PJ85718DM 扩展每颗通过地址引脚设置不同地址然后挂到同一条 I2C 总线上。主控固件里维护一张温度点配置表每个点都有通道编号、报警上限、报警下限和校准参数。上层协议只管按点号读数据底层自动选择传感器地址和本地/远程通道。上位机协议不一定要很复杂。对于中小型设备Modbus RTU 是最稳妥的选择电气隔离、寄存器映射、调试工具都很成熟。把每个温度点映射成一个保持寄存器单位用 0.1°C报警状态放到离散量寄存器。这样 MK51DN512CLQ10 只要串口收发几个字节就能接入现有的监控平台。5.3 后续把采集数据变成控制逻辑温度数据真正有价值的地方是参与控制。比如送风温度偏高时新风阀开大一点盘管出水温度偏低时关小水阀。采到的温度不应该只是被动上报还要在 MCU 里做简单判断或 PID 调节。Kinetis 这颗 MCU 的定时器、触发 ADC 和 DMA 能力都不弱把 PID 控制跑在它上面完全不成问题。我个人的习惯是在固件里先做一个 1 秒周期的温度采集任务每 10 个周期做一次平均然后用平均后的值去更新控制状态。这样既能抑制噪声又不会让阀门执行器频繁动作。故障处理上把 ALERT 中断当成最高优先级事件一旦远程温度超过限值直接进入保护状态而不是等控制程序慢慢处理。最后说一个我反复提醒自己的教训远程温度的模拟前端一定要当成“关键信号”来画板不能当成普通 GPIO 随便走线。第一版样机为了省事把 D、D- 走得很长中间还和 PWM 调速线并行结果风道温度读数 3°C 地跳折腾了两周才定位到是走线串扰。后来改成双绞差分走线远离功率开关节点并在末端做好滤波问题当场消失。如果你也要做 HVAC 里的双通道温度监测建议在原理图阶段就把这组信号当宝贝别等到试产再后悔。