1. 项目缘起与整体设计思路嵌入式温度监测这个方向看起来简单真做起来坑不少。我前后做过好几个温度采集项目从单点DS18B20到多点分布式从板载NTC到远程热电偶每次都会遇到类似的问题本地温度采集精度不够、远程温度走线一长就飘、多路通道切换时串扰严重、上位机拿到的数据跳变没法用。这次要聊的这套方案核心是用 PJ85718DM 做模拟前端采集用 PIC32MX795F512L 做本地数据处理和远程通信调度覆盖嵌入式和 HVAC 两类典型场景。先说清楚这套东西是什么。PJ85718DM 是一颗多通道温度传感信号调理芯片支持本地温度检测和远程二极管/晶体管温度检测通过 I2C 或 SMBus 接口输出数字化的温度值。PIC32MX795F512L 是 Microchip 的 32 位 MIPS 内核 MCU512KB Flash、128KB RAM带多个 I2C 控制器、UART、CAN 和以太网 MAC非常适合做 HVAC 控制器的主控。两者搭配本地温度由 PJ85718DM 内置的本地通道采集远程温度通过外接的远程传感晶体管采集MCU 负责轮询、滤波、标定、上报和联动控制。这套方案能解决什么问题第一本地和远程温度可以在同一颗芯片上完成采集省掉额外的ADC和模拟开关第二远程温度通道支持串联电阻补偿和beta值校正长线走线的误差可以压到可接受范围第三MCU 侧可以做滑动平均、中值滤波和异常剔除输出稳定可用的温度数据第四HVAC 场景下可以直接用这些温度做风机调速、阀门开度和压缩机保护逻辑。适合谁来参考做嵌入式温度采集的固件工程师、做 HVAC 控制器的硬件工程师、做板级热管理的系统工程师以及刚接触 I2C 温度传感器、想搞清楚远程温度到底怎么校准的初学者。下面我会从方案选型、硬件细节、固件实现、标定流程、问题排查几个维度把整套东西拆开讲。2. 核心器件选型与关键参数解析2.1 为什么选 PJ85718DM 而不是分立方案很多人第一反应是用 NTC 加分压电阻加 MCU 的 ADC 来采温度。这个方案成本低但问题很明显NTC 非线性严重需要查表或 Steinhart-Hart 公式拟合分压电阻的精度直接影响结果MCU 的 ADC 参考电压如果有波动温度就跟着飘多路采集还要加模拟开关通道间串扰和漏电流都是隐患。PJ85718DM 这类专用温度调理芯片的好处在于它把传感器激励、信号放大、ADC 转换、线性化和数字接口都集成进去了。本地通道用的是芯片内部的温度敏感元件出厂已经校准过远程通道支持外接的二极管连接三极管利用两个不同电流下的 Vbe 差值来计算温度这个原理本身对串联电阻不敏感所以长线走线的寄生电阻影响可以被大幅抵消。具体到参数层面PJ85718DM 的本地温度精度典型值是 ±1°C远程通道在理想条件下可以做到 ±1°C 以内分辨率可以配置到 0.0625°C 甚至更细。I2C 接口速率支持标准模式 100kHz 和快速模式 400kHzMCU 侧轮询一次本地加远程两路温度在快速模式下大概几百微秒就能完成。这个速度对于 HVAC 这种秒级响应的场景绰绰有余。2.2 PIC32MX795F512L 在系统中的角色定位PIC32MX795F512L 不是单纯拿来读 I2C 的。它的定位是系统主控温度采集只是它众多任务中的一个。这颗 MCU 的主频可以跑到 80MHz512KB Flash 足够放温度标定表、滤波算法和通信协议栈128KB RAM 可以开多个缓冲区做数据队列。它有两个独立的 I2C 控制器一个可以专门挂温度传感器另一个留给 EEPROM 或显示模块互不干扰。在 HVAC 应用里这颗 MCU 还要处理风机 PWM、阀门驱动、按键输入、LCD 显示、以及和上位机或楼宇控制器的通信。所以温度采集的固件必须做成非阻塞的不能因为等 I2C 数据就把整个系统卡住。我的做法是把温度采集放在定时器中断触发的状态机里每次只做一步采完本地通道再采远程通道中间穿插其他任务。2.3 本地与远程通道的硬件连接要点本地通道没什么好说的芯片贴板安装注意远离发热元件就行。远程通道才是重点。远程传感元件一般用低成本的三极管把集电极和基极短接当成二极管用。这个二极管要放在被测温的位置比如 HVAC 的回风管道、换热器表面或者远程机柜内部。走线方面远程二极管的两根线最好用双绞线并且尽量远离高压开关节点和 PWM 走线。串联电阻在 1kΩ 以内时PJ85718DM 的自动串联电阻抵消功能可以处理掉大部分误差。如果走线超过一米建议在芯片输入端加 RC 低通滤波截止频率设在 10kHz 左右滤掉高频干扰但保留温度信号。注意远程三极管的基极-发射极结电容和走线电容会形成低通如果电容太大芯片的电流切换还没稳定就采样读数会偏低。实测走线超过两米时建议把芯片的转换速率调低一档给信号更多建立时间。3. 固件架构与温度采集实现细节3.1 I2C 驱动层的关键配置PIC32MX795F512L 的 I2C 外设配置有几个容易踩坑的地方。首先是波特率设置公式是 I2CBRG (FSCK / (2 * FSCL)) - 2其中 FSCK 是外设总线时钟。假设外设总线是 40MHz目标 SCL 是 400kHz那么 I2CBRG (40M / (2 * 400k)) - 2 50 - 2 48。这个值要写进 I2CxBRG 寄存器。其次是 SDA 保持时间和建立时间在快速模式下这两个参数有最小值要求。PIC32 的 I2C 模块有 I2CxCON 寄存器里的 DISSLW 和 SIDL 等位可以调但更稳妥的做法是直接按数据手册推荐值配置不要为了省事全用默认值。还有一点PJ85718DM 的 I2C 地址取决于 A0/A1/A2 引脚的接法典型地址是 0x4C 到 0x4F 之间。如果总线上挂了多颗同型号芯片必须确保地址不冲突。我遇到过有人把三颗芯片的地址引脚全接地结果只能读到一颗的数据排查了半天才发现是地址撞了。// I2C 初始化示例基于 PIC32MX795F512L void I2C1_Init(void) { I2C1BRG 48; // 400kHz 40MHz 外设时钟 I2C1CONbits.ON 1; // 使能 I2C 模块 I2C1CONbits.SIDL 0; // 空闲模式继续工作 I2C1CONbits.DISSLW 0;// 使能斜率控制 }3.2 温度寄存器读取与数据格式转换PJ85718DM 的温度数据是 16 位高字节是整数部分低字节的高四位是小数部分分辨率 0.0625°C。读取的时候先发寄存器地址再读两个字节。注意远程通道和本地通道的寄存器地址不同本地温度一般在 0x00远程温度在 0x01 或 0x10 附近具体要看数据手册的寄存器映射。数据转换的时候有个细节温度是补码格式负温度的高字节最高位是 1。如果直接当成无符号数处理零下温度就会变成很大的正数。正确做法是先判断符号位再做补码转换。float ConvertTemp(uint8_t msb, uint8_t lsb) { int16_t raw (msb 8) | lsb; raw 4; // 低四位是状态位不是温度数据 if (raw 0x0800) { // 判断符号位 raw | 0xF000; // 符号扩展 } return raw * 0.0625f; }3.3 多通道轮询与滤波策略本地加远程两路温度如果每路都单独触发一次转换再读效率不高。PJ85718DM 支持连续转换模式配置好之后它会自动轮流转换本地和远程通道MCU 只需要定时去读最新结果就行。这样可以把 I2C 通信次数降到最低。滤波方面我一般用三级处理第一级是硬件 RC 滤波在芯片输入端做第二级是固件里的滑动平均窗口大小取 8 或 16根据温度变化速率来定第三级是异常值剔除如果某次读数和前一次差超过 5°C就判定为干扰直接丢弃。HVAC 场景温度变化慢窗口可以取大一点比如 32这样数据非常平滑。实操心得滑动平均的窗口不是越大越好。窗口太大温度真实变化时响应会滞后。我一般先用 8 窗口跑如果发现数据还是跳再加大到 16。同时保留一个原始值缓冲区方便调试时看干扰到底有多大。4. 远程温度标定与误差补偿实战4.1 串联电阻补偿的原理与配置远程二极管测温的核心原理是用两个不同的电流 I1 和 I2 分别流过二极管测量两次 Vbe差值 ΔVbe (kT/q) * ln(I1/I2)。这个差值只和温度、电流比有关和串联电阻无关。但实际芯片在切换电流时串联电阻上的压降会叠加到 Vbe 上如果不在采样时序上做处理就会引入误差。PJ85718DM 内部有串联电阻抵消电路通过在不同电流下分别采样并做数学运算把串联电阻的影响消掉。配置的时候需要设置一个电阻补偿寄存器值根据实际串联电阻大小来定。如果走线电阻大概 1Ω补偿值设小一点如果串了 100Ω 的保护电阻补偿值就要相应加大。4.2 Beta 值校正与晶体管选型远程三极管的 Beta 值会影响测温精度。理想情况下二极管连接的晶体管其集电极电流和基极-发射极电压的关系是确定的但实际晶体管的 Beta 不是无穷大基极电流会分走一部分导致误差。PJ85718DM 支持 Beta 值校正配置一个校正因子可以补偿这个误差。晶体管选型上推荐用 Beta 值在 100 到 300 之间、一致性好的型号。不要用大功率管因为大功率管的结面积大结电容也大会影响采样建立时间。小信号管比如常见的 2N3904 或 MMBT3904 就挺合适便宜且一致性好。4.3 实际标定流程与数据记录标定是远程温度能不能用的关键。我的标定流程是这样的先把远程三极管和一颗高精度参考温度计一起放进恒温槽从 0°C 到 70°C 取 8 个点每个点稳定 10 分钟后记录 PJ85718DM 的读数和参考温度计的读数。然后算每个点的误差如果误差在 ±1°C 以内直接用一个固定偏移量补偿就行如果误差随温度变化就要做分段线性补偿。标定温度点参考温度计读数PJ85718DM 读数误差补偿方式0°C0.1°C0.8°C0.7°C固定偏移25°C25.0°C25.4°C0.4°C固定偏移50°C50.0°C50.9°C0.9°C固定偏移70°C70.0°C71.5°C1.5°C分段补偿从表里能看出来低温段误差小高温段误差大这时候固定偏移就不够了需要在 50°C 以上加一个斜率补偿。补偿公式可以写成T_corrected T_raw - offset - k * (T_raw - 50)其中 k 是高温段的补偿斜率。注意标定用的恒温槽必须稳定足够长时间尤其是从高温往低温降的时候三极管的热惯性会导致读数滞后。我一般每个点等 15 分钟以上确认连续三次读数变化小于 0.1°C 才记录。5. HVAC 场景下的系统集成与联动逻辑5.1 温度数据在 HVAC 控制中的使用方式HVAC 系统里温度不是拿来看的是要用来做控制的。回风温度用来调风机转速送风温度用来判断是否需要开加热或制冷室外温度用来做新风比例调节换热器表面温度用来做防冻保护。这些控制逻辑对温度数据的实时性和可靠性要求不一样。回风温度变化慢采样周期可以放到 1 秒一次滤波窗口取大一点。送风温度变化快采样周期要短200ms 一次滤波窗口取小一点。换热器表面温度是保护用的一旦超过阈值要立刻响应所以这路温度不做滤波直接读原始值配合硬件比较器做快速保护。5.2 多路温度采集的任务调度PIC32MX795F512L 要同时处理多路温度还要跑其他任务调度上要分优先级。我的做法是用一个 10ms 的定时器中断做时基在中断里维护一个软件计数器每 20 个 tick 触发一次回风温度采集每 2 个 tick 触发一次送风温度采集换热器温度则用外部中断加比较器直接响应。I2C 通信放在主循环里做中断只负责置标志位。这样不会因为 I2C 等待影响中断响应。主循环里检查标志位如果到了采集时间就启动一次 I2C 传输传输完成后再做数据转换和滤波。volatile uint8_t flag_return_air 0; volatile uint8_t flag_supply_air 0; void Timer1_ISR(void) { static uint16_t tick 0; tick; if (tick % 20 0) flag_return_air 1; if (tick % 2 0) flag_supply_air 1; IFS0bits.T1IF 0; }5.3 与上位机通信的数据打包温度数据最终要传给上位机或楼宇控制器。我一般用 Modbus RTU 或者自定义的二进制协议。Modbus 的好处是通用很多组态软件直接支持。自定义协议的好处是紧凑适合低带宽场景。数据打包的时候温度值统一放大 10 倍变成整数比如 25.4°C 打包成 254。这样用两个字节就能表示 -3276.8°C 到 3276.7°C精度 0.1°C对 HVAC 足够了。每个温度通道占两个寄存器再加上状态字和校验一帧数据大概 20 个字节。实操心得打包的时候一定要加时间戳或者序列号。HVAC 系统里通信偶尔会丢包如果没有序列号上位机分不清是数据没更新还是温度真的没变。我一般用一个 16 位循环计数器每发一帧加一上位机发现序列号跳变就知道丢包了。6. 常见问题排查与避坑经验实录6.1 温度读数跳变或固定不动的排查思路读数跳变是最常见的问题。排查顺序是这样的先用示波器看 I2C 波形确认时序没问题再看电源纹波如果 VDD 上有几十毫伏的开关噪声温度读数就会跳然后检查远程走线看是否和 PWM 线捆在一起了最后看固件里的滤波参数窗口太小也会导致跳变。读数固定不动一般是 I2C 通信失败MCU 读到的都是缓存里的旧值。这时候要检查 I2C 的 ACK 位如果从机没应答说明地址错了或者芯片没上电。还有一种可能是芯片进入了关断模式需要发唤醒命令。现象可能原因排查方法解决措施读数跳变 ±3°C电源噪声示波器看 VDD 纹波加 100nF 和 10uF 去耦读数固定不变I2C 无应答查 ACK 位和地址确认地址和上电状态远程温度偏高串联电阻未补偿测走线电阻配置补偿寄存器高温段误差大Beta 值未校正对比标定数据配置 Beta 校正因子多芯片地址冲突A0/A1/A2 接法相同查地址引脚改硬件接法或分总线6.2 远程走线干扰的抑制技巧远程走线超过 30cm 就要注意了。我踩过的坑是把远程三极管的线和风机 PWM 线放在同一个线槽里结果温度读数跟着风机转速跳。后来把温度线改成屏蔽双绞线屏蔽层单端接地跳变从 ±5°C 降到了 ±0.5°C。还有一个技巧是在芯片输入端加共模扼流圈对高频共模干扰抑制效果很好。如果成本允许用带屏蔽的电缆并且两端都加 RC 滤波效果更稳。RC 的电阻取 100Ω电容取 1nF截止频率大概 1.6MHz对温度信号没影响但能把几十兆的干扰挡掉。6.3 固件层面的容错与恢复机制I2C 总线会死锁这是嵌入式工程师都遇到过的问题。如果 MCU 在传输过程中复位而从机还在等时钟SDA 就会被拉低总线锁死。恢复方法是把 SCL 当成普通 GPIO手动发 9 个时钟脉冲让从机把数据移完然后发一个 STOP 条件。PIC32MX795F512L 的 I2C 模块支持总线超时检测配置 I2CxCON 里的 BOEN 位和 I2CxSTAT 里的状态位可以在超时后自动复位 I2C 模块。但自动复位不一定能解决所有死锁所以固件里还是要加手动恢复的兜底逻辑。void I2C1_BusRecover(void) { I2C1CONbits.ON 0; // 关闭 I2C 模块 delay_us(10); // 手动发 9 个时钟 for (int i 0; i 9; i) { SCL_LAT 0; delay_us(5); SCL_LAT 1; delay_us(5); } // 发 STOP 条件 SDA_LAT 0; delay_us(5); SCL_LAT 1; delay_us(5); SDA_LAT 1; delay_us(5); I2C1CONbits.ON 1; // 重新使能 }6.4 标定数据的存储与版本管理标定数据不能写死在代码里因为每块板的远程三极管和走线都不一样。我的做法是把标定参数存在 MCU 的 EEPROM 或者外部 Flash 里开机时读出来。参数包括本地偏移、远程偏移、远程斜率、Beta 校正因子、串联电阻补偿值。版本管理也很重要。每次标定完记录标定日期、标定设备编号、标定环境温度。如果后面发现某批板子温度不准可以追溯是不是标定环节出了问题。我一般用一个简单的结构体存这些信息加一个 CRC 校验防止 Flash 位翻转导致参数错误。typedef struct { float local_offset; float remote_offset; float remote_slope; uint16_t beta_correction; uint16_t series_resistance; uint32_t cal_date; uint16_t crc; } CalibrationData;7. 性能验证与长期运行观察7.1 精度验证的测试方法验证精度不能只靠一颗参考温度计最好用多点对比。我的做法是同时放三颗经过校准的参考传感器取平均值作为真值然后看 PJ85718DM 的读数和真值的偏差。测试点覆盖 0°C、10°C、25°C、40°C、55°C、70°C每个点稳定后连续采 100 次算平均误差和标准差。实测下来本地通道在 0 到 70°C 范围内误差可以控制在 ±0.5°C 以内远程通道经过标定后可以控制在 ±1°C 以内。标准差方面本地通道大概 0.05°C远程通道 0.1°C 左右。这个水平对 HVAC 应用完全够用。7.2 长期运行的数据漂移观察温度传感器会漂移尤其是远程三极管长期工作在高温高湿环境下封装应力会导致参数变化。我做过一个连续运行 30 天的测试每天记录同一温度点的读数发现前 3 天有大概 0.2°C 的漂移之后趋于稳定。所以建议在系统运行 72 小时后做一次单点复标把漂移补掉。如果应用环境温度变化剧烈比如室外机建议每半年做一次复标。复标不需要拆机只要在已知温度点比如冰水混合物 0°C记录读数算出差值更新偏移参数就行。7.3 系统资源占用与优化空间这套方案在 PIC32MX795F512L 上跑I2C 温度采集占用的 CPU 时间大概 2% 左右RAM 占用不到 1KBFlash 占用大概 8KB含滤波和标定逻辑。如果系统任务多可以把温度采集的优先级放低用空闲时间跑。优化空间主要在滤波算法上。滑动平均虽然简单但需要存一个窗口的数组。如果 RAM 紧张可以用一阶 IIR 滤波只存一个历史值效果接近滑动平均但省内存。IIR 的系数根据采样周期和温度变化速率来调一般取 0.1 到 0.3 之间。最后分享一个小技巧调试温度采集的时候把原始值、滤波值、标定值三个都打印出来对比着看。很多时候问题出在标定环节但现象看起来像是滤波没做好。三个值一对比问题定位就快多了。