资讯详情 PJ85718DM+STM32F042C6:嵌入式与HVAC双通道温度采集方案
📅 2026/10/22 6:39:02
做嵌入式或者HVAC控制器的朋友应该都有同感测温这件事看起来简单做好一点都不简单。我上一个项目里刚好要把板卡自身热管理和风管温度采集合并到同一套硬件上最开始用NTC热敏电阻做方案结果在校准和防干扰上耗费了大量的时间。后来换成PJ85718DM与STM32F042C6这套组合本地温度和远程温度用一个传感器同时搞定I2C直接读数字量整条链路一下子清爽多了。PJ85718DM是一颗I2C接口的集成数字温度传感器它最实用的地方在于同时支持本地测温感知芯片自身周边环境温度和远程测温通过外部探头监测远端温度点非常适合嵌入式主板热监测和HVAC系统送风/回风温度采集这类场景。搭配STM32F042C6这颗小封装的Cortex-M0内核MCU成本、功耗、外设资源都刚好够用。这篇文章把我从选型到固件的一整套流程写下来包括我踩过的坑和调试时的排查思路给正准备做类似温度监测项目的朋友一个可复用的参考。1. 方案背景为什么一块芯片要干两件事1.1 嵌入式与HVAC的温度监测需求对比先聊透需求不然方案做出来容易四不像。嵌入式设备里温度监测通常分两块一块是板卡自身的热管理比如处理器旁边、电源模块附近需要看温度用来做风扇调速或者降频保护另一块是环境侧比如机箱进风口温度、服务器机房冷通道温度距离主控板有一定距离。传统做法是主板上贴一个测温点远端再拉一个测温点两颗器件占用面积通信接口还不统一软件里得维护两套处理逻辑。HVAC场景就更典型了。控制箱里的控制器需要采集风管送风温度、回风温度、有时候还要采集室外温度通过温度差值来调节风机转速和水阀开度。这些温度点通常离控制器有一段线缆距离而且控制箱里往往强电、弱电共存干扰环境比纯嵌入式设备恶劣得多。这两个场景的共同点是需要多个温度点、温度点之间物理上有距离、数据最终都要数字化上报到上层。PJ85718DM一个芯片提供本地和远程两个通道本地通道管板卡自身温度或控制箱内温度远程通道通过探头引到远端测量点正好一个芯片当两个用。1.2 为什么不用NTC热敏电阻和单点数字传感器如果你只在实验室里调试过几块板子可能体会不到NTC方案在大批量产品里的痛。我项目里最初用的就是NTC问题一大堆。NTC的阻值和温度是非线性关系虽然有B值公式可以计算但实际工程里为了精度还是得做查表或者多项式拟合。更麻烦的是NTC的B值在批次之间存在离散性同一型号不同批次的器件换算出来的温度可能差好几度批量生产时几乎无法避免逐板校准。而且NTC是模拟信号远端测量时线缆电阻会直接串联进分压电路长线压降带来的误差很难修正线缆一旦受到电机、变频器干扰采样值就跟着抖。单点数字温度传感器虽然省掉了查表和校准但一个传感器只能测一个位置。要测本地和远程两个点就得装两颗I2C地址资源占两个PCB面积翻倍成本也翻倍。更重要的是两颗芯片在物理上是两个独立的测温点软件里没法保证两个数据在同一时间戳上采样对需要计算温差的应用来说是个隐患。PJ85718DM的优势就在这出厂校准好的数字输出、一颗芯片双通道、同一时刻可连续读取本地和远程温度软件处理简单生产工序也省了一大截。另外它还带可配置的报警阈值输出后面我会专门讲。1.3 STM32F042C6的选型逻辑很多人一看到温度采集就下意识选STM32F103或者F407其实这个项目完全不需要那么高的性能。PJ85718DM的I2C速率最高也就400kHz每秒读十次都用不到1%的CPU占用用一颗Cortex-M0内核的芯片绰绰有余。STM32F042C6是LQFP48封装Cortex-M0内核主频48MHz片上带一组硬件I2C、一组USART和USB控制器。最关键的是它供电3.3V和PJ85718DM同一个电源轨不需要做电平转换。内部HSI振荡器在大多数温度采集场景下精度足够可以省掉外部晶振成本又省一笔。有人会问软件模拟I2C不行吗能行但我不推荐。软件模拟I2C在单字节读写时问题不大一旦涉及到连续读取、异常总线恢复、以及以后可能要加低功耗模式软件的时序控制就会变得非常别扭。STM32F042C6的硬件I2C外设配置好之后读写操作交给外设完成CPU可以去做别的事这才是工程做法。2. 硬件设计一个I2C拓扑接好本地与远程2.1 最小系统与I2C总线连接先把最小系统搭起来。STM32F042C6可以直接用内部HSI时钟上电后系统时钟走8MHz想要跑到48MHz需要配置PLL。外部晶振可以不加温度采集对时钟精度要求不高HSI的误差在正常工作温度范围内对通信和测温几乎没有影响。PJ85718DM的VDD接3.3VVDD对地放0.1uF陶瓷电容有条件的话再并联一个10uF电解电容吸收低频纹波。SDA和SCL各接一个上拉电阻到3.3V。I2C总线是开漏结构这两个上拉电阻是必需的不是选配。我用的上拉电阻是4.7k实测400kHz速率下波形干净。如果你的SDA/SCL走线很长或者总线上挂了多个设备可以把上拉降到2.2k如果总线非常短只有这一颗传感器10k也能稳定工作。注意不要用1k以下的强上拉边沿会变得太陡长走线时反而容易产生振铃和过冲。PJ85718DM有两个地址引脚A0和A1通过高低电平组合可以配置出4个不同的I2C地址。在一条总线上最多可以挂4颗同型号芯片每颗同时测本地和远程也就是8个温度点覆盖一个典型的HVAC控制箱绰绰有余。地址配置如下A0A17位地址000x48100x49010x4A110x4B2.2 远程温度探头的接法与抗干扰设计远程通道是PJ85718DM最值的部分但也是最容易出问题的部分。它的远程测温原理是通过外接温敏器件随温度变化引起的电气特性变化来反推温度HVAC风管场景一般用探头形式的外部感温元件探头封装在不锈钢套管或者塑料护套里通过屏蔽线引回控制器。嵌入式场景则灵活得多。比如我想测一块扩展板卡上的功率管温度就把远端探头通过软排线引到功率管散热片旁边用导热胶固定。本地通道照常监测主控板环境温度一个芯片两处监测点结构很清晰。远程探头布线有几个原则都是实际调试中总结出来的引线尽量短。非要用长线时用双绞线或者屏蔽线屏蔽层在控制器侧单端接地千万不要两端都接地否则会形成地环路低频干扰反而更大。走线远离继电器、变频器驱动线、开关电源电感这些强干扰源实在绕不开就垂直交叉避免平行近距离走线。探头固定要牢靠。HVAC风管里风速大探头如果只是松垮地塞在风管里读数会随风速波动。如果探头引线特别长可以在控制器端采样引脚对地并联一个0.1uF电容吸收高频耦合噪声。我实际遇到过一次很典型的干扰问题控制箱里有一个大功率继电器每次吸合瞬间远程温度读数就跳一下一度让我怀疑是探头坏了。后来用示波器抓远程通道的引脚波形发现继电器动作时有一串毛刺叠加在信号上。处理方式就是把探头线换成了屏蔽双绞线并且在采样端加了RC滤波问题立刻消失。2.3 供电、去耦和电平匹配传感器电源建议串联一个10Ω左右的小电阻再进VDD和VDD对地的电容组成简单的低通滤波把电源里的高频纹波滤掉一部分。如果你把传感器直接挂在开关电源纹波比较大的电源轨上会发现远程通道和本地通道的读数都偏大或者抖动这是因为纹波通过芯片内部参考电压直接影响ADC转换结果。STM32F042C6的VDDA引脚同样需要滤波。官方手册要求VDDA和VDD之间接磁珠和电容实际项目里如果PCB空间紧张用10Ω电阻加0.1uF电容也能工作但ADC精度和内部参考稳定性会略受影响温度采集对精度要求很高的话还是建议按官方推荐来。因为PJ85718DM和STM32F042C6都是3.3V供电I2C电平天然匹配不需要电平转换芯片。这是一个很大的省心点。如果哪个模块是5V供电就必须在I2C线上做电平转换而不是简单的电阻分压否则会把3.3V器件引脚打坏。3. 固件实现从I2C初始化到温度出炉3.1 硬件I2C初始化要点STM32F042C6的硬件I2C外设初始化并不复杂HAL库提供了现成的结构体配置。一个容易出错的点是Timing参数这个参数直接决定I2C时钟频率很多初学者在这里栽过跟头。I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance I2C1; hi2c1.Init.Timing 0x2000090E; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress1 0; hi2c1.Init.OwnAddress2 0; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(hi2c1) ! HAL_OK) { Error_Handler(); } }Timing参数怎么看这个值其实由四组字段拼成PRESC决定I2C时钟预分频SCLDEL和SDADEL控制数据建立时间和保持时间SCLH和SCLL控制时钟高电平和低电平时间。I2C时钟源为48MHz时参考配置是PRESC4、SCLDEL4、SDADEL2、SCLH6、SCLL8算出来的SCL频率接近400kHz。实际开发中直接用STM32CubeMX的I2C Timing计算工具生成即可但理解参数含义对后续排查总线问题很有帮助。3.2 温度寄存器读取与数据解析PJ85718DM的温度数据以12位二进制补码形式存放在两个寄存器里高字节在前低字节在后。12位数据分布在寄存器的高12位低4位是保留位或者状态位。读出来之后要把两个字节组合成一个16位数再右移4位符号扩展之后乘以分辨率0.0625°C就得到实际温度值。#define PJ85718DM_ADDR 0x48 #define REG_TEMP_MSB 0x00 #define REG_TEMP_LSB 0x01 #define REG_REMOTE_MSB 0x02 #define REG_REMOTE_LSB 0x03 float read_temp(uint8_t msb_reg) { uint8_t reg msb_reg; uint8_t buf[2] {0, 0}; int16_t raw; HAL_I2C_Master_Transmit(hi2c1, PJ85718DM_ADDR 1, reg, 1, 100); HAL_I2C_Master_Receive(hi2c1, PJ85718DM_ADDR 1, buf, 2, 100); raw (int16_t)((buf[0] 8) | buf[1]) 4; return raw * 0.0625f; }为什么组合后要右移4位而不是直接换算因为两个字节一共16位但有效温度数据只在高12位。右移4位之后既完成了位对齐又借助有符号右移实现了符号扩展。比如读回MSB0x1ALSB0x50组合成0x1A50右移4位得到0x1A5十进制421乘以0.0625就是26.3125°C。如果是负温度读回MSB0xF2LSB0x00右移4位得到0xF20这在16位有符号语境下是-224乘以0.0625就是-14°C。12位分辨率听起来很高但要注意分辨率和精度是两个概念。分辨率表示数字量能变化的最小台阶也就是0.0625°C精度则取决于芯片内部的校准和ADC本身的线性度。PJ85718DM这类集成温度传感器的典型精度在正负0.5到1°C之间对HVAC控制来说足够了但如果你指望它测出来能当计量标准用那是想多了。3.3 本地和远程读数的一致性处理本地温度和远程温度分别在连续的寄存器地址里读的时候最好一次性连续读出4个字节然后在本地换算。如果分开单字节读取中间万一碰巧遇到ADC转换边界可能会读出本地温度是上一时刻、远程温度是下一时刻的拼接数据计算温差时会出现一个奇怪的毛刺。连续读取的方式也很简单HAL库的I2C_Master_Receive接口可以一次读多个字节uint8_t buf[4]; uint8_t reg 0x00; HAL_I2C_Master_Transmit(hi2c1, PJ85718DM_ADDR 1, reg, 1, 100); HAL_I2C_Master_Receive(hi2c1, PJ85718DM_ADDR 1, buf, 4, 100); int16_t local_raw (int16_t)((buf[0] 8) | buf[1]) 4; int16_t remote_raw (int16_t)((buf[2] 8) | buf[3]) 4; float t_local local_raw * 0.0625f; float t_remote remote_raw * 0.0625f;读取频率方面HVAC场景1秒采集一次足够温度变化本身是慢变量没必要把I2C总线跑得很满。嵌入式风扇调速可以适当加快到100~200毫秒一次但要注意芯片内部的转换时间读得太快读到的可能还是上一次转换的结果。稳妥做法是每次读取前加一个小的延时或者直接在上一次转换完成后立即发起新一轮读取。3.4 报警阈值配置让传感器自己干活这是很多人忽略的功能。PJ85718DM自带温度阈值比较可以设置高阈值和高阈值滞回温度超过阈值时ALERT引脚输出有效电平。这个功能放在HVAC场景里太实用了比如回风温度超过45°C需要触发排风过去要在MCU里循环比较现在配置好阈值寄存器之后MCU完全不用管甚至可以在低功耗模式下等着ALERT引脚唤醒。阈值寄存器的数据格式和温度寄存器一样也是12位分辨率写入之前先把温度值除以0.0625换算成原始计数值再按高低字节分别写入#define REG_CONFIG 0x04 #define REG_HIGH_MSB 0x05 #define REG_HIGH_LSB 0x06 void set_high_threshold(float temp_celsius) { int16_t raw (int16_t)(temp_celsius / 0.0625f); uint8_t buf[3]; uint8_t cfg 0; HAL_I2C_Mem_Read(hi2c1, PJ85718DM_ADDR 1, REG_CONFIG, 1, cfg, 1, 100); cfg | 0x04; HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR 1, REG_CONFIG, 1, cfg, 1, 100); buf[0] (raw 8) 0xFF; buf[1] raw 0xFF; HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR 1, REG_HIGH_MSB, 1, buf[0], 1, 100); HAL_I2C_Mem_Write(hi2c1, PJ85718DM_ADDR 1, REG_HIGH_LSB, 1, buf[1], 1, 100); }ALERT引脚输出模式有比较模式和中断模式两种。比较模式下温度回落到滞回阈值以下之后ALERT自动解除中断模式下必须通过读取寄存器来清除标志。HVAC应用一般用比较模式就够了省得主机反复去清理状态。3.5 上报协议把温度送到上位机手里温度采集完之后总得有个出口。STM32F042C6的USART1接一个RS485收发芯片走Modbus RTU协议或者自定义走协议都可以。HVAC行业里Modbus RTU是事实标准上位机、触摸屏、组态软件都支持推荐优先考虑。Modbus RTU读温度分布的核心就是功能码03主机发送读保持寄存器请求从机回复寄存器数据。温度信息可以组织成两个寄存器分别存放本地温度和远程温度单位0.1°C用有符号整数的形式表示。帧格式如下字节位置内容说明0设备地址0x011功能码0x032-3起始寄存器寄存器地址4-5寄存器数量寄存器个数6-7CRC16低字节在前如果不想用Modbus自定义协议更简单帧头加长度加数据加CRC就够了。下面是一个简化的上报代码温度值保留一位小数通过串口发出void send_temperature_report(void) { uint8_t report[6]; report[0] 0xAA; report[1] 0x55; int16_t t_local (int16_t)(read_temp(REG_TEMP_MSB) * 10); int16_t t_remote (int16_t)(read_temp(REG_REMOTE_MSB) * 10); report[2] t_local 8; report[3] t_local 0xFF; report[4] t_remote 8; report[5] t_remote 0xFF; HAL_UART_Transmit(huart1, report, 6, 100); }串口波特率我习惯用115200或者9600HVAC控制箱内部通信对实时性要求不高9600也够用。要注意的一点是RS485空闲时必须处于接收状态发送完成后要切换回接收这个切换时序要处理好不然总线上会出现一两个字符的回环主机那边解析就会出错。4. 调试实录与问题排查速查4.1 I2C卡死总线被拉低怎么恢复I2C总线在工程里最容易遇到的一个问题是总线卡死现象是程序跑着跑着SDA线一直被拉低MCU报总线错误从此一切读写都失败。我自己调试的时候也遇到过几次几乎都是从机在传输中间被复位或者干扰打断I2C状态机停在半路SDA还维持着输出低电平。这种问题靠断电重启能解决但不治本。软件上可以做一个总线恢复流程把I2C外设禁用将SCL和SDA引脚重新配置为开漏GPIO手动翻转SCL九个时钟周期同时把SDA释放到高电平。这个操作能够让卡死的从机状态机复位。代码实现大概是下面这个样子void recover_i2c_bus(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_I2C_DISABLE(hi2c1); GPIO_InitStruct.Pin I2C1_SCL_PIN | I2C1_SDA_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(I2C1_SCL_PORT, GPIO_InitStruct); for (int i 0; i 9; i) { HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_RESET); delay_us(5); HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_SET); delay_us(5); } HAL_GPIO_WritePin(I2C1_SDA_PORT, I2C1_SDA_PIN, GPIO_PIN_SET); }这个恢复流程我在几块板子上都试过只要上拉电阻没接错九根时钟基本上都能把总线救回来。但要注意恢复流程对硬件原因导致的总线故障无效如果你的上拉电阻开路或者MCU引脚损坏再怎么翻转时钟都没用。4.2 远程温度读数跳变探头和干扰的纠缠远程温度读数偶尔跳个1到2°C是最难排查的问题之一因为它可能同时由好几个原因叠加。我的排查思路是按顺序来首先检查探头线接触。接线端子氧化、插头松动是最常见的原因。HVAC现场安装时探头线可能被拉扯过看似插好了其实内部已经接触不良这种故障用万用表量电阻往往量不出来因为静态接触是好的震动一下才断开。其次检查采集频率。如果读得太快芯片内部ADC还没完成转换读出来的可能是不完整的值这时候跳变是规律性的。解决方法是给读取周期加限制至少等待一个转换周期再读。然后考虑电磁干扰。继电器吸合、变频器启动都会产生强烈的电磁干扰如果探头线屏蔽不好干扰会直接耦合进采样信号。前面说的RC滤波和双绞线就是干这个用的。软件上可以加滑动平均滤波连续采4到8次去掉最大值最小值再取平均能过滤掉大部分随机跳变。#define FILTER_LEN 8 float filter_temperature(float new_value) { static float buf[FILTER_LEN]; static uint8_t idx 0; float sum 0; buf[idx] new_value; idx (idx 1) % FILTER_LEN; for (uint8_t i 0; i FILTER_LEN; i) sum buf[i]; return sum / FILTER_LEN; }4.3 地址冲突和多板级联一个控制箱里如果放了好几块采集板每块板上PJ85718DM的A0和A1都默认接地那I2C总线肯定全乱了。解决办法也很简单就是按我们最早说的地址表把A0、A1接成不同的组合每块板上分配一个独立地址。需要注意的是地址引脚不能悬空。悬空电平不确定有些板子读出来是0x48有些又是0x49生产的时候一批板子地址乱七八糟排查起来相当头疼。A0和A1一定要明确接地或者接VDD。如果4个地址还不够用那就得分I2C总线。STM32F042C6只有一组硬件I2C但你可以通过I2C MUX芯片来扩展多组总线每组分给不同的地址组。这个方案我实际用过配置起来稍复杂但功能稳定适合点数多的场景。上电的时候可以写一个总线扫描函数在0x48到0x4B范围内逐个探测地址看哪些地址有设备应答这能很快确认地址有没有配错。4.4 读出全0xFF或全0从硬件到软件逐个查全0xFF的典型场景是主机在跟一个不存在的从机通信。总线上只有上拉电阻主机发送地址后没有设备应答读回来的字节自然全是1。这时候检查三件事地址对不对、A0/A1配置对不对、芯片供电有没有起来。全0的情况通常是SDA线被某个设备拉住了。用万用表量一下SDA和SCL的空闲电平正常都应该接近3.3V。如果SDA只有零点几伏说明总线上有设备在占用总线或者某个引脚短路到地。先把所有从机拔掉单独量总线电压如果恢复高电平就一个个挂回从机排查。还有一个小概率是芯片本身坏了。PJ85718DM这类小封装传感器比较容易在焊接过程中受伤尤其是手工焊接时温度过高或者时间过长。如果硬件检查全都正常但芯片就是不工作换一颗新的试试经常当场解决。4.5 温度读数和真实值有稳定偏差如果读数不跳变但整体比实测温度高或者低一个固定值这种情况一般不是芯片的问题而是安装和环境的问题。拿本地通道来说芯片放在PCB上如果附近有LDO、功率电阻这类发热源本地温度读出来自然会偏高这是正常的物理现象不是故障。设计板卡的时候要把传感器尽量放在远离主要发热源的位置或者接受这个偏差后在软件里做补偿。远程通道出现稳定的正偏差最常见的原因是探头没有跟被测物体充分接触。探头绑在风管外面跟插到风管里面读数可以差好几度如果只是为了测量空气温度探头必须裸露在空气中并且通风良好。如果探头要贴物体表面测温度必须用导热胶固定而不是简单地用胶带缠一下。电源纹波导致的读数整体偏移我前面提过也可以作为一个排查方向。给传感器VDD单独加一组LC滤波如果读数恢复正常那就可以确定是电源的问题。我在一个项目中遇到过本地通道读数整体偏高约1°C的情况当时测量电源纹波将近100mV滤波电容补上去之后读数降到了预期范围。4.6 一个小工具I2C地址扫描最后分享一个调试小技巧。系统里挂了多个传感器和从机的时候写一个I2C地址扫描函数非常有用。原理就是依次向每个地址发送一个单字节写请求如果收到ACK说明这个地址上有设备存在。void scan_i2c_addresses(void) { for (uint8_t addr 0x08; addr 0x78; addr) { if (HAL_I2C_IsDeviceReady(hi2c1, addr 1, 1, 10) HAL_OK) { printf(Found I2C device at 0x%02X\r\n, addr); } } }这个扫描函数在板卡初次上电调试的时候尤其有用可以快速确认传感器是否被正确枚举、地址是否冲突。生产环境里也可以保留配合上位机做产线自检。我个人实际操作下来的体会是这套PJ85718DM加STM32F042C6的方案最大的价值不是省了一颗传感器而是把温度采集这件事从“模拟世界的精细活”变成了“数字世界的搬运活”。芯片出厂校准帮你省掉了产线的标定工序双通道帮你省掉了PCB面积和软件分支I2C数字化输出让你可以用一套统一的协议栈去处理所有温度点。HVAC和嵌入式场景里温度监测很少是主角但如果这个配角不稳定整个系统的控制逻辑都会跟着遭殃。最后再分享一个小技巧批量生产的时候每一片PCB板上都要给传感器留出可以软件校准的温度补偿点虽然PJ85718DM出厂已经校准过但PCB布局、探头安装方式都会引入系统性的偏差。预留一个补偿偏移量存储在MCU的Flash里老化测试时标定一次比在几十块板子上逐个焊电阻调精度要省事得多。