做暖通(HVAC)控制柜,最头疼的往往不是主控跑不动,而是温度点位又多又散。送风、回风、新风、盘管进出水、室外补偿、柜内散热,随便一数就是八到十几个点。早些年我喜欢把所有传感器直接接到主板 AD 引脚上,结果一到现场拉线就后悔:十几米长的模拟信号线靠近变频器和风机盘管,读数一会儿跳 0.3℃,一会儿整体偏 1℃。后来我把设计思路改成了“本地贴片 + 远程探头”双通道模式:本地温度由采集前端就近采样,远程温度走三线制或数字接口回传,主控只负责汇总、滤波和协议上报。这套方案的核心器件就是 PJ85718DM 温度采集前端和 PIC32MZ2048EFH144 主控 MCU,既绕开了长线模拟信号的衰减问题,也让主板的接口变得干净统一。今天这篇就按我做这个项目的真实推进顺序,从选型、电路计算、固件框架到现场校准完整讲一遍,正在做 HVAC、机房监控、储能或冷链数据采集的朋友,可以直接拿来参考。
1. 项目到底要解决什么问题
1.1 HVAC 控制器的温度点位需求
HVAC 系统里的温度不是“测一个就行”,它必须形成多点位的闭环。以一台常规的空气处理机组为例,典型的温度点位包括:
送风温度:判断处理完的空气是否达标,直接影响送风阀开度和加热/制冷设备的启停。回风温度:用于估算房间负荷,和设定值做偏差后,PID 调节水阀开度。新风温度:联动新风阀,冬天防冻、夏天做焓值补偿。盘管进出水温度:判断水侧换热效果,发现温差异常时触发防冻或能效报警。室外温度:用于工况判断和初、加温切换。控制柜内部温度:保护主控板、电源模块、继电器等电气件,防止柜内积热。
这些点在结构上分布得很散。柜内到风道探头通常是 2~10 米,到水管和室外测温点甚至要拉 30~50 米。如果不把本地和远程分开设计,只靠一块主板的 ADC 去抗长线干扰,现场会非常痛苦。所以说“本地 + 远程”并不是为了显得专业,而是 HVAC 应用本身逼出来的需求。
1.2 为什么采用“采集前端 + 主控”两级架构
以前做测温方案,主流有这几条路:全模拟电路自己搭,板上焊运放、基准源,ADC 用 MCU 内置。这样做成本低,但校准和长线抗干扰要自己扛;用 1-Wire 数字温度传感器,比如常见的那类单总线芯片,接线少、协议简单,但长距离供电不稳、总线上设备一多就容易出时序问题;用 4-20mA 变送器,抗干扰确实强,可每一路变送器都不便宜,功耗也高。
这套项目里,PJ85718DM 扮演的是温度采集前端模块的角色。它把信号调理、恒流源激励、ADC 采样、校准参数和数字接口封装在一起,主控只需要通过 I2C 或 UART 去读寄存器,就能拿到高分辨率的温度数据。对开发来说,最大的好处是不用为每一个传感器单独搭调理电路,也不用自己算 NTC 分压网络,模块已经把线性化、查表、断线检测都处理好了。
PIC32MZ2048EFH144 则是主控层。选它的原因不是单纯“性能强”,而是它把嵌入式连接需要的资源都备齐了:200MHz 主频、2MB 闪存、512KB RAM,跑 Modbus 协议栈、BACnet 协议、PID 算法和本地历史记录毫无压力;外设层面,多路 UART、I2C、SPI、CAN、USB 和以太网 MAC 都集成在里面,后续接触摸屏、RS-485 网关或者直接拿来做 以太网 网关,都不用换平台。
这两级架构的价值在于分工:前端管物理信号的准确采集,主控管数据逻辑和对外通讯。以后想换精度更高的探头,只要前端的激励参数能匹配,主控几乎不用动。
1.3 本地与远程的分工边界
所谓本地温度,我把它定义为控制板柜内、距离主控不超过 0.5 米的传感器点,典型代表是板上热敏电阻、柜内空气温度、电源模块表面温度。远程温度则是分布在管道、风道、室外或远端区域的点,典型代表是送风温度、盘管进出水温度、室外补偿温度。
两者在设计原则上有明确区别。本地温度追求“快”:采样周期定为 100~200ms,主要是为了柜内保护和过温快速断电。远程温度追求“准”:采样周期放宽到 300ms~1s,因为探头本身的热时间常数很长,采得太快没有意义,反而会把抖动带进控制回路。还有一条原则:远程某一路探头开路或短路时,只报警、只屏蔽这一路,绝不能把整台控制器拉停。这一点在 HVAC 现场非常重要,一个探头断线就让整个空调停机,客户会疯掉的。
2. 硬件链路设计与参数计算
2.1 本地 NTC 测温口的分压计算
先说最常见的本地测温:10kΩ NTC 热敏电阻,B 值 3435。PJ85718DM 接收 NTC 时,硬件上可以外接一个精密上拉电阻到基准电压,NTC 对地,然后采集中间分压点的电压。
NTC 的阻值随温度变化公式是:
R = R25 × exp(B × (1/T − 1/T25))T 和 T25 都取开尔文温度。用这个公式可以算出几个典型点的阻值:
| 温度 | NTC 阻值 | | -20℃ | 约 77.6kΩ | | 0℃ | 约 28.8kΩ | | 25℃ | 10kΩ | | 50℃ | 约 4.1kΩ | | 70℃ | 约 2.2kΩ |
如果上拉电阻也用 10kΩ、供电 3.3V,采集点电压 V = 3.3 × R_NTC / (R_NTC + 10k)。于是得到:
| 温度 | 采集点电压 | | -20℃ | 约 2.92V | | 0℃ | 约 2.45V | | 25℃ | 1.65V | | 50℃ | 约 0.96V | | 70℃ | 约 0.60V |
这个分压网络在整个 -20℃~70℃ 范围内跨度约 2.3V,对 12 位 ADC 来说能占满大部分量程,分辨率非常充裕。而且工作电流只有 0.1mA 级别,NTC 自身功耗不到 0.3mW,自热误差可以控制在 0.1℃ 以内。如果用 1% 精度的普通电阻做上拉,温度误差大概会多出 0.2~0.4℃,所以我直接选了 0.1% 精度、25ppm 温漂的电阻。这个成本不能省,它决定了整个测量链路的基准。
2.2 远程测温链路:三线制 PT100/PT1000 的接法
远程的水管温度和风道温度,我最推荐的做法是接铂电阻,也就是 PT100 或 PT1000。为什么不用 NTC?因为 NTC 的一致性相对差,而且长距离下 B 值漂移会直接叠加到系统误差里。铂电阻虽然贵一些,但长期稳定性好,市场上也很成熟。
铂电阻接线方式直接决定精度,差别特别大。两线制:激励线和检测线共用,线上电阻直接串进测量回路。以铜芯线每米 0.1Ω 估算,50 米就有 5Ω 误差,对 PT100 来说相当于十几摄氏度的偏差,完全不可用。四线制:激励和检测完全独立,精度最高,但需要四根线,施工成本高。三线制:两根激励线、一根检测回线,只要前三根线的线阻一致,就能把线阻误差抵消掉大半,现场施工也能接受。
具体接线时,PJ85718DM 这类前端模块的 RTD 通道通常内置恒流源激励,通过仪放测 RTD 两端的差分电压,三线制会多出一根检测线做反馈补偿。我个人的建议是:只要满足 0.5℃ 的控制精度,三线制足够了,四线制留给计量级场景。
如果远程点位实在太多,也可以走数字总线方案,比如用单总线的数字温度传感器挂一串。但我实际用过之后发现,长距离下单总线的上拉电阻、供电电容、时序时序调整都会让人头皮发麻。除非点位少、距离短,否则我不建议把它用在专业 HVAC 控制器上。
2.3 主控侧接口:I2C 上拉、电源和防护
PJ85718DM 与 PIC32MZ2048EFH144 之间的连接不复杂,核心就是 VCC、GND、SCL、SDA,可能还有 ADDR 地址引脚和 ALERT 报警输出。
电源这一块要单独说。前端模块的供电不要直接从继电器驱动电源或风机电源拉,建议由主控板的主电源经磁珠分出来,再在模块引脚旁边放 10μF 电解电容和 100nF 陶瓷电容。原因是风机启停瞬间电流冲击大,纹波会顺着电源线窜进 ADC,让温度读数跳变。
I2C 上拉电阻要看模块是否自带。如果模块没有上拉,就在主控端加 4.7kΩ 到 3.3V。注意 I2C 总线上所有设备的上拉电阻是并联关系,挂的设备多了,等效阻值会被拉低,信号边沿变快,反射也会变强。这个时候反而是降速或者把主控端上拉调到 10kΩ 更稳。
ADDR 地址引脚千万不要悬空,明确拉高或拉低。多路从设备时给每个通道分配独立地址,不能靠运气。远程接口从端子进来前,加上 TVS 管保护,防止静电和浪涌把模块或者主控打坏。如果控制器带着可控硅、继电器这类强电负载,最好在 PJ85718DM 和主控之间做数字隔离,隔离 I2C 或者隔离 UART 都能选,至少让两边的地彻底分开。
2.4 布线布局的几个现场要点
这些是踩出来的规矩。第一,传感器走线远离继电器、变压器和可控硅区域,特别是本地 NTC 的接线,哪怕短也要绕开。第二,远程探头线用屏蔽双绞线,屏蔽层只在主控侧单端接地,不要两端都接,否则会在屏蔽层里形成地环路电流,反而引入干扰。第三,模块的模拟地和主控的逻辑地在主控板某一点单点相连,不要到处都是地连接,避免形成地环路。第四,如果远程距离超过 30 米,线缆进柜的地方还要做防雷接地保护,用专用的浪涌保护器件接在端子排上。
3. 固件实现:从底层读取到上层上报
3.1 底层初始化:时钟和 I2C 外设
PIC32MZ2048EFH144 的固件我用 C 语言,基于 MIPS M 核,直接操作外设寄存器或者使用 Harmony 生成的驱动都行。项目里用 Harmony 做了外设配置,重点配置三块:I2C1 用于读 PJ85718DM,ADC1 用于读本地 NTC,UART 用于调试输出。
I2C 初始化一般是这样:
// 使用 Harmony 生成的 I2C1 初始化 #define I2C1_SLAVE_ADDR 0x50 void i2c1_init(void) { // 引脚映射:SDA1、SCL1 复用为 I2C 功能 // 时钟 400kHz,由外设总线时钟分频得到 I2C1_CLOCK_FREQ = 400000; I2C1BRG = 0x135; // 实际值由 Harmony 根据时钟计算 I2C1CONbits.ON = 1; // 使能总线空闲检测和时钟拉伸 I2C1CONbits.SMEN = 1; }调试初期,I2C 速率没必要直接上 400kHz。先跑 100kHz,等验证了线缆电容和上拉匹配,再提到 400kHz。远程转接板比较长的时候,400kHz 的上升沿很容易变差,先把时序跑到稳定比什么都重要。
3.2 采样周期与滑动平均滤波
温度探头本身的响应很慢,所以固件里不适合“读一次就上报一次”。我设计了一套比较稳妥的采样节奏:
- PJ85718DM 每 300ms 读一次远程通道;
- 本地 NTC 每 100ms 采一次 ADC;
- 每 10 次数据做滑动平均,1 秒更新一次对外上报值。
滑动平均代码很简单:
#define AVG_N 16 static int32_t sum; static int16_t buf[AVG_N]; static uint8_t idx; int16_t sliding_average(int16_t sample) { sum -= buf[idx]; sum += sample; buf[idx] = sample; idx = (idx + 1) % AVG_N; return (int16_t)(sum / AVG_N); }这里要注意,平均窗口越大,滞后越明显。窗口 16 代表大约 1.6 秒的滞后,在 HVAC 控制里完全能接受,但对快速柜内保护就不太合适。柜内保护我用的是另一路“快速通道”,直接拿原始采样值做判断,不经过滑动平均。
除了滑动平均,还做了异常跳变抑制。如果某一拍的值和上一拍滤波值相差超过 5℃,并且下一拍又跳回来了,我直接丢掉这一拍。这种突刺绝大多数来自电源干扰和继电器开合瞬间。真实温度变化不会一秒钟跳 5℃,物理规律决定的。
3.3 温度换算与查表策略
如果温度值已经由 PJ85718DM 以数字量输出,固件可以直接使用。如果用的是本地 NTC 经 ADC 采集,那还需要换算。
推荐的查表流程是:预先在固件里生成一张 -40℃~110℃、每 0.1℃ 一个点的电压表或阻值表,然后用 ADC 值二分查找,再用线性插值得出小数部分。这个做法比直接跑自然对数快得多,也避免了浮点运算的不一致性。
简单示意:
float adc_to_temp(uint16_t adc_val) { // adc_val 为 12 位 ADC 结果 // table[] 存储每个温度对应 ADC 值 int lo = 0, hi = TABLE_SIZE - 1; while (hi - lo > 1) { int mid = (lo + hi) / 2; if (table[mid] > adc_val) hi = mid; else lo = mid; } float t0 = -40.0f + lo * 0.1f; float frac = (float)(adc_val - table[lo]) / (float)(table[hi] - table[lo]); return t0 + frac * 0.1f; }这里的表项间隔是 0.1℃,用线性插值已经足够。因为 NTC 本身的 B 值误差就比插值误差大得多,没必要追求更精细的算法。
3.4 对外上报:Modbus RTU 与调试打印
HVAC 现场最常用的控制总线是 RS-485 Modbus RTU。我在 PIC32MZ2048EFH144 上移植了一个轻量级 Modbus 从站协议栈,寄存器映射很直观:
| 寄存器地址 | 内容 | 读写 | | 0x0000 | 柜内本地温度,单位 0.1℃ | 只读 | | 0x0001 | 送风温度 | 只读 | | 0x0002 | 回风温度 | 只读 | | 0x0003 | 盘管进水温度 | 只读 | | 0x0004 | 盘管出水温度 | 只读 | | 0x0005 | 室外温度 | 只读 | | 0x0020 | 各通道报警状态位 | 只读 | | 0x0100 | 报警上限设置 | 读写 | | 0x0101 | 报警下限设置 | 读写 |
调试串口是另一个重要窗口。固件里我打印了所有通道的原始值、滤波值和最终上报值,方便在现场对着真值比对。很多问题从工程值上看不出来,但原始 ADC 值一看就能发现:比如某一路原始值周期性跳变,多半是电源纹波;某一路整体偏移,多半是接触电阻或校准问题。
4. 校准、误差拆解与现场验证
4.1 误差来源有四个地方
很多人以为买了高精度传感器就万事大吉,实际不然。整套链路的误差来自四处:第一,ADC 本身的增益误差和偏移误差,即使 12 位 ADC,在最不利的情况下也可能有 ±3~5 LSB 的误差;第二,参考电压或 VDD 的漂移,VDD 从 3.3V 漂到 3.25V,会直接变成温度读数偏移;第三,分压电阻的精度和温漂,前面说的 0.1% 电阻就是为了压住这块;第四,NTC 自身的 B 值离散度,即使是同一批次,也可能有 ±1% 的阻值偏差,换算成温度大约 0.3℃。
PJ85718DM 这类数字前端模块会把大部分模拟误差在出厂时就校准掉,所以现场最需要盯的是探头本身的偏差和接线带来的误差。话虽如此,我也不会完全相信出厂标定,首次上电后先全部抽检一遍,已经成为项目固定流程。
4.2 两点校准和三点校准怎么做
如果精度要求是 ±0.5℃,两点校准通常够用。校准点在 0℃ 和 70℃:0℃ 可以自己用冰水混合物实现,在保温杯里装满碎冰,加少量水,把探头插入搅拌,稳定后就是接近 0℃ 的标准温场;70℃ 有条件就上干体炉,没条件就用水浴锅加标准温度计。
读回实际测量值后,假设测量值 M 和真实值 T 是线性关系 M = k×T + b,用两个校准点算出 k 和 b,在固件里做反向补偿。三点校准则再加一个 25℃ 点,用来修正非线性。对 HVAC 应用来说,两点校准已经能覆盖绝大多数需求。
校准值的存储要注意:不要把补偿参数放在代码常量里,每台设备出厂时参数不同,需要烧录或者写到外部 EEPROM。PIC32MZ2048EFH144 内部有足够的 Flash 模拟 EEPROM 区域,但频繁擦写会影响寿命,我在项目里是外挂了一颗小容量的 I2C EEPROM,专门存校准值和设备序列号。
4.3 现场实测出来的数据
测试现场是某模拟风道实验台,送风由加热器从 20℃ 升温到 45℃,远程温度探头布置在距离主控约 18 米的风道内,用的是三线制 PT1000。方法和顺序如下:
探头和标准数字温度计并排插入同一个风道测温孔,等待温度稳定后再记录。每组数据读 10 次取平均,每 5 分钟记录一个点。升温过程从 20℃ 开始,每隔 5℃ 记录到 45℃,一共六个点。整套比较下来,最大正偏差 +0.35℃,最大负偏差 -0.2℃,全部落在 ±0.5℃ 的控制要求内。期间还用可控硅驱动加热器做了一次启停冲击测试,观察柜内本地温度在启停瞬间的波动,测试前加装的磁珠和旁路电容起到了明显作用,波动幅度从原来的 1.2℃ 降到了 0.3℃ 以内。
4.4 项目验收时参考的指标
| 性能项 | 指标 | | 测温范围 | -40℃ ~ 110℃ | | 本地温度精度 | ±0.3℃ | | 远程温度精度 | ±0.5℃ | | 刷新周期 | 1 秒 | | 报警响应 | 1 秒内 | | 浪涌保护 | 差模 ±2kV |
这些指标对 HVAC 控制来说足够实用。如果要医疗或实验室环境那种 ±0.1℃ 的精度,那就要换更高精度的探头、做四线制、再做温度梯度校准,成本和复杂度都完全不同。
5. 常见问题速查与避坑清单
5.1 现象与原因对照表
| 现象 | 可能原因 | 处理思路 | | 温度整体偏高 | 上拉电阻偏小、NTC B 值偏小 | 做二点校准补偿 | | 温度整体偏低 | 长线线阻未补偿、接触电阻偏大 | 改三线制或软件补偿固定线阻 | | 上电显示 -40℃ 或 110℃ 跳变 | 传感器短路或开路、端子接触不良 | 检查接线和端子压接 | | 空调风机一启动读数就跳 | 电源纹波、强电干扰耦合 | 磁珠隔离、屏蔽层单端接地 | | I2C 偶发通信失败 | 上拉电阻并联值过小、总线过长 | 调整上拉、降速、加滤波电容 | | 远程三根线顺序接错 | 检测线接到激励端、线阻补偿失效 | 用万用表先量出线缆芯线编号 |
5.2 一个现场 I2C 总线故障的排查实录
有段时间,现场的控制器总是偶发性地读不到第三个模块温度。仪表显示下午出现得频繁,上午几乎不出现。这个现象本身就很有指向性:温度升高,导体电阻变大,I2C 信号边沿恶化,问题就暴露出来了。
排查过程是这样的:先排除地址冲突,把第三个模块单独接到主板上,读数是正常的。再把它通过原来那根 2 米排线接上,示波器量 SDA 波形,发现上升沿明显变缓,已经接近门限电平附近。进一步检查发现,第三个模块板载已经接了上拉电阻,主板上又并了一组上拉,并联后等效阻值太小,信号摆幅不够。解决方案很粗暴:主板侧拆掉这组上拉,SDA 和 SCL 线上各串一只 33Ω 电阻抑制过冲,I2C 速率降到 100kHz。之后连续跑了三周,没有再出现通信失败。
这个案例提醒我,I2C 不是“接上就能跑”,总线电平受上拉、线缆电容、从设备数量三个因素共同影响。遇到间歇性故障,先怀疑边沿,再去拆软件。
5.3 六条现场私藏经验
远程探头接线端子一定要选按压式,不要选螺丝刀拧的传统端子。传统端子拧不好,接触电阻会慢慢变大,温度读数会一天比一天偏高,很难查。
PJ85718DM 的供电从主 3.3V 经磁珠单独引出,不要在模块旁边并联继电器驱动线。电源回路窜扰是最隐蔽的温度漂移源。
即使某路温度不参与控制,也建议存 24 小时历史。现场调试经常遇到“刚才那个点升温曲线怎么样”的问题,有历史记录就能直接还原故障过程。
固件里温度转换能用查表加插值就不要直接跑对数。对数库函数在嵌入式环境里既不快也不稳,查表误差完全可控,调试也直观。
报警逻辑一定要加滞回带。比如高温报警触发值 60℃,恢复值设在 59.5℃,如果恢复值等于触发值,现场会看到报警继电器咔哒咔哒来回跳,半天就把触点烧了。
第一版现场测试不要信任出厂标定,全部探头做多点抽检。标称精度是一回事,实际到货批次的一致性又是另一回事。抽检一次的成本,远低于后面到项目现场返工的成本。
这套系统从原型到现在,前前后后改了三版。第一版输在远程模拟链路,第二版输在电源干扰,第三版才算把本地和远程分开做好了。现在回过头看,最值得沉淀的其实不是某一颗芯片多厉害,而是“把模拟测量问题挡在前端、把通信问题限定在协议层、把现场问题留给校准机制”这个整体设计思路。PJ85718DM 负责把物理量变成可信的数字量,PIC32MZ2048EFH144 负责把这堆数字量变成系统能用的控制信息,两者配合,温度监测这件事才能真正在 HVAC 现场站稳脚跟。