1. 项目概述与整体设计思路
我这次做的是一个嵌入式和HVAC(暖通空调)场景下的温度监测系统,核心控制器采用PJ85718DM与STM32F415ZG双芯片方案。PJ85718DM作为模拟前端和采集控制部分的辅助芯片,STM32F415ZG则是系统的主控大脑,负责通信调度、数据处理、显示和人机交互。系统实现了本地双路温度采集、远程温度链路接入、RS485总线组网、TFT显示屏实时监控、参数持久化与固件升级等一系列功能。
项目最初的定位很明确:不是做一个实验室里的评估板,而是要能直接放进HVAC控制柜里长期运行的产品级硬件。所以从器件选型开始,所有决定都以“稳定可靠、可批量复制”为优先。整个系统需要解决的痛点有三个:一是本地环境温度监测的精度和响应速度,二是远程温度节点的数据接入方式,三是多节点组网后的数据统一汇聚与显示交互。HVAC场景里,温度数据的实时性和准确性直接影响风机、水阀、压缩机的控制逻辑,一个传感器读数漂移1℃,整套控制策略就会跟着跑偏,甚至造成能耗浪费和设备损耗。
这套系统的数据链路设计成两个层面:本地温度数据通过I2C总线直接读取传感器,远程温度数据由RJ45端口接入的独立采集模块提供。远程采集模块使用差分串行信号通过四芯双绞线传输,具备更强的抗干扰能力和更远的传输距离。整机再通过RS485总线接入上层控制系统,使用Modbus RTU协议与后台或HMI交互,标准协议的好处是后续对接平台完全不用专门定制。
适合谁来参考这篇内容?如果你正在做一个以MCU为核心的监测类产品,无论是温度、湿度还是其他模拟量采集,都可以参考这套“硬件物理层+驱动层+协议层+UI层”的分层设计方法。如果你只是初学者,也可以把它当作一个完整的实战案例,看看一个产品级项目在选型和工程细节上是怎么思考和取舍的。
项目硬件拓扑和整体方案选型,我花了很长时间斟酌。最开始考虑过用单芯片方案,把所有功能一股脑塞进一颗MCU里,但评估后放弃了。原因在于:PJ85718DM作为模拟采集辅助芯片,能够处理一些高精度模拟信号的调理和采集任务,分担主控的压力;STM32F415ZG则把主要资源用在通信、UI和数据处理上。这样分工以后,两个芯片各自的任务边界很清晰,调试时定位问题也更快。板载资源方面,选这颗主控的原因也简单:内置1MB Flash、192KB SRAM,以及丰富的I2C、USART、SPI、ADC和定时器资源,完全覆盖项目需求,性价比和使用便利性都合适。
2. 硬件平台与模块划分详解
2.1 双芯片架构与引脚分配策略
项目采用PJ85718DM与STM32F415ZG协同工作的方式,两颗芯片之间通过SPI接口通信。PJ85718DM负责模拟信号链的初步调理和采集,包括NTC分压信号的缓冲放大,然后将处理后的数据通过SPI传输给主控MCU,主控再结合SHT30数字传感器数据做融合处理。这样的好处是模拟信号的处理尽量靠近源头,避免长距离走线带来的噪声干扰。
引脚分配上我遵循了一个原则:数字通信引脚和模拟采样引脚严格分区布局。PA2/PA3映射到USART2,外接调试串口的电平转换电路;PB6/PB7配置为I2C1,用于连接板上的SHT30传感器和FRAM存储芯片;SPI2挂在PB13/PB14/PB15上,连接TFT显示屏。模拟部分,ADC1的通道单独安排在一个远离数字开关信号的位置,避免开关噪声耦合进模拟采样链路。电源引脚全部加上0.1µF和10µF两级退耦电容,并尽量靠近芯片引脚放置。PCB布线时,模拟地与数字地单点相连,电源走线加宽到1mm以上,降低压降。
有几处细节值得单独说。NSS引脚不用时,我强制上拉到3.3V,防止SPI外设意外进入从机模式。板载调试器和外接调试器同时存在时,需要关闭板载调试器的使能跳线,否则两路调试器会争抢状态机,出现偶发调试异常。另外,备份的I2C通道引脚和LED引脚距离太近,布线时会产生耦合干扰,最终果断放弃了备份I2C方案,保留一路经过验证的主通道。
2.2 时钟链路与电源完整性设计
时钟是整个系统正常工作的基础,这个环节我花了不少心思。外部25MHz无源晶振配两个18pF负载电容,经过PLL配置,M=25分频得到1MHz基准,N=192倍频到192MHz,再经P=4分频,输出48MHz作为系统主频。APB1分频器设为4,APB2设为2,APB1定时器时钟仍然为48MHz,APB2定时器时钟为96MHz,这样TIM2/TIM3的计数时钟都能满足微秒级到毫秒级的硬件延时需求。
负载电容取值不是拍脑袋定的。晶振数据手册给出的典型CL负载为12.5pF,根据 CL = (C1 * C2) / (C1 + C2) + Cstray 反推,电路寄生电容约4pF时,两个18pF电容串联等效9pF,加上4pF寄生,正好接近12.5pF的标称CL。这套容差方案实测非常稳,串口波特率误差小于0.3%,长时间压力测试没有出现乱码。
ADC外设的时钟单独用PLL的Q输出配置到12MHz,落在该系列ADC推荐的采样时钟区间。这个细节很关键,因为ADC转换结果的质量和采样时钟稳定性直接相关,如果把ADC时钟挂在不稳定的高频分频链路上,转换结果低位会持续跳动。低频外设方面:I2C1时钟来自APB1的48MHz,通过CCR寄存器分频得到100kHz;USART2同样挂在APB1上,用BRR寄存器计算初值,48MHz/115200=416.67,取整416,实际波特率误差约0.15%。SPI2挂在APB1上,48MHz分频为12MHz,满足TFT屏幕的读取时序要求。
启动时我还用了一个技巧:把PLL的P、Q、R三个输出都配置为有效,即使当前只用P输出,Q和R也同时稳定输出,避免后期扩展外设时重新初始化PLL导致系统短暂重启。这个坑我第一次做项目时踩过,当时只配置了P输出,后面想加功能时一改PLL配置,整个系统就重启一次。这次直接把冗余配置做好,后面扩展功能省了很多事。
电源完整性方面,板卡供电支持外部12V DC和USB 5V两种方式,两路之间通过二极管隔离,LDO和开关稳压器共同输出3.3V和5V。USB供电在调试时方便,12V供电在工业现场使用方便,两路电源自动切换,不会产生电压倒灌。复位电路由专用复位芯片提供,上电复位和手动复位都走这颗芯片,低电压时也能可靠复位,避免MCU在电压不稳时执行不可控操作。
2.3 按键去抖、指示灯与状态机设计
板上一共四个按钮,三个是控制按钮,一个是复位按钮。我没有用简单的轮询方式,而是把按键扫描动作放在2ms定时器中断里做状态机处理:空闲态、按下态、确认态、长按态。每次扫描都做软件去抖,连续两次采样电平一致才认为状态变化,实测抖动误触发率基本为零。每个按键在确认态才触发一次业务回调,按住不松手时,长按达到2秒进入长按模式,循环切换显示通道和调节报警阈值上限。长按和短按的互斥处理也靠状态机解决,确保同一个按下事件不会同时触发短按和长按动作。
指示灯用了一颗双色RGB LED加一颗独立的绿色电源指示灯。系统上电后,PWR绿色LED直接点亮,表示供电正常。RGB LED在正常工作下显示蓝色呼吸效果,温度越限时改为红色快闪。呼吸效果用TIM3的PWM输出,占空比以正弦方式缓慢调制,避免用delay函数阻塞主循环。报警时红色快闪由另一个OC通道输出,周期200ms,不中断主流程,保证报警的同时还能响应按键和通信。这种分离设计让代码里不会出现“卡死在某个延时里导致指示灯无响应”的情况。
整机状态机分为四类:初始化、运行、报警、配置。初始化状态完成时钟、外设和传感器自检,自检失败会记录具体错误码到后备寄存器,并闪烁红色LED指示故障类别。运行状态下,每秒钟执行一次完整采集、计算、显示更新和上报。报警状态下,本地显示红色警示,同时把报警类型写入FRAM的报警日志,方便远程主机通过Modbus读取历史报警记录。配置状态下,允许通过TFT菜单或上位机工具调整报警阈值、校准偏移、使能开关等参数,参数存入FRAM对应地址区间。整套状态机用枚举定义,主循环只在运行与报警之间切换,配置状态只能在菜单操作时进入,避免状态叠加的复杂度。
3. 传感器选型与数据处理流程
3.1 环境温湿度传感器与热敏电阻方案对比
传感器选型我前后对比过好几款,最终本地测温定下来两路:一路使用SHT30数字温湿度传感器,另一路使用负温度系数热敏电阻(NTC)和一个10kΩ精密电阻构成的分压网络。SHT30走I2C接口,直接返回温度和湿度,分辨率高、长期稳定性好,适合做核心环境参数采集。NTC则作为辅助测温点,放在PCB边缘靠近外部热源的位置,用来感知局部温度变化,比如散热风道附近或电源模块的热点区域。
SHT30选型时看重的三个点:一是I2C地址可选,默认0x44,把ADDR引脚拉高后地址改为0x45,这样板子上可以挂两片SHT30而不冲突;二是它内部有加热器,可以应对高湿结露场景,但正常使用时必须关闭,否则会导致自升温;三是测量速率可配置,低能耗模式下典型的周期测量速率2Hz,足够用于室温场景,但温度瞬态变化大的场合需要切换到高周期模式。我的板子选用默认0x44地址,数据更新频率设为10Hz,在1秒的采集周期里做三次样本取平均,显著降低了读数毛刺。
NTC这边用的是100kΩ@25℃的型号,B值3950,配合10kΩ精密电阻串联成半桥结构,输出连接到ADC1的通道。负温度系数意味着温度升高时电阻下降,分压点电压也随之下降,MCU通过ADC反向换算回温度。这样做的好处是不需要额外模拟前端,直接利用MCU内置ADC就能实现0.1℃级别的分辨率。但要注意ADC参考电压的稳定性,如果直接用3.3V电源做参考,电源波动会直接造成读数误差。我的做法是用一个独立的3.0V基准源给ADC提供VREF+,这样即使主电源从3.3V波动到3.15V,ADC转换结果依然准确。
两个方案的适用场景差异也很明显:SHT30适合做环境整体的温湿度监测,数据稳定、免标定、带数字接口;NTC则适合做设备内部热点监测,成本低、响应快、可以根据不同位置灵活部署。一个系统中同时使用两种传感器,本质上是做“数据源冗余”,主传感器异常时辅助传感器能顶上,这在HVAC设备连续运行场景下非常必要。测温范围0~70℃时,NTC通过查表方式实现标定,表中相邻温度点间隔1℃,线性插值后分辨率优于0.1℃。需要注意,不同B值的热敏电阻,查表数据完全不同,必须从厂家手册拿原始R-T表,不能跨型号套用。实际测试中,这套NTC方案在25℃恒温环境中校准后,误差很小,长期漂移也可接受。
3.2 单总线传感器与ADC采样的协同处理
远程采集模块那边,除了差分串行通路外,还保留了一路单总线数字温度传感器,挂在某个GPIO上,与ADC采样通道协同配合。单总线传感器的优势是接线少、成本低、每个传感器都有唯一序列号,一条总线上挂多个传感器时,通过ROM命令精确寻址,不会互相干扰。主控MCU作为总线主机,每次采集前先发送复位脉冲,等待传感器应答脉冲,然后发送ROM命令选中目标传感器,再发送功能命令启动转换并读取温度结果。整个时序要求非常严格,特别是复位时序和应答时序的时间窗,必须用微秒级延时精确控制。
这里踩过一个很典型的坑:第一次写单总线驱动时,用了一个45µs的延时函数,看起来和手册要求差不多,结果一连读回0xFFFF。后来用逻辑分析仪逐个对比波形,发现是复位脉冲后的等待时间过长,把传感器应答窗口漏掉了。修正后,我把时序拆分成三个独立延时区间:复位低电平480µs、释放总线60µs、采样应答70µs,完全按手册最小值偏上执行,实测一次读取稳定。之后再遇到类似总线设备,我都会先拿逻辑分析仪确认时序,再改软件,避免盲目改延时。
ADC采样这边,配置为12位分辨率、采样时间60.5个周期,实际采样率在10kSPS级别。前端加了一阶RC低通滤波,截止频率约1.6kHz,用来抑制50Hz工频及其谐波的混叠。采集到的NTC分压值不直接查表,而是先做中值滤波:连续采集5次,排序后取中间值,再对中间值做滑动平均,这样既抑制了随机噪声,又保留了温度变化的趋势。整条链路处理下来,温度分辨率在0.01℃级别,实际稳定度在±0.05℃以内。这个数据在HVAC温控场景下很有价值,因为温差在1℃以内时的PID调节效果,直接取决于采样链路本身的噪声底。
3.3 I2C总线读写时序与从设备管理
我习惯把I2C总线上所有设备整理成一张表再开工,包括设备地址、寄存器长度、读写频率、错误恢复策略。总线上挂了SHT30(地址0x44)、FRAM(地址0x50)、以及后续可能扩展的IO扩展芯片(地址0x20)。I2C1主模式,标准速率100kHz,时钟来自APB1。MCU作为唯一主机,所有从设备都处于被动等待状态,不存在总线仲裁场景。写入时,先发设备地址加写位,再连续写寄存器地址和数据;读取时,先发设备地址加写位写入寄存器地址,然后重启总线,再发设备地址加读位,参数和结果都在事件标志位轮询中完成。
SHT30的单次触发测量命令0x2C06,在调用后等待10ms再读取数据,数据长度6字节,包含两字节温度、两字节湿度、两字节CRC。我从前也图省事跳过CRC校验,结果在某个EMC干扰强的现场出现偶发错误读数,之后所有传感器数据都强制做CRC校验,错误帧直接丢弃并重读。FRAM的读写和EEPROM差异很大:EEPROM写一页要等3~5ms,而FRAM写后立即生效,不需要等待。但FRAM也有个特点,就是读写次数虽然极高,接近无限寿命,但它的写入时序受时钟频率和总线负载影响较大。
FRAM挂在同一条I2C总线上时,上拉电阻选2.2kΩ。总线电容约300pF时,2.2kΩ上拉可以保证上升时间在1µs以内,符合标准模式时序。上拉能力不够时,信号边沿会变缓,导致时钟延伸被误判。总线调试时常见的一个问题:SHT30的ADDR引脚悬空时,地址为0x44,但如果PCB走线太长,地址引脚受到干扰,可能偶发被识别成0x45。我的做法是在地址引脚上做硬上拉到GND,并加一个小电容滤波,彻底杜绝地址漂移。
4. 数据可视化与系统联调
4.1 本地TFT显示与菜单交互实现
本地显示用2.8寸TFT,分辨率320x240,驱动IC为ILI9341,通过SPI2连接。TFT的背光控制用了MCU的一个PWM通道,亮度可以动态调节。显示内容分为两块:主界面和菜单界面。主界面以数字大字为主,实时显示温度值、湿度值、报警阈值、当前状态文字,另外用进度条显示温度相对报警阈值的占比,方便一眼看出余量。菜单界面则是列表式导航,支持上下选择、确认、返回,参数修改时可以用数字键盘或加减步进方式。
SPI2的时钟配置为12MHz,写全屏缓冲区时大约需要15ms,刷新率可以接受。但注意,ILI9341在RGB接口或SPI接口下的初始化序列不完全相同。我这款屏是SPI接口,初始化命令序列有一百多条,最初直接用开源库的RGB例程,结果屏幕花屏,后来换成与硬件匹配的SPI初始化序列才正常。不同批次的ILI9341,驱动命令可能略有差异,建议上电后读取ID,再根据返回值选择对应初始化分支。
菜单交互用状态机实现,独立的菜单状态和参数修改状态区分开。参数修改状态里,按键负责调节数值和切换编辑位,确认键保存参数到FRAM。为了防误触,进入参数修改模式前,要求长按确认键1.5秒。实际生产环境里,很多误操作都来自连续快速按键,这个1.5秒长按的门槛很有效。显示刷新频率方面,主界面每秒刷一次,菜单界面在按键动作时按需刷新,不做全屏重绘,降低SPI总线占用率,也避免画面闪烁。
4.2 RS485总线组网与远程数据链路
远程链路选择RS485总线,这是标准的差分总线,支持多点通信,传输距离可达1200米,非常适合HVAC这类分散式设备数据汇集。板卡作为其中一个节点,通过差分收发器接入RS485总线。总线拓扑为手拉手菊花链,末端节点必须加终端匹配电阻,电阻阻值等于电缆特性阻抗,一般在120Ω左右。我实际焊上去的是120Ω贴片电阻,实测总线空闲时差分电压稳定在2.0V以上,符合标准。
RS485驱动器的收发控制,用MCU的一个GPIO切换方向。方向切换时机非常关键:发送完成后不能立刻切回接收,必须等最后一个字节的停止位完全发送完毕,否则最后一个字节会被截断。我的做法是,在发送完一帧数据后,延时一个字节时间再加一点保险,再拉低发送使能引脚。这个延时可以按波特率计算:115200bps时,一字节大约87µs,保险延时设为200µs就足够。实测在115200波特率下,连续发送多帧,从机无丢失帧现象。
总线上挂载的从机数量,按标准计算:驱动器的接收输入阻抗是标准单位负载的1/4,一条总线上可以挂最多128个节点。实际项目中挂了16个温度采集节点,加上主控板,总线负载仍然很轻,信号质量良好。每个节点都有一个拨码开关设置地址,地址范围1~16,主控轮询周期1秒,16个节点全部采集一遍只需要3秒,足够满足HVAC巡检周期。帧格式采用Modbus RTU:地址码、功能码、寄存器地址、数据、CRC16。CRC计算做了查表优化,一帧CRC计算时间在几十微秒级别,不影响轮询周期。整条RS485链路的关键点在于:地线必须连通,屏蔽层单端接地,避免地环路;两端终端电阻不能省,否则长线反射会造成帧错误。
4.3 调试串口日志系统与问题定位方法
调试阶段,串口日志帮了大忙。我配置了一个调试串口,输出采用异步打印方式,波特率115200,8N1。日志级别分了四档:ERROR、WARN、INFO、DEBUG,编译时通过宏开关控制级别。日常跟踪时用INFO级别,排查I2C问题时直接开DEBUG,把每次读写的寄存器地址、数据、错误码全打出来。打印函数用了可变参数重定向到fputc,并且内部加了临界区保护,防止调试信息输出时和中断抢占造成乱码。
调试串口的布线也有一点讲究:调试串口的RX/TX不要和RS485的差分A/B靠得太近,否则调试口电平跳变会通过PCB寄生耦合干扰RS485的差分信号。我在第二版PCB上把调试串口安排在了板子左侧,RS485接线端子放在右侧,两者间距超过3cm,实测干扰明显下降。如果条件允许,调试串口和RS485最好分属不同接插件区域,并用地线隔离。
串口调试还有一个非常实用的技巧:在关键函数入口和出口各打一条日志,记录函数名和时间戳,可以快速定位死循环或卡死位置。我在单总线温度读取函数、FRAM写入函数和I2C读取函数里都埋了这种日志,后期系统偶发卡顿时,靠日志时间戳分析,很快定位到是某个I2C设备无应答导致while循环未退出。最终在读取函数里增加了超时退出逻辑,超过50ms即放弃本次读取,并置错误标志,然后主循环在下一秒重试,系统的稳定性立刻提升了一个档次。
5. 常见问题与排查技巧实录
5.1 温度读数不准与数据跳变的分层排查
温度读数和参考温度计对不上,是第一轮调试中最常见的故障现象。我总结下来,大致有四种成因:传感器自热、接触热阻、参考电压漂移、软件滤波不足。传感器自热方面,SHT30正常工作时自身功耗很低,但如果I2C频繁读取或者内部加热器误开启,温度会偏高0.3~0.8℃。解决方法是把读取频率控制在1Hz,并检查配置寄存器确认加热器处于关闭状态。接触热阻是安装问题,热敏电阻或传感器探头没有和被测温表面贴紧,中间有空气层,导致读数滞后严重。现场应用时,我会在探头和被测面之间涂导热硅脂,并用机械方式压紧,滞后时间能缩短一半以上。
参考电压漂移是NTC方案特有的坑。如果ADC参考直接用3.3V系统电源,当系统负载变化时,3.3V会波动十几毫伏,这个波动直接折算到温度读数上就是0.3℃左右的偏差。换用独立3.0V基准源后,这个问题彻底解决。另外一个隐蔽的问题:电源走线过窄导致VREF引脚噪声过大,也会引起ADC结果低位跳动,我把VREF引脚旁边的滤波电容从0.1µF加大到1µF,噪声峰值明显改善。软件滤波不足则表现为读数毛刺,一帧数据偶发跳变1℃以上,通常来自EMC干扰或I2C读取的数据错误。中值滤波加滑动平均解决这个问题很有效,配合CRC校验剔除错误帧后,毛刺基本消失。
排查这类问题时,我建议先固定变量:先用精密电阻箱模拟NTC阻值,验证ADC和查表逻辑是否正确;再用恒温槽校准SHT30读数;最后才放到实际环境里联调。这样分层定位,效率比盲目改滤波参数高得多。
5.2 I2C总线死锁分析与恢复策略
I2C总线死锁是嵌入式开发里最经典的疑难杂症,表现形式是总线上的SCL或SDA被拉低,主机发起的起始条件永远无法建立。死锁的根本原因一般是某个从设备在时序异常时处于中间状态,正试图拉低SDA,而此时主机不再驱动时钟。尤其是在设备供电瞬间、总线竞争或EMC干扰时,更容易遇到。我遇到过一次:FRAM写入中断电,恢复供电后,I2C总线一直被拉低,复位MCU也没用,直到断电再上电才恢复。这让我意识到,光靠复位MCU不够,必须在软件里实现总线恢复机制。
总线恢复的标准做法是:将SCL对应的GPIO引脚临时配置为推挽输出,手动产生9个时钟脉冲,让卡死的从设备完成当前字节传输,释放SDA。做完9个脉冲后,再发送一个STOP条件,随后重新初始化I2C外设。这个过程要在系统启动时、每次I2C操作失败后都执行一遍。我实现了一个函数:当检测到总线忙超过100ms时,强制执行9脉冲恢复流程。实测下来,对于绝大多数卡死场景都能恢复。还有一个预防性措施:每次I2C操作前后都检查总线空闲状态,如果总线不空闲,不盲目发送起始条件,而是先恢复再操作。
FRAM写入操作的另一个经验是,在I2C写操作完成前,不要立刻让MCU进入低功耗模式或复位,否则FRAM内部状态机可能因时序不完整而卡住。我统一在FRAM写入后加了一个2ms的延时,确保总线时序完整收尾。
5.3 RS485通信偶发乱码与地环路问题处理
RS485偶发乱码排查起来,比普通串口麻烦得多。乱码不固定,时有时无,可能在某个节点接入后出现,也可能在电机启停时出现。这类问题多数来自三方面:终端电阻缺失、公共地电位漂移、屏蔽层接地错误。
先说终端电阻。标准485总线两端各需要一个终端电阻,但很多项目只加了一端或者完全没加,结果长线反射会让波形振铃,在波特率较高时误码率明显上升。我的处理方式是:首末节点各焊一个120Ω电阻,中间节点不焊,总线空闲时差分电压在2V左右,波形无过冲。另外,总线分支线要短,尽量小于30cm,分支过长相当于人为制造阻抗不连续点,也会反射。接线时用双绞线,保证差分对的耦合度和共模抑制能力。
公共地电位漂移是另一个常见问题。理论上RS485是差分传输,不依赖地线,但如果发送端和接收端之间的地电位相差太大,超过收发器的共模输入范围(通常-7V~+12V),就会造成误码甚至芯片损坏。排查方法是:测量两个节点之间的地线电压,一旦超过1V就要警惕。可靠做法是,每个节点单独用双绞线中的一根做信号地,同时总线上只在一端通过RC网络连接到大地,避免多点接地产生地环路电流。屏蔽层只在一端接地,防止屏蔽层成为地环路的一部分。解决地环路后,我的485链路在长距离、强干扰环境下连续运行非常稳定,再把波特率从9600提高到115200也能正常工作。
5.4 固件升级Bootloader设计与参数持久化经验
设备交付后,固件升级是绕不开的话题。我给板卡设计了两种固件升级方式:一种是在应用层通过RS485接收固件包,写到外部Flash,再通过Bootloader跳转;另一种是本地通过调试串口使用专用工具直接烧录。生产线上主要用后一种,现场升级用前一种。
Bootloader设计时,我把内部Flash划分成三个区域:Bootloader区、App区、参数区。Bootloader区放最小启动代码和固件接收、校验逻辑;App区放业务代码;参数区存放设备地址、报警阈值、校准值等信息。每次上电,Bootloader先检查App区的有效性,无效则进入升级模式等待接收固件;有效则直接跳转到App。跳转前需要重定位中断向量表,在App启动代码最前面设置SCB->VTOR为App区起始地址。用STM32F415ZG时,直接写SCB->VTOR=APP_ADDRESS,在带系统Bootloader的情况下,这条代码必须在main函数最开头、任何外设初始化之前执行,否则中断一进来就会跑飞。
固件升级过程中,校验是关键。我采用CRC32对固件包进行校验,分包接收,每包都校验CRC16,全部接收完再校验整个固件的CRC32。升级中途断电也不会损坏App区,因为写入的固件是先写到暂存区,校验通过后才把暂存区内容搬运到App区。参数持久化存储在专用参数区,由于用的是FRAM,写入寿命不是问题,读取速度也快。但参数区和App区要分开,不能混在一起,否则App升级会覆盖参数。参数初始化时,先读取参数区的魔数,如果魔数不匹配,说明参数区从未被初始化,就写入默认参数并设置魔数。这一步很实用,避免了首次上电时参数区是随机数据导致设备处于未知状态。
6. 扩展与移植方向思考
6.1 从HVAC场景到通用监测平台的适配
这套系统虽然围绕HVAC场景设计,但硬件平台和软件架构并不局限于某个垂直领域。主控MCU带有丰富的通信外设和ADC资源,温度传感器挂接方式也是标准总线,完全可以复用到一个通用的低成本温度监测平台。比如,把TFT显示器和按键去掉,只保留传感器接口、RS485、指示灯,整板面积和BOM成本都能大幅下降,得到一个纯远程监测节点。监测节点采用12V供电,功耗约1W,可直接安装在更多应用场景中。RS485总线可以级联多个节点,主站统一采集,后台软件轮询即可。
软件架构上,我把传感器采集、通信协议、显示模块、参数存储各层之间通过模块化接口解耦。要让这套系统适配新的应用场景,只需要替换传感器驱动层和UI层,通信协议层和数据采集框架可以原样保留。比如从HVAC场景切换到设备机房环境监测时,只需把温度量程和报警阈值参数改掉,主控逻辑几乎不用动。这种“平台化”的思维方式,比针对每个项目重新写一套代码要省力得多。
6.2 冗余监测设计与系统可靠性增强
系统实际运行中还做了冗余测温点部署。本地两路温度、远程一路温度,三个温度源互为校验。当本地SHT30和NTC的读数差异超过预设阈值时,系统会进入“传感器异常”状态,优先采信与历史数据更接近的那一路,并记录异常日志。远程温度如果连续多次读取失败,系统会自动把数据显示为“离线”,不影响本地监测。这种多测点冗余设计在HVAC现场特别重要,因为一个传感器读数漂移可能会误导整个控制策略,而三个独立源做交叉校验,能显著降低误判概率。
冗余策略还包括电源和复位部分:板卡供电同时支持外部12V和USB 5V,两路之间通过二极管隔离,板上LDO和开关稳压器共同输出3.3V和5V。USB供电在调试时方便,12V供电在工业现场使用方便,两路电源自动切换不会产生电压倒灌。复位电路由专用的复位芯片提供,上电复位和手动复位都通过复位芯片完成,低电压时也能可靠复位,避免MCU在电压不稳时执行未知指令。远程升级时,Bootloader先检测输入电压是否稳定,如果不稳定会回滚到旧固件,防止升级过程中断电导致设备变砖。
6.3 接入上层系统与云平台的可能性
RS485 Modbus协议作为工业现场事实标准,让这套板卡可以很方便地接入其他系统。后台监控软件、PLC、HMI等设备都支持标准Modbus RTU,只需要按照寄存器映射表读取温度、湿度和设备状态即可。我也在网关和云平台方向做过尝试,通过在网关侧写一个小程序,把Modbus轮询到的数据重新封装后推送到云端,就实现了远程网页端和手机端的实时温度查看。整个链路的瓶颈在网关的处理能力,而不在板卡本身,因为Modbus请求的响应时间很短。
如果要进一步扩展,还可以考虑引入实时操作系统,把传感器读取、显示刷新、通信处理、参数存储拆分成独立任务。RTOS的好处是让实时任务和非实时任务各自拥有独立的执行上下文,降低模块间耦合度。但对于当前这个规模的项目,裸机状态机已经能够满足所有实时性要求,引入RTOS需要权衡学习成本和收益。
7. 附录:关键参数速查表
为方便复现,我把本项目中使用到的关键参数整理成一张速查表,供后续开发和调试时直接参考。
| 项目 | 参数值 | 备注 |
|---|---|---|
| 主控MCU | PJ85718DM与STM32F415ZG协同 | 模拟前端+主控分离 |
| 系统时钟 | 48MHz(PLL: 25MHz HSE, M=25, N=192, P=4) | 经PLL产生 |
| APB1时钟 | 48MHz(分频4) | 用于I2C、USART、SPI2 |
| APB2时钟 | 96MHz(分频2) | 用于定时器 |
| ADC时钟 | 12MHz | 用于NTC采样 |
| 定时器时钟 | APB1定时器48MHz,APB2定时器96MHz | TIM2/TIM3通用 |
| I2C1 | 100kHz,标准模式 | 连接SHT30、FRAM |
| USART2调试串口 | 115200,8N1 | 日志输出 |
| RS485波特率 | 115200,可降至9600 | Modbus RTU |
| SPI2 | 12MHz | 连接TFT显示屏 |
| 单总线延时 | 复位480µs / 释放60µs / 应答70µs | 单总线时序参数 |
| SHT30地址 | 0x44(ADDR拉低/悬空) | 可切换到0x45 |
| FRAM地址 | 0x50 | 参数存储和日志 |
| NTC | 100kΩ@25℃,B=3950,ADC1通道 | 半桥分压采样 |
| RS485终端电阻 | 120Ω(首末节点) | 反射抑制 |
| 液晶驱动 | ILI9341,SPI接口,320x240 | 2.8寸TFT |
| 时钟晶振 | 25MHz无源晶振,2×18pF负载电容 | 实测波特率误差小于0.3% |
| RS485节点数 | 最多128个 | 每节点1/4单位负载 |
| 电源输入 | 12V DC / USB 5V | 两路自动切换 |
| 固件校验 | 分包CRC16 + 整体CRC32 | Bootloader升级用 |
| 日志级别 | ERROR / WARN / INFO / DEBUG | 编译时宏控制 |
| 报警逻辑 | 温度越限红色快闪 + FRAM日志 | RGB LED指示 |
8. 尾声:一点实际经验分享
这一套从选型、原理图、PCB到固件全流程走下来,我最想说的是:温度监测看着简单,真正把精度、稳定性和可维护性都做到位,需要从硬件物理层一路抠到软件状态机,任何一个环节松懈都会在后期成倍放大问题。调试过程中踩过的坑不算少,但每次解决之后,对系统的理解也更深一层。回头看,那些省下来的时间,其实都花在了前期规范上:时钟链路算清楚再画板,接口物理层规范一步到位,传感器数据做足校验,状态机能简则简。前期多花一天,后期省一周,这句话在嵌入式项目里是大概率成立的。
16个温度采集节点、1秒轮询周期、RS485总线、本地双路传感器、远程差分链路,把这些全部跑通并稳定运行,靠的不是某个单点技巧,而是整条链路每一环都做到位。第一次画板时我也有过想省略的冲动,比如省终端电阻、省基准源、省CRC校验,但每一个被省略的环节,最终都在调试阶段以更难看的方式回来了。如果你也在做一个类似的温度监测系统,我的建议很简单:把基础做扎实,别急着堆功能。传感器选型多花半天比较,时钟链路多算一遍,接口物理层一步到位,这些看似枯燥的基础工作,才是系统长期稳定运行的真正保障。系统里留一点扩展余地,比如备份I2C通道、预留SPI接口、Flash分区留足空间,比事后重新画板要经济得多。目前这块板卡已经在某测控环境连续运行数周,温度数据稳定,无异常。后续如果条件允许,我会再做一版更低功耗的无线采集节点,到时候继续分享实际经验。