1. 项目缘起与整体设计思路
嵌入式温度监测这件事,说起来简单,做起来坑多。我最早接触这类需求是在一个HVAC控制板的项目上,当时的需求很朴素:板子上要同时盯着本地几个发热点的温度,还要通过总线把远端几个温区的数据收回来,统一做风机调速和告警判断。选型的时候翻了不少器件手册,最后定下来用PJ85718DM做本地多路温度采集,配合MKV42F256VLH16这颗MCU做数据处理和远程温度数据的汇聚转发。这套组合不是拍脑袋定的,背后有几层考量。
先说PJ85718DM。它是一颗多通道远程温度传感器,核心能力是支持多路远端二极管连接的测温通道,同时自身也集成了一路本地温度检测。远端测温的原理是利用二极管的基极-发射极电压与温度之间的线性关系,通过交替注入不同电流、测量电压差来反推温度。这种方式的好处是测温点可以物理上远离主控板,把二极管或者三极管接在需要监测的位置,比如换热器表面、风道出口、功率器件散热片上,用双绞线拉回来就行。HVAC场景里测温点分散是常态,这个特性非常关键。
再说MKV42F256VLH16。这颗MCU属于主流工业级ARM Cortex-M系列,主频够用,外设资源丰富,尤其是I2C、SPI、UART这些通信接口给得很足,Flash和RAM容量对于跑温度采集、滤波算法、简单的PID风机控制来说绰绰有余。它在这套方案里的角色是“中枢”:通过I2C总线周期性读取PJ85718DM的本地和远端温度寄存器,做数据校验和滤波,然后根据预设阈值驱动PWM输出控制风机,同时把温度数据打包通过UART或者CAN发给上位机。
整体设计思路可以概括成三层:感知层由PJ85718DM和外围的测温二极管组成,负责把物理温度转换成数字量;处理层由MKV42F256VLH16承担,负责轮询采集、数据滤波、逻辑判断;执行与通信层包括PWM风机驱动、告警输出、以及向上位机上报数据的通信接口。这三层各司其职,耦合度低,后期要扩展测温通道或者改通信协议都比较方便。
为什么不用MCU自带的ADC加热敏电阻的方案?热敏电阻便宜,但精度和一致性差,尤其是多点测温时每个点都要单独校准,量产标定工作量巨大。PJ85718DM这类专用远程温度传感器出厂一致性做得好,远端二极管测温的误差可以控制在正负1度以内,而且支持串联电阻补偿,长线缆带来的误差也能修掉。对于HVAC这种对温度精度有一定要求、又要求长期稳定运行的场景,专用传感器方案的综合成本其实更低。
注意:远端测温二极管的选择有讲究。普通开关二极管的一致性不够好,建议用专门为测温设计的二极管连接三极管,比如把集电极和基极短接当二极管用,这种器件的温度系数更稳定。
2. 核心器件细节与硬件设计要点
2.1 PJ85718DM的通道配置与寄存器操作
PJ85718DM的寄存器映射是理解这颗芯片的钥匙。它内部有一组配置寄存器、一组温度数据寄存器、还有状态寄存器和限值寄存器。上电后第一件事是配置寄存器,设定远端通道的使能、转换速率、以及是否开启串联电阻补偿。转换速率这个参数直接决定了功耗和响应速度的平衡,HVAC场景里温度变化慢,一般设成1Hz到4Hz就够了,没必要跑太高。
温度数据寄存器是16位格式,高字节是整数部分,低字节是小数部分,分辨率可以到0.0625度。读的时候要连续读两个字节,先读高字节再读低字节,中间不能被打断,否则数据会错位。我踩过一次坑:用中断方式读I2C,结果在读完高字节后被一个高优先级中断打断,等回来读低字节时芯片已经更新了数据,导致整数和小数部分来自不同采样周期,温度值跳变。后来改成用DMA一次性搬两个字节,问题解决。
远端通道的串联电阻补偿也值得说。长线缆本身有电阻,测温二极管上流过的电流会在线阻上产生压降,导致测到的电压偏高,算出来的温度就偏低。PJ85718DM内部有补偿机制,可以通过配置寄存器设定补偿电阻的值,芯片在计算时会自动扣除这部分影响。补偿电阻的估算方法是:量出线缆单程电阻,乘以2得到回路电阻,再根据芯片手册给出的补偿步进值换算成寄存器配置值。实测下来,用一米左右的普通双绞线,补偿电阻设成几十毫欧量级就能把误差压到0.5度以内。
2.2 MKV42F256VLH16的外设分配与时钟规划
MKV42F256VLH16在这套方案里要同时处理好几件事:I2C读传感器、PWM驱动风机、UART上报数据、可能还有定时器做周期调度。外设分配要提前规划好,避免引脚冲突。我的分配方案是:I2C1接PJ85718DM,速率设成100kHz标准模式,线缆不长的话400kHz快速模式也能跑;PWM用TIM1的通道1和通道2,分别驱动两路风机,频率设成25kHz,这个频率高于人耳听觉范围,风机不会发出啸叫;UART2用于调试和上报,波特率115200;TIM2做1ms系统滴答,用于任务调度和超时计数。
时钟规划容易被忽视。MKV42F256VLH16的主频可以跑到比较高的水平,但外设时钟是分频出来的。I2C时钟如果分频不当,实际速率会偏离设定值,导致通信不稳定。我的做法是先确定系统时钟,然后反推各外设的分频系数,确保I2C实际速率在90kHz到110kHz之间,PWM频率精确落在25kHz附近。这些参数在初始化代码里都要算清楚,不能随手填。
提示:I2C总线上一定要接上拉电阻,阻值根据总线电容和速率来选。100kHz速率下,4.7k欧姆是常用值;如果总线电容较大或者速率提到400kHz,要减小到2.2k欧姆甚至更小。上拉电阻太大,上升沿变缓,通信会出错。
2.3 测温二极管的布局与走线
远端测温的精度很大程度上取决于二极管的布局和走线。二极管要尽量贴近被测点,但又要避免直接贴在高压或者大电流走线旁边,否则会引入噪声。我的经验是:如果测的是散热片温度,二极管涂上导热硅脂后用小夹子固定在散热片表面,引线用双绞线,绞距尽量密一些,这样能有效抑制共模干扰。
走线方面,测温二极管的引线不要和PWM驱动线、继电器控制线捆在一起走。PWM线的dv/dt很高,耦合到测温线上会直接反映成温度读数跳动。如果实在避不开,就用屏蔽双绞线,屏蔽层单端接地。另外,测温线的长度也要控制,太长的话线阻和分布电容都会影响测量,一般建议不超过两米。超过两米的话,除了开启串联电阻补偿,还要考虑降低I2C速率或者改用差分传输方案。
3. 固件实现与核心环节拆解
3.1 初始化流程与寄存器配置代码
固件的第一步是把MKV42F256VLH16的外设初始化好,然后配置PJ85718DM。初始化顺序有讲究:先配时钟,再配GPIO,然后配I2C,最后配PWM和UART。PJ85718DM的配置要在I2C初始化完成之后进行,配置内容包括:使能本地通道和远端通道、设定转换速率、设定串联电阻补偿值、设定温度限值寄存器。
下面是一段配置PJ85718DM的伪代码,展示了关键寄存器的写法:
// 假设I2C写函数为 i2c_write(dev_addr, reg_addr, data) // PJ85718DM的I2C从机地址根据引脚配置确定 // 配置寄存器:使能远端通道1和2,本地通道使能,转换速率4Hz i2c_write(PJ85718_ADDR, CONFIG_REG, 0x0F); // 串联电阻补偿寄存器:设定补偿值 i2c_write(PJ85718_ADDR, SERIES_R_COMP, 0x08); // 远端通道1高温限值:设定为85度 i2c_write(PJ85718_ADDR, REMOTE1_HIGH_LIMIT, 0x55); // 远端通道1低温限值:设定为-20度 i2c_write(PJ85718_ADDR, REMOTE1_LOW_LIMIT, 0xEC);配置完成后,芯片就开始周期性转换,温度数据会更新到对应的数据寄存器里。MCU只需要按需读取即可,不需要发命令触发转换。
3.2 温度数据读取与滤波算法
读取温度数据看起来简单,但要做好滤波才能得到稳定的读数。我的做法是:每100ms读一次所有通道的温度,连续读8次,去掉最大值和最小值,剩下的取平均。这样既能滤掉偶发的尖峰干扰,又不会引入太大延迟。8次采样在100ms周期内完成,相当于每个通道的更新率是10Hz,对于HVAC场景完全够用。
滤波算法用滑动窗口实现,窗口大小设成8。每次新数据进来,替换掉最旧的数据,然后重新计算平均值。为了省CPU,可以维护一个累加和,每次替换时减去旧值加上新值,再除以窗口大小。这样每次滤波只需要一次减法和一次加法,效率很高。
温度数据的格式转换也要注意。PJ85718DM返回的是16位有符号数,高字节是整数部分,低字节的高四位是小数部分。转换公式是:温度 = (高字节 * 256 + 低字节) / 256.0。如果高字节的最高位是1,表示负温度,要先做符号扩展再计算。我见过有人直接拿高字节当整数用,结果负温度全错,这种低级错误在调试时很浪费时间。
3.3 风机控制逻辑与PWM输出
风机控制是这套方案的核心执行环节。逻辑不复杂:根据温度读数查表或者做线性插值,算出目标PWM占空比,然后更新PWM寄存器。但有几个细节要注意。第一,PWM占空比的变化要平滑,不能突变,否则风机会发出明显的转速变化声,体验很差。我的做法是每次更新时限制变化量,比如每次最多变化2%,这样从0到100%需要50个控制周期,按100ms周期算就是5秒,过渡很自然。
第二,要设置启动占空比。风机在低占空比下可能转不起来,尤其是含油轴承的风机,启动扭矩不够。我的经验是启动占空比设成30%左右,等转起来之后再降到目标值。这个逻辑可以用状态机实现:检测到需要启动时,先输出30%占空比持续500ms,然后切换到正常控制。
第三,要有堵转检测。如果PWM输出正常但温度持续上升,可能是风机堵转或者损坏。可以通过检测风机反馈信号(如果有)或者观察温度变化趋势来判断。没有反馈信号的话,可以设定一个超时:如果PWM占空比已经加到80%以上,但温度在30秒内没有下降,就触发告警。
3.4 远程温度数据的汇聚与上报
远程温度数据通过UART或者CAN上报给上位机。协议设计要简单可靠,我一般用自定义的二进制帧格式:帧头两个字节,然后是通道号、温度值、状态标志,最后是校验和。帧头用0xAA和0x55,不容易和数据混淆。校验和用累加和取反,计算简单,检错能力够用。
上报周期根据上位机需求定,一般1秒一次就够了。如果温度变化快,可以缩短到200ms。上报的时候要把本地温度和所有远端温度都带上,上位机拿到完整的数据才能做全局判断。如果某个通道读取失败,要在状态标志里标明,上位机看到标志就知道这个通道的数据不可信。
注意:UART通信要做好错误处理。如果连续多次收到校验错误的帧,或者发送缓冲区满,要有相应的处理逻辑,不能死等。我的做法是发送用DMA加环形缓冲区,发送完成中断里更新缓冲区指针,主循环里检查是否有数据要发,这样不会阻塞主循环。
4. 常见问题与排查技巧实录
4.1 温度读数跳变或明显偏差
温度读数跳变是最常见的问题。原因可能有几种:I2C通信受干扰导致数据错位、测温二极管引线耦合了噪声、电源纹波太大影响传感器内部基准。排查的时候先看跳变的规律:如果是随机跳变,多半是通信干扰;如果是周期性跳变,可能是PWM或者继电器动作时耦合进来的。
我的排查步骤是:先用示波器看I2C的SCL和SDA波形,确认上升沿是否干净、有没有过冲或者振铃。如果波形不好,先调上拉电阻和线缆。然后看测温二极管引线附近有没有大电流走线,有的话挪开或者加屏蔽。最后检查电源,用示波器看传感器供电引脚上的纹波,如果超过50mV,要加LC滤波或者换LDO。
还有一种情况是读数整体偏高或偏低,但很稳定。这通常是串联电阻补偿没设对,或者测温二极管的温度系数和芯片预期的不一致。解决办法是拿一个标准温度计做参考,在已知温度下校准。校准的时候至少取两个点,比如0度和50度,算出偏移量和增益误差,然后在固件里做补偿。
4.2 I2C通信失败或时断时续
I2C通信失败的原因很多,我整理了一个排查表:
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 完全无应答 | 从机地址错、供电缺失、引脚接反 | 用示波器看SDA是否有拉低 | 核对地址、量供电、检查引脚 |
| 偶尔NACK | 总线电容大、上拉电阻不合适 | 看波形上升沿时间 | 减小上拉电阻、缩短线缆 |
| 数据错位 | 中断打断读时序、时钟拉伸未处理 | 检查读函数是否原子操作 | 改用DMA、处理时钟拉伸 |
| 总线锁死 | 从机异常拉低SDA | 量SDA电平 | 加总线恢复逻辑、复位从机 |
总线锁死是个棘手问题。如果从机在传输过程中异常复位,可能会一直拉低SDA,导致总线无法恢复。解决办法是在I2C初始化时加一段恢复逻辑:把SCL配置成GPIO,手动发送9个时钟脉冲,然后发送停止条件,这样能强制从机释放总线。这个逻辑在量产固件里一定要有,否则现场偶发的总线锁死会导致整个系统失效。
4.3 远端测温通道读数异常
远端通道读数异常通常和二极管本身或者连线有关。如果某个通道读数一直是满量程或者零,先检查二极管是否接反或者损坏。用万用表二极管档量一下,正向压降应该在0.5V到0.7V之间。如果读数偏高很多,可能是二极管开路,芯片检测到开路后会输出一个特定值(通常是最高温度值)作为标志。
如果读数偏低,可能是二极管两端并了电容或者受到了漏电流影响。测温二极管的反向漏电流会随温度升高而增大,如果漏电流太大,会影响测量精度。解决办法是选漏电流小的二极管,或者在二极管两端并联一个电阻,给漏电流提供通路。电阻值一般选1M欧姆左右,太小会影响测量,太大起不到作用。
还有一个容易被忽视的问题:多个远端通道共用一根地线时,地线上的压降会串扰到测温回路。如果每个通道的电流不同,地线压降就不同,导致通道间相互影响。解决办法是每个测温通道用独立的回线,或者在软件里做通道间补偿。硬件上做星型接地是最稳妥的。
4.4 风机控制异常与PWM输出问题
风机不转或者转速异常,先确认PWM输出是否正常。用示波器量PWM引脚,看频率和占空比是否符合预期。如果PWM正常但风机不转,可能是驱动电路问题,比如MOSFET没导通、续流二极管接反、或者风机本身损坏。如果PWM频率不对,检查定时器配置,特别是预分频和自动重载值有没有算错。
风机转速波动大,通常是控制周期太短或者滤波不够。温度采样本身有噪声,如果直接用单次采样值算PWM,占空比就会跟着噪声跳。解决办法是加长滤波窗口,或者对PWM变化做斜率限制。我一般用滑动平均加变化率限制,效果很好。
还有一种情况是风机在低速时发出异响。这通常是PWM频率落在了人耳敏感区间,或者占空比太低导致风机电流不连续。把PWM频率提到20kHz以上,同时保证最低占空比不低于20%,基本能解决。
5. 实操心得与扩展思路
这套方案我在几个不同的HVAC控制板上都用过,积累了一些文档里不会写的经验。第一个心得是:PJ85718DM的本地通道其实很有用,可以用来监测MCU或者板子附近的温度,作为环境温度参考。如果本地温度和远端温度差异很大,说明板子本身发热严重,可能需要加散热或者调整布局。
第二个心得是:MKV42F256VLH16的Flash容量足够,可以把温度限值、风机曲线这些参数做成可配置的,存在Flash的特定区域,通过UART命令修改。这样现场调试的时候不用重新烧固件,改几个参数就行,效率高很多。参数存储要做好校验和备份,防止Flash损坏导致参数丢失。
第三个心得是:I2C总线上可以挂多个PJ85718DM,通过地址引脚区分。如果测温通道不够用,直接再加一颗就行,固件里把通道号映射改一下。但要注意总线电容,挂太多器件会导致上升沿变缓,这时候要么降低速率,要么加I2C缓冲器。
扩展方面,这套架构很容易加上湿度传感器或者压力传感器,I2C总线上再挂几个从机就行。如果要做联网控制,可以在UART上接一个通信模块,把数据转发到云端。MKV42F256VLH16的运算能力还有富余,跑一个简单的Modbus协议栈完全没问题。后续如果要加PID闭环控制风机,现在的PWM和温度采集框架直接就能用,只需要把控制逻辑从查表改成PID计算就行。
最后分享一个调试小技巧:在固件里加一个温度数据日志功能,把每次采样的原始值和滤波后的值都存到RAM缓冲区里,通过UART导出来用Excel画曲线。这样能直观地看到滤波效果和干扰情况,比盯着串口打印的数字有用得多。我靠这个办法发现过好几次偶发的干扰问题,都是靠肉眼看数字发现不了的。