1. 为什么温度采集要分"本地"和"远程"两条链路
1.1 嵌入式监控与HVAC控制对温度数据的不同胃口
做HVAC控制面板的时候,温度采集永远是最不起眼但最容易翻车的一环。我这套方案一开始的目标非常朴素:用PJ85718DM搭前端传感器,用STM32L432KC做主控,把设备自身板卡温度和风管里的环境温度同时采回来,并且让上位机也能远程读到。听起来像是一个"读两个温度"的小活,真正动手才发现,"本地温度"和"远程温度"背后的需求差异,直接决定了硬件和固件的架构走向。
嵌入式设备里的温度监测,关心的是"板卡本身有没有过热"。MCU、功放、电源模块旁边放一两路温度点,温度数据基本是给设备自身做保护用的。这类应用对绝对精度要求不高,但对响应速度、异常值的识别要求比较急。比如功放突然过载,芯片表面温度几秒钟内可能冲上去十几度,如果滤波太重,等数值缓缓走出来,设备已经热损伤了。
HVAC的场景正好反过来。风管回风温度、送风温度变化非常缓慢,一小时内平滑波动几度是常态。这种场合需要的是长期稳定、抗干扰、可标定的绝对温度,对"下一秒跳变多少"反而不敏感。数据不仅要给本地控制器做PID调节,还要让楼宇自控系统周期性地拉取,做能耗分析和设备联动。所以HVAC侧的通信协议要规范,寄存器映射要清晰,断线、短路、传感器老化都能被上位机感知。
同一个系统里同时做这两种监测,意味着不能用一个粗暴的"读温度-送出去"的线性流程。需要给两个测温点分配不同的采样策略、滤波策略和上报策略,否则就会陷入"嵌入式侧嫌太慢、HVAC侧嫌太跳"的两头不讨好。
1.2 这条链路里PJ85718DM负责什么,STM32L432KC又负责什么
在我的项目里,PJ85718DM定位成"温度采集前端"。它负责把外部测温元件调理成数字量,通过I2C接口交给MCU;芯片内部会完成高精度ADC转换、温度数据寄存、状态标志和基本的断线诊断。也就是说,MCU不用再去关注仪表放大器怎么调零、冷端补偿怎么做、ADC参考电压稳不稳,只需要定期读寄存器,把数字直接变成物理温度。这样做的好处是硬件BOM非常干净,也方便后续换用不同测温元件时只改前端芯片配置,不动主控逻辑。
STM32L432KC在这里的角色更像"大脑加管家"。它管三件事:第一,通过I2C轮询PJ85718DM,把所有测温点读回来;第二,把温度换算、滤波、越限判断做掉,并直接驱动本地风扇、继电器或报警灯;第三,跑一个稳定的Modbus RTU从机协议,让远程中控能读到"本地温度"和"远程温度"。选L432KC的原因很直接:Cortex-M4F内核带硬件浮点,滤波和线性校正在FPU上跑起来几乎没有负担;同时它的低功耗能力很突出,后面我会详细说省电模式怎么用。如果你手里有别的STM32型号,这套思路整体也能搬,只是引脚和低功耗入口要微调。
两条链路的分工,我画得很清楚:PJ85718DM只负责"把温度变数字",不负责决策;STM32L432KC不关心传感器内部怎么测温,只管把数字变成可信赖的温度值,再按场景分别投递到"本地执行器"和"远程通信口"。边界一旦清晰,开发时可以并行推进,排错时也不用满板子乱摸。
2. 硬件上怎么把它们接在一起:电路连接与最容易被忽视的细节
2.1 最小系统的引脚分配
我的硬件连接相当传统,但有几个点在打样之前特别容易漏掉。电源方案是PJ85718DM和STM32L432KC共用一组3.3 V供电,L432KC本身可以从USB或者外部稳压器取电。为了给PJ85718DM一个尽量干净的供电,我在传感器电源脚旁边放了100 nF陶瓷电容加10 uF钽电容的组合,电容尽量靠近芯片电源脚。HVAC控制器里常有继电器和风机,这些负载启停会造成电源毛刺,如果传感器供电和继电器驱动供电不做隔离,温度读数会周期性跳动,看起来就像"空调一开温度就乱跳"。
I2C引脚分配方面,我用的是PA8做I2C1_SCL,PA9做I2C1_SDA,这两个引脚在L432KC的LQFP32封装上都是普通GPIO复用,做数字通信足够。D0和D1对应的UART引脚我留给了RS485远程通信,没有让I2C和UART挤在一起,方便后期调试。给PJ85718DM的A0、A1地址引脚我都接了硬线,不通过跳线设置,避免现场被人乱拨导致地址冲突。
下表是一份实际可用的引脚分配参考:
| 功能 | STM32L432KC引脚 | 目标设备 | 备注 |
|---|---|---|---|
| I2C1_SCL | PA8 | PJ85718DM SCL | 复用开漏,外接上拉 |
| I2C1_SDA | PA9 | PJ85718DM SDA | 复用开漏,外接上拉 |
| UART2_TX | PA2 | RS485收发器 DI | 发送数据 |
| UART2_RX | PA3 | RS485收发器 RO | 接收数据 |
| RS485方向控制 | PA1 | 收发器 DE/RE | 高发低收 |
| 本地风扇控制 | PB0 | 继电器/PMOS | 跟HVAC温控联动 |
PA8和PA9并不是L432上唯一的I2C引脚,但在我这块板子上它们离晶振和电源输入最远,被继电器干扰的概率最小。如果你画的板子空间紧张,可以换PB6、PB7这种备用I2C映射,但换完之后记得重新检查重映射寄存器和中断优先级。
2.2 I2C总线的上拉、去耦和电平匹配
I2C总线是最容易出现"时好时坏"的地方。PJ85718DM的SCL和SDA都必须接上拉电阻,阻值我选了4.7 kΩ。如果是400 kHz高速模式,或者总线上挂了4个以上的器件,建议用2.2 kΩ,否则上升沿变缓会导致波形畸变;反之如果线很短、器件少,10 kΩ也能用,但不要超过10 kΩ。一个很常见的问题是:STM32L432KC的I2C引脚配置成开漏模式后,内部没有上拉,如果你只上了MCU侧的一条总线上拉、传感器侧的从机模块板载也有上拉,并联后总阻值可能低于1 kΩ,总线信号会变小,读出来的温度偶尔会变成0xFFFF。我的经验是:总线上只保留一组上拉,位置放在MCU引脚附近,而不是每一块子板都放。
去耦电容的位置比容值更容易被忽略。给PJ85718DM的供电脚放了电容还不够,我还需要在I2C数据线上避免走线过长。如果传感器离MCU超过20 cm,就不能再用普通杜邦线直连了,要么改用双绞线并降低I2C速率到100 kHz,要么把PJ85718DM改成“采集完通过UART发送结果”的模式。我踩过一次坑:把传感器用30 cm排线接到主板,I2C 400 kHz下正常,一到冬季环境温度降到零下,波形边沿变差,偶尔会读到跳变的温度。后来排查发现是低温下排线寄生电容加大,加上4.7 kΩ上拉偏弱导致的。
如果系统里还有3.3 V以外的器件,比如5 V供电的OLED显示屏或者5 V单片机,一定不要直接把I2C引脚互连。PJ85718DM这类芯片通常不是5 V tolerant的,插上5 V器件,轻则读数异常,重则烧掉输入钳位二极管。稳妥做法是加电平转换芯片,或者只和同电压域器件通信。这里我见过太多人图省事直接飞线,最后排查到半夜才发现问题。
2.3 给HVAC现场的远距离传感器留好接口
HVAC应用里,传感器不一定就在主控板上。回风温度传感器可能要装到离控制器几米甚至几十米的风管里。这时候不能机械地把PJ85718DM理解为"贴片焊在板子上的传感器",它的带外部输入版本可以把远端测温元件引过来。但远端信号线在工业现场容易被风机变频器干扰,所以我在传感器信号输入端额外加了一个RC低通滤波器,时间常数取10 ms左右,既能滤掉变频器的高频噪声,又不会让温度响应慢到影响控制。
更关键的是信号线的屏蔽和接地。我试过用普通双绞线直接接传感器,远端风机一开,温度波动±1 °C以上;后来把屏蔽层单端接地、并把信号线远离强电线缆之后,波动压到了±0.1 °C以内。屏蔽层只能一端接地,否则会在屏蔽层里形成地环路电流,反而引入更大的干扰。如果你要在同一根线槽里走传感器线和供电线,务必把它们隔开至少10 cm。
另外一个容易被忽视的点是传感器接线端子。HVAC温控器内部空间小,但端子间距不能太密,否则潮湿环境下会有漏电。我见过一台设备在南方回南天时温度一直偏高,拆开发现传感器端子间有冷凝水膜,相当于在测温元件并了一个几十千欧的电阻。把端子换成带防潮罩的型号,涂三防漆之后,问题就消失了。
3. 固件第一步:把PJ85718DM的寄存器读出来并换算成温度
3.1 初始化与设备ID检查
PJ85718DM这种带数字接口的传感器,直接读寄存器之前先做设备ID检查太重要了。I2C总线上如果没接对器件,或者地址算错,MCU端看起来就是"通信失败",很难区分是硬件没焊好还是软件配错。我这里在初始化流程里固定读一次ID寄存器,把它和预期值比对,不匹配直接点亮故障LED,同时通过Modbus上报"传感器离线"。
设备地址的计算是一个经典坑点。PJ85718DM的7位I2C地址由芯片固定位和A0、A1引脚电平组成。比如固定高4位是0xA,A1A0组合成低2位,那么基础地址可能是0x48左右。STM32HAL库的HAL_I2C_Master_Transmit()需要传入的是左移一位后的8位地址,也就是写地址。我见过很多人在这个地方把0x48直接传进去,结果地址实际变成了0x90,自然找不到设备。下面这段是我稳定运行的初始化代码:
#define PJ85718DM_ADDR (0x48 << 1) // 7位地址0x48左移成8位写地址 uint8_t pj_id = 0; uint8_t id_reg = 0x03; HAL_StatusTypeDef st = HAL_I2C_Master_Transmit(&hi2c1, PJ85718DM_ADDR, &id_reg, 1, 50); if (st != HAL_OK) { handle_error("I2C transmit failed"); } HAL_I2C_Master_Receive(&hi2c1, PJ85718DM_ADDR | 0x01, &pj_id, 1, 50); if (pj_id != 0x1D) { handle_error("PJ85718DM ID mismatch"); }读ID用的是读地址,也就是7位地址左移一位后再加1。HAL库内部已经把地址处理做了封装,但你心里必须清楚每个参数的含义。我在调试阶段在这类地址问题上来来回回浪费了不少时间,后来统一在代码里注释清楚"这里是8位地址,不是7位",再没犯过同类错误。
3.2 原始数据解析与温度换算
PJ85718DM内部通常把温度以16位有符号数存到两个连续寄存器里,温度寄存器的LSB精度和偏移量需要根据手册确定。我手上这一版芯片配置是0x00和0x01寄存器组成一个16位补码值,LSB对应0.01 °C。也就是说,如果读出的16位原始值是250了,实际温度就是2.50 °C;如果是-50,那就是-0.50 °C。由于STM32L432KC带单精度FPU,直接转成浮点计算非常合适,代码可以写成:
int16_t raw = (int16_t)((reg_h << 8) | reg_l); float temp_c = raw * 0.01f;不要直接对原始整数做大除法或者移位试图“挤精度”,补码转换是必须的。很多人读出来一个负数,直接用无符号运算,结果-0.5 °C变成了655.35 °C,控制器直接认为过热甚至触发停机。正确做法是先拼成int16_t再扩展,编译器会处理符号位。如果你把寄存器高低字节的定义搞反,常见的表现是温度在小数部分乱跳,但整数部分大体正确,排查时可以对比一下十六进制原始值。
还有一点,PJ85718DM寄存器里可能包含一个"数据就绪"标志位。我坚持在每次转换前先读状态寄存器,等就绪位被置位后再读取温度数据。虽然大多数情况下连续读取也没有问题,但在HVAC这种需要长期稳定运行的场景里,等一个就绪标志的成本可以忽略,却能把数据不同步的风险挡在外面。代码层面也就是在读取温度寄存器前多了一次状态读。
3.3 数据滤波:移动平均、低通一阶怎么取舍
读回来的温度不能直接用,原因不是芯片不准,而是控制器现场的干扰会让单次采样出现少量离群值。滤波动前先想清楚:这个温度点将来是给谁用的?给本地过温保护用的,滤波必须轻;给HVAC风管温度做调节用的,滤波可以适当重一点。我在系统里对两个测温点采用了完全不同的滤波策略。
本地板温我用的是一阶IIR低通,系数alpha取0.3。这个系数意味着新采样占30%,历史值占70%。它的好处是计算只需要一个历史值,RAM占用极低,而且系数的调节非常直观:alpha越大越敏感,越小越平滑。每次采样后执行:
static float filted_local = 25.0f; filted_local += 0.3f * (temp_c - filted_local);HVAC回风温度我用的是5次循环移动平均。采样周期1秒,移动平均窗口就是5秒,对于动辄十几分钟才变化几度的风管温度已经够用,而且移动平均比低通滤波更容易理解,现场维护人员看代码时不会产生"这个0.3是啥意思"的困惑。移动平均的代价是需要保存一个5点数组,每读一个新值把最旧的值挤掉。代码就这么简单:
static float samples[5]; static uint8_t idx = 0; static float sum = 0.0f; sum -= samples[idx]; samples[idx] = temp_c; sum += samples[idx]; idx = (idx + 1) % 5; float hvac_temp = sum / 5.0f;这两种滤波的取舍要特别说清楚。移动平均对大范围毛刺的抑制效果好,但对最新的变化响应是固定延迟5秒;低通滤波响应快一些,可如果现场偶尔有一个极端离群值,低通滤波会被"带偏"一个尖峰。因此我在本地过温保护逻辑里还加了一级"限幅滤波":如果新采样值比上一次滤波值跳变了5 °C以上,就只按5 °C修正,而不是直接全盘接受。这能有效避免继电器打火瞬间产生的电磁干扰让温度误判。
4. 远程温度上报:Modbus RTU从机的实现要点
4.1 远传方案怎么选:RS485优先,但别忽略无线方案
远程温度的"远程"二字,在嵌入式和HVAC两个场景里含义不太一样。嵌入式设备通常是板级监控,远程温度从I2C到主控再到调试串口,能在上位机软件里看到就够了;HVAC系统则常常要把回风温度、送风温度送到楼宇自控系统,一般通过RS485总线组网,走Modbus RTU协议。我做这套系统时优先选了RS485加Modbus RTU,原因有三点:抗干扰能力强,两线差分传输在几十米内稳定可靠;协议简单透明,任意组态软件和PLC基本都能直接读;节点扩展容易,一条总线上最多挂32个从机,对HVAC足够。
如果你要的设备是纯电池供电、布线困难,那可以改用LoRa或者2.4 G无线透传模块把温度数据定期发出去。无线方案的优点是省去现场施工,但代价是实时性变差、功耗增加、通信链路不稳定。我个人的项目经验是:工业现场能上有线就上有线,无线留给改造项目;无线只做"周期上报"而不是"实时控制",这样即使通信中断几分钟,也不会影响本地温度保护闭环。
RS485收发器需要MCU用一个GPIO控制DE和RE方向。发送前把方向脚拉高,UART发送完成后必须延时一点再把方向脚拉低,否则最后一个字节的停止位会被掐掉,上位机收到的CRC校验永远不对。这个延时的长度跟波特率有关,9600波特率下1个字节大约1 ms,我通常发送完一帧后延时2 ms再拉低方向脚。先把方向控制这个机制理解透,后面调Modbus怎么都顺。
4.2 从机寄存器映射与状态机代码结构
Modbus RTU从机的核心是寄存器映射要清晰。我在这套系统里定义了一个只有3个保持寄存器的简单映射:
| 寄存器地址 | 寄存器含义 | 数据格式 |
|---|---|---|
| 0x0000 | 本地板温 | 有符号整数,单位0.01 °C |
| 0x0001 | 远程/回风温度 | 有符号整数,单位0.01 °C |
| 0x0002 | 状态字 | 位0=传感器1在线,位1=传感器2在线,位2=本地过温报警 |
Modbus寄存器和物理字节序也要提前定好。工业组态软件读到的寄存器不一定按你发送的顺序解析,我们统一采用高字节在前,也就是大端模式。比如本地板温25.30 °C,对应整数2530,十六进制是0x09E2,发送时先发0x09再发0xE2。一旦定好顺序,上位机端和MCU端都不要再改。
从机协议的核心是UART接收中断加状态机。收到一帧完整的Modbus RTU报文后,先静默3.5个字符时间判断帧结束,然后对报文做CRC校验。校验通过后判断功能码,0x03表示读保持寄存器,0x06表示写单个寄存器。这里我建议把解析状态机做得尽量短平快,不要在大循环里逐字节阻塞等待,否则总线一忙就丢帧。下面是一个简化的功能码0x03处理框架:
uint16_t modbus_crc16(uint8_t *buf, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= buf[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001; else crc >>= 1; } } return crc; } void handle_read_holding_reg(uint8_t *req, uint8_t *resp) { uint16_t addr = ((uint16_t)req[2] << 8) | req[3]; uint16_t qty = ((uint16_t)req[4] << 8) | req[5]; // 根据addr和qty把对应温度整数填入resp,最后附上CRC }CRC计算必须严格按照Modbus标准的多项式0xA001,不要自己发明。很多人在上位机通信卡在CRC上,要么漏了低字节交换,要么把CRC追加顺序搞反。你在逻辑分析仪上对比过一帧正确报文后,就会发现本质上是一个死板的计算流程,完全不需要奇技淫巧。
4.3 远程和本地怎么协同:上位机只读,本地闭环自控
远程监测和本地控制,最容易犯的错是把控制决策交给上位机。HVAC可靠性要求高,一旦通信断掉,风管温度失控会造成很大麻烦。正确的分工是:本地STM32L432KC独立完成所有保护与控制逻辑,比如温度超过设定上限时直接开风扇或关加热;远程Modbus只负责把当前温度值、状态字"透明"地上报给上位机,上位机可以发指令调节目标温度,但硬保护和上限逻辑永远在MCU本地执行。
我用了一个简单的温差控制逻辑做演示:当风管回风温度高于目标温度2 °C并且持续30秒,打开风机;低于目标温度1 °C并且持续30秒,关闭风机。这个滞后区间(2 °C开、1 °C关)就是为了防止继电器在临界点频繁吸合。如果你直接回差设成0,继电器会像打点计时器一样哒哒哒不停,电气寿命迅速下降。
协同问题上还有一点:上位机"读"数据时,MCU端不要因为正在响应Modbus而暂停温度采样和本地控制。L432KC的Cortex-M4F跑在80 MHz,一帧Modbus报文解析也就几十微秒,完全不用担心采样被阻塞。真正要注意的是RS485收发器方向切换和主循环时序,别在中断里做浮点滤波计算,我一般把浮点运算放在主循环,Modbus中断只负责填充接收缓冲区。
5. 低功耗与可靠性:STM32L432KC的省电模式和运行保护
5.1 为什么选定STM32L432KC,低功耗模式能省到什么程度
STM32L432KC不是一颗性能怪兽,但它非常适合这种"实时响应要求不高、长期稳定运行要求高"的监测节点。80 MHz主频对Modbus和浮点滤波绰绰有余,256 KB Flash和64 KB SRAM对三个温度点而言有点浪费,但剩余空间可以给将来OTA升级或者协议扩展留余地。真正让我选它的原因是低功耗模式。
在嵌入式板级监控场景里,如果系统由电池或小功率能量收集电路供电,那么MCU大部分时间应当处于低功耗模式。L432KC支持STOP2模式,在这种模式下,CPU和大部分数字外设停止工作,但SRAM保持内容,RTC和几个唤醒引脚可以工作。根据ST官方的典型参数,STOP2下电流可以降到1 µA左右,但这需要所有GPIO配置成合适状态,并且关闭不用的外设时钟。实际项目里,如果你还挂着RS485收发器和PJ85718DM,整板功耗会取决于外设,MCU这颗芯片本身的电流很小,不能指望它拯救整个系统的电流危机。
HVAC场景一般有24 V交流供电,低功耗不是重点,但省电模式依然有好处:可以降低长期运行的温升,延长板上电解电容寿命。我的建议是:电源条件允许的场合,仍然用"定时唤醒-快速采样-继续睡眠"的节奏,让系统运行有呼吸感,而不是让MCU永远全速空转。
5.2 STOP2定时唤醒采样,别在主循环里死等
很多初学者的写法是主循环里放一个HAL_Delay(1000),每1秒读一次温度。这在有市电供电的HVAC控制器里问题不大,但在电池供电场景就等于一直全速运行,功耗高得离谱。正确做法是用RTC闹钟定时唤醒MCU,让它在醒来后完成温度采集、滤波、Modbus响应处理,然后重新进入STOP2。
L432KC上配置RTC唤醒闹钟,并把系统切入STOP2的流程大体是这样:先配置RTC的异步预分频和同步预分频,让RTC走1秒时基;再设置闹钟A的秒值,使能RTC闹钟中断;最后在进入STOP2前确保唤醒源已经就绪。进入STOP2的代码用HAL库很简单:
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);这里有个大坑:从STOP2模式唤醒后,系统时钟默认可能回到MSI低速率时钟,而你原本用的是PLL倍频到80 MHz,所以必须在醒来的第一时间重新配置时钟。否则I2C和UART的波特率全错,温度读出来全是乱码。我曾经在这里折腾了一个晚上,后来在唤醒后的第一行就调用SystemClock_Config(),问题立刻消失。
除了时钟,I2C外设状态在低功耗模式下也可能需要重新初始化。稳妥起见,我会把MX_I2C1_Init()也放在唤醒后的初始化序列里。不要觉得多初始化一次浪费,对于唤醒周期是秒级来说,这点微秒级的时间完全可以忽略。
5.3 看门狗、I2C总线和RS485的共地问题
温度监测设备往往会被人遗忘在机柜角落里,程序一旦卡死,没有人会马上去按复位键,所以看门狗必须加。我使用STM32的IWDG独立看门狗,超时周期设为2秒。主循环每执行一轮就喂一次狗,注意不要在定时器中断里喂狗,否则主循环卡死时中断还可能让看门狗误以为系统还活着。喂狗操作放在主循环的末尾,和温度采集、Modbus处理在同一个执行路径上,才算真正的"系统级心跳"。
I2C总线的另一个隐蔽坑是"总线挂死"。如果PJ85718DM在供电异常时把SDA拉低,MCU端再发START信号也会被钳位,通信彻底瘫痪。我在每次通信失败后会尝试一个恢复流程:先把SCL切换成普通GPIO,手动翻转9个时钟脉冲,让从机释放SDA,然后再把I2C外设复位。这套机制在实验室里看似多此一举,但现场总线被雷击或绝缘破损干扰时,它能省掉一次上门维修。
最后是RS485总线的地线。很多人在现场只接A、B两根线,不接地线,认为差分传输不需要公共地。这在短距同电源系统里勉强能跑,但跨设备、长距离时共模电压可能超过收发器承受范围,轻则通信误码,重则烧毁收发器。我的做法是RS485总线里至少拉一根地线用于信号参考,保护地单独处理,不能让大电流从信号地回流。共地问题在HVAC这种强电弱电混装的场合尤其重要,值得在设计和施工阶段都盯紧。
6. 精度校准、实测数据和几个让我印象深刻的坑
6.1 标定流程与误差预算
PJ85718DM在出厂时做过基本的数字校准,但系统级误差还包含供电电压、PCB走线、传感器长线阻抗等,所以上电后第一件事就是做一次整机标定。我的标定方法很土但很有效:把系统放在恒温箱里,设置四个温度点,比如5 °C、20 °C、35 °C、50 °C;每个点稳定30分钟后,同时记录PJ85718DM读数和参考温度计读数,用两点线性校正把"芯片温度值"映射成"真实温度值"。
线性校正公式是corrected = (raw_temp - offset) * gain。两个校准参数让整条温度曲线平移和缩放。实际操作中,我只在20 °C和50 °C两个点做了标定,5 °C点作为验证点来检查曲线是否平滑。HVAC测温范围通常只有0 °C到60 °C,两点标定已经足够;如果你做的是低温冷库监测,建议在-20 °C也加一个点,防止低温段非线性让偏差变大。
误差预算也要心里有数。整个链路里,传感器本身的非线性、ADC量化误差、信号线上的噪声、滤波带来的延迟误差,以及参考温度计自身的误差,都会叠加到最终数据上。我给自己定的目标是系统误差不超过±0.5 °C,实测下来在恒温箱里这个目标可以达到,但现场安装后因为线缆和风管温度梯度,通常会多出0.2 °C左右的偏差。不要一上来就追求0.1 °C的绝对精度,那是计量级仪表才该干的事。
6.2 实测数据:本地板温与HVAC回风温度的对比
我自己跑了大概两周的连续记录,截取一组比较有代表性的数据放在这里。系统安装在一个模拟风柜里,回风传感器离风机约1.5米,本地板温传感器在MCU附近。环境温度大约23 °C,风机每天间歇运行12小时。
| 时间 | 本地板温 (°C) | 回风温度 (°C) | 状态备注 |
|---|---|---|---|
| 00:00 | 32.8 | 23.4 | 风机停止 |
| 00:30 | 33.1 | 23.5 | 风机停止 |
| 08:00 | 34.2 | 24.1 | 风机启动后板温略升 |
| 12:00 | 38.5 | 27.6 | 午后环境温度升高 |
| 12:30 | 37.0 | 26.8 | 风机关闭,回风温度回落 |
| 20:00 | 35.6 | 25.2 | 正常运行 |
从数据里能看到两个很有意思的现象。一是本地板温和回风温度的波形基本同步,但板温始终比回风温度高10 °C左右,这是MCU自身功耗带来的稳定温升,不能当作环境温度用。二是风机启动后,板温在半小时内反而缓慢上升了将近1 °C,一开始我以为读数错了,后来发现是因为风机运行导致控制器整体电流增加,电源模块发热传导到了主板上。这说明"板温"和"环境温度"是两个完全不同的物理量,设计时千万别混着用。
Modbus远程读取在这两周里非常稳定。我用一个简易上位机脚本每30秒读一次两个寄存器,两周内没有出现CRC错误或掉线记录。总线长度大约30米,9600波特率,A、B两线双绞,信号地接在同一配电箱的接地排上。如果你遇到远程读取偶尔超时,可以先抓Modbus报文看一眼CRC和地址,不要急着怀疑MCU程序。
6.3 回头再看,哪些坑最不值得踩
第一个最不值得踩的坑是没有提前读PJ85718DM的手册就开写代码。刚开始我以为寄存器地址和某款常见数字温度传感器完全一样,结果初始化时状态标志读错位,温度数据在0 °C附近反复跳变。后来老老实实对着手册核对每个寄存器位定义,半小时就解决了,之前的调试时间至少浪费了半天。任何型号相近的芯片,都不能默认寄存器兼容,这是做硬件开发的第一课。
第二个坑是滤波参数没有按应用场景区分。最开始本地和远程两条链路共用一个移动平均滤波,数据曲线倒是很平滑,可本地过温保护反应慢了将近10秒,幸好是在测试阶段发现的。后面把本地改成限幅加低通、远程保留移动平均之后,双方表现都对了。这个教训让我养成了一个习惯:写滤波代码之前先问"这个温度值给谁消费",而不是无脑套同一个函数。
第三个坑是RS485方向控制的延时不够。第一版固件发送完后立刻拉低方向脚,上位机PC偶尔能收到数据,但PLC总是报CRC错误。用示波器抓波形才发现停止位被截断了。多加2 ms延时后,PLC端通信一次通过。这种问题只会在"上位机比较挑"的时候暴露出来,反而更难排查。
调试这套系统的过程中,我最大的体会是:温度监测项目几乎没有哪一个环节能靠"堆钱"或"堆性能"绕过去。选一颗好用的传感器前端芯片,配一颗合适的低功耗MCU,把寄存器读对,把滤波做稳,把通信协议写规范,每一步都是细节,但每一步也都有标准答案。希望这篇记录能帮后面做嵌入式板级监控或者HVAC温控项目的朋友少踩几个坑。