
1. 项目背景与核心需求拆解温度监测这件事看起来简单真要做到工业级可靠、本地远程双通道、还要兼顾成本里面的门道比想象中多得多。我最近刚完成一个嵌入式温度采集项目用的就是 PJ85718DM 红外温度传感器搭配 STM32F303VE 主控应用场景横跨嵌入式设备内部热管理和 HVAC 系统的风管温度监测。这套组合不是随便选的背后有比较明确的工程逻辑。先说 PJ85718DM 这颗传感器。它是一颗数字输出的红外温度传感器支持 I2C 接口测量范围覆盖 -40°C 到 125°C精度在常温段可以做到 ±0.5°C 以内。相比传统的 NTC 热敏电阻它的优势在于非接触测量——不需要物理接触被测物体这在 HVAC 场景里特别关键因为风管里的空气温度用接触式传感器响应慢而且容易受安装位置影响。PJ85718DM 的视场角大约是 35 度这意味着在合理距离内它可以准确捕捉目标区域的温度不会把周围环境的辐射也混进来。STM32F303VE 这边它是 Cortex-M4 内核带 FPU 和 DSP 指令集主频 72MHz有 512KB Flash 和 64KB RAM。选它不是因为算力过剩而是因为温度监测往往不是单一任务——你可能同时要跑 PID 控制、LCD 显示、Modbus 通信、数据记录还要留出余量做固件升级。F303 的混合信号能力也强内置多路 12 位 ADC 和 DAC万一以后要扩展模拟传感器或者输出模拟控制信号不用换主控。我实际跑下来在 72MHz 下同时处理 I2C 温度采集、UART 上报和定时器 PWM 输出CPU 占用率不到 30%余量很充足。这个项目的核心需求可以拆成三层。第一层是本地监测设备自己要知道当前温度是多少用于过温保护、风扇调速或者加热控制。第二层是远程监测温度数据要通过有线或无线方式传到上位机或云平台方便集中管理。第三层是可靠性HVAC 系统往往 24 小时运行传感器要能长期稳定工作通信不能丢包异常情况要有告警。这三层需求决定了硬件选型和软件架构的基本方向。适合参考这个方案的人包括做嵌入式硬件设计的工程师、HVAC 控制器的开发者、需要非接触温度测量的项目负责人以及想了解 I2C 传感器和 STM32 配合使用的初学者。如果你之前只用过 DS18B20 这类单总线传感器这篇文章会帮你理解红外温度传感器的不同之处和实际使用中的坑。2. 硬件设计与关键器件选型解析2.1 PJ85718DM 的引脚配置与外围电路PJ85718DM 采用 TO-39 金属封装四个引脚分别是 VCC、GND、SDA、SCL。供电范围 3.0V 到 5.5V典型值 3.3V。这里有个细节要注意虽然传感器支持 5V 供电但 I2C 电平是跟着 VCC 走的。如果你用 5V 给传感器供电而 STM32F303VE 的 I2C 引脚是 3.3V 电平直接连会出问题。我一开始就踩了这个坑后来改成 3.3V 供电省掉了电平转换芯片。外围电路其实很简单但有两个地方不能省。第一是电源去耦VCC 和 GND 之间要并一颗 100nF 的陶瓷电容位置尽量靠近传感器引脚否则电源纹波会直接影响温度读数。第二是 I2C 上拉电阻SDA 和 SCL 各需要一颗 4.7kΩ 到 10kΩ 的上拉电阻接到 3.3V。我实测下来 4.7kΩ 在 100kHz 标准模式下波形最干净如果用 400kHz 快速模式可以降到 2.2kΩ但功耗会稍微增加。传感器的视场角是 35 度这意味着在距离 D 处测量光斑直径大约是 0.63 倍的 D。比如传感器离目标 10cm光斑直径约 6.3cm。这个参数在 HVAC 风管测量里很重要——风管截面通常不大如果传感器离得太远光斑会超出风管壁读到的是管壁温度而不是空气温度。我的做法是把传感器安装在风管侧面开孔处镜头距离风管中心约 5cm光斑直径约 3.1cm正好覆盖风管中心区域避开了管壁的干扰。2.2 STM32F303VE 的 I2C 外设配置要点STM32F303VE 有多个 I2C 接口我选的是 I2C1映射到 PB6SCL和 PB7SDA。配置的时候有几个关键参数需要确认。时钟频率设 100kHz 还是 400kHzPJ85718DM 支持到 400kHz但实际测试中 100kHz 的抗干扰能力更强尤其是在 HVAC 这种有电机和继电器的环境里。我最终选了 100kHz采集周期 100ms 一次完全够用。I2C 的 GPIO 模式要设成开漏输出Open-Drain并且使能内部上拉或者外部上拉。这里有个容易忽略的点STM32 的内部上拉电阻大约 40kΩ对于 I2C 来说太弱了必须用外部 4.7kΩ 上拉。我见过有人只靠内部上拉结果波形上升沿很缓通信偶尔出错查了半天才发现是上拉不够。DMA 要不要用如果只是单次读取温度不用 DMA 也行。但如果要连续采集并且同时处理其他任务建议给 I2C 接收配 DMA。我的做法是用 DMA 把传感器返回的 3 个字节温度高字节、温度低字节、PEC 校验字节直接搬到内存CPU 不用等 I2C 中断效率高很多。PEC 是数据包错误校验PJ85718DM 支持这个功能建议开启能过滤掉不少通信错误。2.3 本地显示与远程通信的硬件扩展本地显示我用的是一块 0.96 寸 OLEDI2C 接口和温度传感器共用 I2C1 总线。这里要注意地址冲突PJ85718DM 的 I2C 地址是 0x5A7 位OLED 通常是 0x3C 或 0x3D不冲突。但如果总线上挂多个同地址设备就需要用 I2C 多路复用器了。远程通信我留了两个方案。有线方案用 RS485通过 MAX3485 芯片把 UART 转成差分信号传输距离可以到 1200 米适合楼宇 HVAC 的集中监控。无线方案用 LoRa 模块通过 SPI 接口连接视距传输 2 到 5 公里适合分散的室外机组。两个方案在 PCB 上都留了接口实际用哪个看现场条件。RS485 的 A/B 线要加 120Ω 终端电阻LoRa 模块要注意天线净空区这些细节后面实操部分会展开。3. 软件架构与核心代码实现3.1 系统任务划分与调度策略软件架构我采用的是前后台系统主循环加定时器中断。没有上 RTOS因为任务数量不多前后台足够用而且代码更简单调试方便。具体划分是这样的TIM2 定时器每 1ms 产生一次中断在中断里做软件计时和任务标志置位主循环里轮询标志位依次执行温度采集、数据处理、显示刷新、通信上报四个任务。温度采集任务每 100ms 执行一次通过 I2C 读取 PJ85718DM 的原始数据。数据处理任务把原始数据转换成摄氏度做滑动平均滤波然后和阈值比较判断是否过温或欠温。显示刷新任务每 200ms 更新一次 OLED避免刷新太快导致闪烁。通信上报任务每 1 秒通过 RS485 或 LoRa 发送一次温度数据数据格式是自定义的二进制帧包含设备地址、温度值、状态字和 CRC 校验。这种划分的好处是每个任务的执行周期明确不会互相阻塞。I2C 读取虽然耗时但 100ms 一次每次大约 1ms占用 CPU 时间很少。OLED 刷新用 I2C DMA也不占 CPU。RS485 发送用 UART DMA同样不阻塞主循环。整体跑下来主循环空闲时间很多以后加功能也有余量。3.2 PJ85718DM 的 I2C 读取时序与数据解析PJ85718DM 的读取流程分两步先写寄存器地址再读数据。温度数据存在 0x07 寄存器读出来是 3 个字节。第一个字节是温度高 8 位第二个字节是温度低 8 位第三个字节是 PEC 校验。温度值的计算方式是把高字节和低字节拼成一个 16 位数低 4 位是小数部分高 12 位是整数部分分辨率 0.0625°C。举个例子读到的两个字节是 0x1A 和 0x40拼起来是 0x1A40十进制是 6720乘以 0.0625 等于 420但这是开尔文温度需要减去 273.15 才是摄氏度结果是 146.85°C。这个温度明显偏高说明传感器可能对着热源了。正常情况下室温 25°C 对应的原始值大约是 0x1A40 减去 273.15 再除以 0.0625算出来大概是 0x0C40 左右。PEC 校验用的是 CRC-8多项式是 0x07初始值 0x00。STM32F303VE 没有硬件 CRC-8需要软件实现。我写了一个查表法的 CRC-8 函数256 字节的表存在 Flash 里计算一次 PEC 大约 10 微秒完全可以接受。如果 PEC 校验失败就丢弃这次数据重新读取连续失败 3 次就上报通信故障。3.3 滑动平均滤波与温度补偿算法红外温度传感器容易受环境温度影响PJ85718DM 内部有环境温度补偿但补偿效果有限。我在软件里加了两级处理第一级是滑动平均滤波第二级是环境温度补偿。滑动平均滤波用 8 个采样值的环形缓冲区每次新数据进来就替换最旧的数据然后求平均。这样可以把随机噪声压下去同时响应速度也不会太慢。8 个采样值对应 800ms 的时间窗口对于 HVAC 这种温度变化缓慢的场景完全够用。如果被测对象温度变化很快可以把窗口缩小到 4 个采样值。环境温度补偿的思路是传感器读到的温度其实是被测物体辐射温度和环境温度的综合结果当环境温度偏离 25°C 时读数会有偏差。补偿公式是T_actual T_measured k * (T_ambient - 25)其中 k 是补偿系数典型值 0.02 到 0.05。T_ambient 可以用 STM32F303VE 内部的温度传感器读取虽然精度不高±1.5°C但用于补偿足够了。我实测下来加了补偿之后在 10°C 到 40°C 环境温度范围内读数偏差从 ±2°C 降到了 ±0.8°C。3.4 远程通信协议设计与数据帧格式远程通信我自定义了一个简单的二进制协议帧格式如下帧头 2 字节0xAA 0x55设备地址 1 字节命令字 1 字节数据长度 1 字节数据区 N 字节CRC16 校验 2 字节。温度上报的命令字是 0x01数据区包含 2 字节温度值有符号整数单位 0.1°C和 1 字节状态字。状态字的位定义bit0 表示传感器正常bit1 表示过温告警bit2 表示欠温告警bit3 表示通信故障bit4 到 bit7 保留。这样上位机收到一帧数据就能知道设备状态不用额外查询。CRC16 我用的是 Modbus CRC多项式 0xA001初始值 0xFFFF。这个 CRC 在工业领域很常见很多组态软件都支持。计算的时候要注意字节顺序Modbus CRC 是低字节在前高字节在后。我一开始搞反了上位机一直校验失败后来用串口助手抓包对比才发现问题。RS485 的收发切换需要控制 DE/RE 引脚发送前拉高发送完拉低。这里有个时序问题UART 发送完成中断触发后不能立刻拉低 DE要等最后一个字节完全移出移位寄存器。STM32 的 UART 有 TCTransmission Complete标志等这个标志置位后再拉低 DE 才可靠。我试过用 TXE 标志结果最后一个字节被截断了换成 TC 就正常了。4. 实操过程与关键环节记录4.1 硬件焊接与上电调试步骤PCB 打样回来之后我先焊了电源部分用万用表测了 3.3V 和 5V 对地电阻确认没有短路。然后焊 STM32F303VE 和晶振用 ST-Link 连接看能不能识别到芯片。这一步很关键如果芯片都认不到后面不用做了。识别成功之后写了一个最简单的 GPIO 翻转程序用示波器看 PA5 引脚有没有方波输出确认时钟和复位电路正常。接下来焊 PJ85718DM 和上拉电阻。这里要注意焊接温度TO-39 封装的引脚比较粗散热快烙铁温度可以设到 350°C但每个引脚焊接时间不要超过 3 秒否则热量传到内部芯片可能损坏。焊完之后用放大镜检查有没有虚焊和连锡。上电之后先不急着读温度先用 I2C 扫描程序确认传感器地址。我写了一个简单的扫描函数遍历 0x00 到 0x7F 地址看哪个地址有应答。PJ85718DM 应该在 0x5A 地址应答。如果没有应答检查上拉电阻、供电电压和焊接。我第一次扫描没找到设备后来发现是 SDA 和 SCL 接反了调换之后正常。4.2 温度采集程序的编写与验证I2C 初始化我用的是 HAL 库配置成 100kHz7 位地址模式开漏输出。读取函数分两步先发送寄存器地址 0x07然后读取 3 个字节。HAL 库的HAL_I2C_Mem_Read函数可以直接完成这个操作参数是设备地址、寄存器地址、地址长度、数据缓冲区、数据长度和超时时间。读到的原始数据要转换成摄氏度。转换函数如下float convert_temp(uint8_t msb, uint8_t lsb) { int16_t raw (msb 8) | lsb; float temp_k raw * 0.0625f; float temp_c temp_k - 273.15f; return temp_c; }验证的时候我用了一个恒温水浴锅把传感器对着水面距离 5cm水温从 20°C 升到 80°C每 10°C 记录一次读数。对比水浴锅的参考温度计偏差在 ±1°C 以内。这里要注意水面会有蒸发蒸发吸热会导致水面温度略低于水体温度所以参考温度计要插在水里不要放在水面上方。4.3 本地 OLED 显示与远程上报联调OLED 我用的是 SSD1306 驱动I2C 地址 0x3C。显示内容分三行第一行显示当前温度第二行显示状态正常/过温/欠温第三行显示通信状态在线/离线。刷新率设 5Hz用HAL_I2C_Mem_Write发送显示数据。这里有个优化技巧不要每次刷新都全屏更新只更新变化的区域可以减少 I2C 流量刷新更快。远程上报联调的时候我先用 USB 转 RS485 模块连接电脑用串口助手接收数据。波特率设 96008 数据位1 停止位无校验。发送周期 1 秒数据帧格式前面说过。串口助手收到数据后我用 Excel 把十六进制转成十进制验证温度值是否正确。这里要注意字节序温度值是低字节在前高字节在后和 Modbus 一致。LoRa 模块的调试稍微麻烦一点因为需要两个模块配对。我用两个 LoRa 模块一个接 STM32一个接 USB 转串口模块。配置的时候要注意频率、扩频因子、带宽和编码率四个参数必须一致否则收不到数据。我一开始频率设错了一个 433MHz 一个 470MHz调了半天才发现。配对成功之后视距传输 1 公里丢包率低于 1%效果不错。4.4 整机联调与长时间运行测试整机联调的时候我把传感器、主控、OLED、RS485 和 LoRa 都接上跑了一个 72 小时的老化测试。测试环境是办公室温度在 22°C 到 28°C 之间波动。测试期间每 10 分钟记录一次数据用 SD 卡存储。72 小时下来温度读数没有跳变通信没有丢包OLED 显示正常。这里有个细节要注意长时间运行的时候I2C 总线可能会因为干扰而锁死。STM32 的 I2C 外设有总线错误检测但有时候需要手动恢复。我的做法是在 I2C 读取函数里加超时判断如果连续 3 次超时就重新初始化 I2C 外设。这个恢复机制在 72 小时测试里触发了 2 次每次都能自动恢复没有影响数据记录。功耗方面整机运行电流大约 45mA其中 STM32 约 20mAOLED 约 15mA传感器约 1mARS485 约 5mALoRa 发射时约 4mA。如果要用电池供电可以把 OLED 关掉STM32 降频到 8MHz功耗能降到 10mA 以下。不过 HVAC 场景一般有市电功耗不是主要矛盾。5. 常见问题与排查技巧实录5.1 I2C 通信失败的五种典型原因I2C 通信失败是这个项目里最容易遇到的问题我整理了五种典型情况和对应的排查方法。第一种是上拉电阻缺失或阻值不对。症状是波形上升沿很缓或者根本拉不高。排查方法是用示波器看 SDA 和 SCL 的波形正常应该在 1 微秒内上升到 3.3V。如果上升时间超过 2 微秒就要减小上拉电阻。第二种是地址错误。PJ85718DM 的 7 位地址是 0x5A但 HAL 库函数需要的是 8 位地址也就是 0xB4左移一位。我一开始传了 0x5A一直没应答后来查手册才发现要左移。第三种是总线锁死。症状是 SCL 被某个设备拉低不放总线一直忙。解决方法是手动发送 9 个时钟脉冲让从设备释放总线。STM32 的 I2C 外设有HAL_I2C_IsDeviceReady函数可以检测设备是否就绪。第四种是电源问题。传感器供电不足或者纹波太大会导致内部复位。用万用表测 VCC 引脚正常应该在 3.3V ± 0.1V。如果纹波超过 50mV要加滤波电容。第五种是时序问题。100kHz 下时序余量很大一般不会出问题。但如果用 400kHz就要注意上升时间和总线电容。总线电容超过 400pF 会导致波形失真需要减小上拉电阻或者降低速率。5.2 温度读数异常的原因分析与处理温度读数异常通常表现为跳变、偏高或偏低。跳变一般是通信错误导致的PEC 校验失败会丢弃数据但如果连续失败就会用上一次的值看起来像跳变。解决方法是检查 I2C 波形和电源纹波。读数偏高最常见的原因是视场内有热源。比如传感器对着窗户窗外阳光辐射会让读数偏高 5°C 以上。解决方法是调整安装角度避开阳光直射和热源。另一个原因是传感器镜头脏了灰尘会影响红外透射率用无水酒精和棉签轻轻擦拭镜头即可。读数偏低一般是环境温度补偿没做好。如果环境温度低于 25°C传感器读数会偏低。开启软件补偿后可以改善。还有一种情况是传感器对着空调出风口冷风直接吹到传感器上导致读数偏低。解决方法是加一个遮风罩或者调整安装位置。5.3 RS485 通信丢包与 LoRa 距离不足的排查RS485 丢包通常是终端电阻没接或者接错。120Ω 终端电阻要接在总线两端中间节点不要接。如果总线很短小于 10 米不接终端电阻也能工作但长距离必须接。另外A/B 线要双绞并且远离动力线否则电机启停时的干扰会导致丢包。LoRa 距离不足的原因比较多。首先是天线问题天线要垂直安装周围 10cm 内不要有金属物体。其次是扩频因子扩频因子越大距离越远但速率越慢。我一般用 SF7 到 SF9视距能到 1 到 2 公里。如果环境遮挡严重可以用 SF12但速率会降到 300bps 左右。最后是发射功率LoRa 模块一般可以设到 20dBm但有些地区对发射功率有限制要根据当地规定设置。5.4 常见问题速查表问题现象可能原因排查方法解决方案I2C 无应答地址错误用扫描程序确认地址地址左移一位I2C 无应答上拉电阻缺失示波器看波形加 4.7kΩ 上拉温度跳变通信错误检查 PEC 校验加滤波和重试温度偏高视场有热源检查安装角度调整位置或加遮罩温度偏低环境补偿不足对比参考温度计开启软件补偿RS485 丢包终端电阻未接检查总线两端接 120Ω 电阻LoRa 距离短天线安装不当检查天线周围垂直安装远离金属长时间运行死机I2C 总线锁死看 SCL 是否被拉低加超时恢复机制6. 实操心得与扩展建议这个项目做下来我最大的体会是红外温度传感器的安装位置比选型更重要。同样的传感器装在风管侧面和装在风管顶部读数能差 3°C 以上。因为热空气往上走风管顶部的温度本来就比中心高。所以安装的时候一定要考虑气流方向和热分布最好在风管上多开几个孔对比不同位置的读数选最有代表性的那个点。另一个心得是关于 I2C 总线的。如果总线上挂了多个设备建议每个设备单独走一组上拉电阻不要共用。我试过 OLED 和传感器共用一组 4.7kΩ 上拉结果 OLED 刷新的时候传感器读数会跳变后来分开上拉就正常了。原因是 OLED 刷新时 I2C 总线负载变化共用上拉会导致电平不稳定。扩展方面这个方案还可以加几个功能。一是数据记录用 STM32F303VE 的 SPI 接口接 SD 卡每分钟记录一次温度方便事后分析。二是报警输出用 DAC 输出 0 到 10V 信号接变频器控制风机转速实现简单的温度闭环。三是多传感器组网用 RS485 挂多个节点每个节点测一个区域的温度上位机统一管理。这些扩展在硬件上都已经留了接口软件上只需要增加相应的任务即可。最后分享一个调试小技巧如果 I2C 通信偶尔出错但找不到原因可以在 SDA 和 SCL 上各并一个 100pF 的电容到地滤掉高频干扰。我有个项目在电机旁边I2C 一直不稳定加了电容之后误码率从 1% 降到了 0.01% 以下。这个电容不能太大否则会拖慢上升沿100pF 到 220pF 之间比较合适。