ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

PIC24搭配数字温度模块,实现本地与远程温度监测系统

PIC24搭配数字温度模块,实现本地与远程温度监测系统 1. 方案思路与器件分工为什么是 PIC24 配一体式温度模块1.1 先别急着选型把需求拆清楚再动手我这边的项目背景是给一套 HVAC 集中控制系统做温度监测节点的升级。原来的方案是每个温度点用 NTC 热敏电阻 运放调理 ADC 采样主控用的是 8 位单片机PCB 上一堆分压电阻、滤波电容生产校准还要逐台调零。随着监测点位从十几路增加到五十几路远程点位也要接入老方案的痛点就非常明显了布线难、误差大、维护靠人工抄表。所以这次项目立项的时候我定的硬性指标是支持本地温度采集至少两路用于 PID 调节和报警联动。支持远程温度采集通过总线方式接入节点数不低于 30 个。控制核心功耗要低特别是待机模式下系统不能长时间满载运行。通信协议尽量标准化方便上位机对接不搞私有协议全家桶。主控要有足够的代码空间和 RAM后续要扩展数据记录功能。基于这些需求我最终选了PIC24FV16KA301做主控搭配PJ85718DM作为温度前端模块。这一组合的核心思路是把“测温度”这件事从前端电路彻底剥离出去主控只负责读取数字量、做逻辑、跑协议不碰模拟电路省掉标定环节也大幅降低了硬件调试难度。1.2 PIC24FV16KA301 到底赢在哪里先聊聊主控选型。HVAC 场景和消费电子不一样设备常年 7×24 小时挂在墙面上或者机柜里控制板空间紧凑很多情况下没有风扇、没有密封防护等级高的外壳所以我对主控有几个硬性要求供电电压要宽3.3V 是主流但系统里可能还有电池后备供电。静态功耗要低休眠时最好能到微安级。外设要够用至少两路 UART、一路 I2C/SPI、若干 ADC 通道。引脚不能太多封装小方便塞进现有控制板结构。PIC24FV16KA301 属于 PIC24F 系列的 XLP 超低功耗型号VDD 范围 1.8V 到 3.6V深度睡眠模式电流可以做到几百纳安级别。它内部有 16 KB Flash 和 1.5 KB RAM写小型协议栈和温度采集调度逻辑完全够用。更重要的是它的外设映射Peripheral Pin Select非常灵活UART、I2C、定时器这些模块可以映射到不同引脚上这给 PCB 布线省了极大的力气。从成本角度看这颗芯片不算贵而且 16 位内核相比 8 位机做 Modbus RTU 的 CRC 运算也轻松很多。我在调试阶段甚至直接开着调试器跑轮询循环单步跟踪时序没有出现 8 位机那种中断优先级互相打架的问题。1.3 PJ85718DM 模块的角色定位把温度检测变成读寄存器PJ85718DM 这个模块我拿到的是数字输出型内部集成了温度传感元件、信号调理、ADC 和数字通信接口。对外输出直接就是温度数据不需要额外做冷端补偿或者查分度表。这就相当于把以前一整套模拟前端全部封装进了一个小模块主控通过 I2C 或者单总线读取寄存器拿到的是已经换算好的数据单位是 0.1℃ 或 0.01℃ 之类。实际项目中我用了两种接法本地温度模块直接焊接在主板上通过 I2C 接到 PIC24 的 I2C1 外设用于采集送风温度、回水温度。远程温度使用同型号模块通过一根三芯屏蔽线引出接到远程风机盘管或者冷梁末端模块工作在单总线模式下通过地址寻址挂到同一根总线上。这种“本地 I2C 远程单总线”的双模式设计是参照了模块手册里给出的典型应用图。实测下来效果不错因为本地数据要进 PID 调节刷新率要求高I2C 的时钟可以跑到 400 kbps而远程节点数量多单总线只需要一根数据线走线成本低轮询几十个点也够用。注意如果你用的温度模块是模拟输出类型那主控选型就得另外考虑 ADC 通道和基准电压的问题。我这里说的 PJ85718DM 是数字输出模组整个方案的接线和驱动代码都是按数字接口写的。2. 硬件设计与接线细节本地测温、远程总线、电源与电平匹配2.1 主控最小系统不要忽略去耦和调试接口PIC24FV16KA301 最小系统不算复杂但每个引脚都不能想当然。我这次画板子的时候总结了几个关键点供电部分3.3V LDO 输出端要并联 10 μF 钽电容和 100 nF 陶瓷电容LDO 输入端的 5V 电源也要先经过一个磁珠隔离开关电源的高频噪声。HVAC 现场经常有大功率变频设备电源线上如果不处理温度采样的数据会明显跳动。复位电路用 MCLR 引脚外接 10 kΩ 上拉电阻和 100 nF 电容到地。有些工程师图省事直接把 MCLR 接 VDD但后期如果要用 ICSP 调试就会发现连接不上还得飞线。ICSP 调试接口一定要留出 5 针或 6 针的排针接口我见过太多次“焊完板子发现没法烧录”的场面。引脚顺序固定好另外串联 100 Ω 电阻可以避免调试线过长导致信号振铃。晶振选择系统时钟我用内部 FRC 振荡器配合 PLL 倍频到 8 MHz做 Modbus 轮询和 I2C 时序足够了。休眠唤醒用 32.768 kHz 外部晶振接到 SOSCI 和 SOSCO这样深度睡眠唤醒后的时钟切换比较平滑。如果项目对时间戳有要求这枚晶振还可以给 RTCC 模块用实现掉电后的日期时间保持。2.2 PJ85718DM 本地接法上拉电阻、滤波电容与线长限制本地模块我放在主控旁边间距控制在 20 mm 以内走线尽量短减少天线效应。I2C 接口的上拉电阻我用了 4.7 kΩ对于 400 kbps 的总线速率来说是标准值。如果总线距离超过 50 cm建议改成 2.2 kΩ 或者降低速率到 100 kbps否则上升沿太缓数据容易错位。电源引脚上必须放 100 nF 去耦电容位置越靠近模块的 VDD 引脚越好。有些模块内部有线性稳压器电源轨对纹波并不敏感但去耦电容不能省毕竟现场可能有静电放电和浪涌干扰。还有一个细节模块的 AD0/AD1 地址引脚我直接做了跳线焊盘默认接地通过 0 Ω 电阻选择。这样同一块主板上如果需要挂两个本地模块可以改地址不用改 PCB。踩坑提示我第一次画板子的时候把模块地址引脚悬空了结果两个模块地址冲突I2C 总线直接锁死主控读到的全是 0xFF。这种问题在实验室里不容易复现但是一接入总线就随机出现。所以地址引脚要么接固定电平要么用排针跳线绝不能悬空。2.3 远程温度总线设计单总线怎么挑线、怎么并接远程温度节点我采用的是单总线拓扑。一主多从所有模块的数据线并接在一起通过一根线串联所有节点。主干线我用的是 RVSP 屏蔽双绞线其中一对线作为电源和数据返回屏蔽层单端接地接在主控的电源地。这里有几个参数要记好单总线典型速率在 15.4 kbps 左右理论上可以支持较长的线长。建议节点数不超过 32 个距离控制在 100 米以内超过就要考虑中继器。每个节点在数据线上需要加一个 4.7 kΩ 上拉电阻到本地 3.3V这样保证在空闲状态数据线被稳定拉高。数据线上的寄生电容是最大的敌人。每米双绞线大约有 50~60 pF 的分布电容50 米线就有 3 nF 左右拉着这根线信号上升沿会变慢。所以远端节点的上拉电阻可以适当减小到 2.2 kΩ但注意不要低于 1 kΩ否则总线上灌电流太大模块输出级可能会过载。实际调试的时候我在主控端的数据线上串联了一个 100 Ω 电阻这能有效抑制反射。对于单总线来说线的末端不建议并联 120 Ω 终端匹配因为没有规定特征阻抗加了反而增加直流负载。2.4 RS-485 远程通信链路选收发器、处理方向控制既然标题里说“本地与远程”远程点位采集回来后还要上报给上位机。这部分我用的是 RS-485 总线主控的 UART1 连接到一颗半双工 RS-485 收发器通过 DE/RE 引脚控制发送和接收方向。选型上我用了带失效保护的收发器型号不唯一只要满足以下条件即可支持 3.3V 供电这样不用额外做电平转换。驱动器输出带限流短路不损坏。接收器带失效保护总线开路或短路时输出为确定高电平避免收到随机噪声。方向控制这里有个容易犯的错误DE 和 RE 如果分别用两个引脚控制固件里必须保证先切换方向再操作 UART。我这边偷了个懒把 DE 和 RE 短接在一起用一根 I/O 控制高电平发送、低电平接收虽然牺牲了一点灵活性但稳定可靠。A、B 输出端各串一个 10 Ω 电阻防止现场浪涌打坏芯片。总线两端各接一个 120 Ω 终端电阻这个不能省长线反射会直接导致 CRC 错误率飙升。3. 软件架构与核心代码实现轮询、协议栈与低功耗调度3.1 主程序的循环思路一个状态机搞定本地和远程整个固件我是用状态机写的主循环不断轮询事件标志位所有耗时操作都拆到中断里完成保证温度刷新率和通信响应速度。先看整体结构#define LOCAL_SAMPLE_INTERVAL_MS 200 #define REMOTE_POLL_INTERVAL_MS 1000 #define DEFAULT_MODBUS_ADDR 0x01 #define LOCAL_SENSOR_ADDR 0x50 typedef enum { EVT_NONE 0, EVT_LOCAL_SAMPLE, EVT_REMOTE_POLL, EVT_MODBUS_RX, EVT_UART_TX_DONE, } event_t; volatile event_t evtFlag; uint16_t localTemp; // 本地温度单位 0.1℃ uint16_t remoteTemp[32]; // 远程温度数组 uint8_t remoteCnt; // 有效的远程节点数事件标志在定时器中断或者 UART 接收中断里置位主循环里统一处理。这样做的好处是逻辑清晰不会出现“在中断里跑协议栈导致时序乱掉”的情况。主循环大致如下while (1) { if (evtFlag EVT_LOCAL_SAMPLE) { evtFlag ~EVT_LOCAL_SAMPLE; readLocalSensor(); } if (evtFlag EVT_REMOTE_POLL) { evtFlag ~EVT_REMOTE_POLL; pollRemoteNodes(); } if (evtFlag EVT_MODBUS_RX) { evtFlag ~EVT_MODBUS_RX; parseModbusFrame(); } }3.2 读取 PJ85718DM 温度I2C 时序与数据转换读取本地传感器时我走的是 I2C 接口。模块内部有若干 8 位寄存器温度数据是 16 位补码形式地址指针会自动递增。我每次连续读 2 个字节组合成一个 uint16_t。关键代码片段uint8_t i2cReadReg(uint8_t devAddr, uint8_t regAddr, uint8_t *buf, uint8_t len) { // 发送起始条件 设备地址写方向 寄存器地址 I2C1CONbits.SEN 1; while (I2C1CONbits.SEN); I2C1TRN (devAddr 1) | 0; while (!I2C1STATbits.TRSTAT); if (I2C1STATbits.ACKSTAT) { I2C1CONbits.PEN 1; return 1; } I2C1TRN regAddr; while (!I2C1STATbits.TRSTAT); if (I2C1STATbits.ACKSTAT) { I2C1CONbits.PEN 1; return 1; } // 重复起始 设备地址读方向 I2C1CONbits.RSEN 1; while (I2C1CONbits.RSEN); I2C1TRN (devAddr 1) | 1; while (!I2C1STATbits.TRSTAT); if (I2C1STATbits.ACKSTAT) { I2C1CONbits.PEN 1; return 1; } for (uint8_t i 0; i len; i) { I2C1CONbits.RCEN 1; while (!I2C1STATbits.RBF); buf[i] I2C1RCV; if (i len - 1) { I2C1CONbits.ACKDT 0; // ACK } else { I2C1CONbits.ACKDT 1; // NACK } I2C1CONbits.ACKEN 1; while (I2C1CONbits.ACKEN); } I2C1CONbits.PEN 1; while (I2C1CONbits.PEN); return 0; }温度换算很简单模块手册里给了公式我直接照搬int16_t raw (int16_t)((buf[0] 8) | buf[1]); temperatureC raw * 0.01f;如果模块精度是 0.1℃就乘 0.1如果是 0.01℃就乘 0.01。我这里明确一点不要凭感觉猜必须翻手册确定分辨率。本地采样我做了软件滑动平均取最近 5 次读数的平均值输出给 PID 模块。因为 HVAC 现场有气流扰动瞬时值波动很大但 5 次平均后温度曲线平滑多了同时延迟也只有 1 秒左右完全不影响调节响应。3.3 远程节点轮询单总线协议的精确时序单总线的难点在于时序要求苛刻读写时隙都是微秒级的。PIC24FV16KA301 跑 8 MHz 的时候一个指令周期是 500 ns用来计时隙刚刚好。我用的是查询方式不进中断避免时序被中断破坏。单总线协议的时序我记得大概是这样的参考常见协议实现复位脉冲总线拉低 480 μs 然后释放。存在检测从设备在释放后拉低 60~240 μs。写 0 时隙拉低 60 μs。写 1 时隙拉低 1~15 μs然后释放。读时隙拉低 1~15 μs释放后采样数据线。代码里我封装了一个 delayMicro 函数用定时器做基准保证延时准确。注意在单总线上操作必须关闭中断仲裁就是别让 I2C 或者其他外设来打断这微秒级的时序。轮询的逻辑是先发复位脉冲找到所有在线节点然后逐节点发送读取命令。PJ85718DM 的地址是 64 位激光刻录的通过 ROM 命令匹配。流程大致是复位。发送 0x55 匹配命令。发送 64 位 ROM 地址。发送 0x44 启动温度转换。等待转换完成典型时间是 500 ms。发送 0xBE 读取寄存器。远程节点的数量我做了动态扫描。系统启动时广播复位命令统计总线上返回的存在脉冲数量然后逐个尝试读取地址存到一个数组里。这样后续轮询就只访问实际存在的节点速度更快。注意单总线的转换时间比较长远程节点不能每个都等转换完成再读下一个。我的做法是先广播启动所有节点转换等待 750 ms 后再逐个读取这样总耗时基本恒定不会随着节点数线性增加。3.4 Modbus RTU 上报协议主从帧怎么解析和打包远程数据汇总到主控后主控要作为从机响应上位机的请求。我用标准 Modbus RTU 功能码 03读保持寄存器来上报温度和状态上位机只要支持 Modbus 协议就能直接接入不需要定制驱动。帧格式大概是从机地址1 字节。功能码1 字节。起始地址2 字节。寄存器数量2 字节。CRC162 字节。PIC24 发送温度数据之前要先把温度值填充到寄存器映射表里。例如温度 25.6℃如果单位是 0.1℃存的是 256也就是 0x0100。CRC16 的实现我是查表发的速度很快uint16_t calcCRC16(uint8_t *buf, uint8_t len) { uint16_t crc 0xFFFF; for (uint8_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; }收到上位机帧之后我先解析地址确认是本机地址再校验 CRC最后判断功能码和寄存器地址。非法请求直接回异常码不响应会造成上位机长时间等待反而更容易触发它那边的超时重试。3.5 定时器调度本地快、远程慢、Modbus 响应快系统的三个关键循环我设了不同的周期并不是所有事件都用同样节奏任务周期优先级说明本地温度采样200 ms高闭环控制需要不能太慢远程温度轮询1 s中能效监测和报警秒级刷新足够Modbus 请求解析事件触发高上位机等待响应越短越好看门狗清狗视需求高死循环保护必须进主循环TMR1 配置为 1 ms 中断每 200 个 tick 置位一次本地采样事件每 1000 个 tick 置位一次远程轮询事件。看门狗我用了 PIC24 内部的 WDT超时周期设置在 2 秒左右主循环里每次循环末尾清狗。如果哪次运行卡死在某个 I2C 等待循环里WDT 会强制复位比人工复位快多了。调试阶段我把 WDT 关掉了因为单步调试会被反复复位。正式烧录的时候才打开。这个一定要列到发布检查清单里。4. 实操过程与调优记录从裸板到稳定运行的几个关键节点4.1 裸板调试J-Link 用不了MPLAB X 才是正路我拿到样板后第一步先不焊温度模块只焊主控、晶振、电源和 ICSP 接口。先用 MPLAB X IDE 连接 PICkit4确认目标板能正常识别再烧一个闪烁 LED 的测试程序验证 GPIO、时钟和复位电路都没问题。这期间我遇到一个很典型的坑晶振死活不起振后来发现是晶振负载电容焊错了方向两个电容焊成了一样大的导致振荡裕量不足。HVAC 控制板常有震动环境晶振如果起振不稳定后期会偶发复位很难排查。我最终用了两个 18 pF 的 NPO 电容布局上尽量靠近晶振引脚。4.2 本地温度采样调试数据跳变的三个阶段第一阶段我把 PJ85718DM 焊到主板上读取到的数据在 24℃ 到 32℃ 之间乱跳明显不是真实室温。我先怀疑是天线的干扰于是用示波器量了 I2C 数据线发现 SDA 边沿有明显的振铃。解决办法是降低 I2C 速率到 100 kbps同时把上拉电阻从 4.7 kΩ 换成 2.2 kΩ。这样处理后信号确实好了很多但温度依然还有 ±1℃ 的波动。我又去查模块的电源引脚才发现 3.3V 上叠加了约 50 mV 的噪声这是 LDO 后级滤波不足导致的。最后在模块电源引脚旁边又加了一颗 4.7 μF 的电容和一个 10 Ω 的串联电阻组成低通滤波噪声降到了 10 mV 以内温度读数稳定到 ±0.2℃。这个经历给我的启发是温度模块虽然输出数字量但对电源质量依然敏感滤波不能省。4.3 单总线远程调试从一根线到三十个节点的进化远程节点我一开始只挂了两个接了一根十几米的屏蔽线调试还算顺利。后来扩展到 16 个节点后轮询开始出现漏数据有的节点每隔几轮才响应一次有的直接没有响应。排查过程从软件入手先是怀疑地址冲突逐个验证 ROM 地址后发现每个节点的地址都唯一。接着换了一根质量更好的屏蔽双绞线把屏蔽层在终端做了单端接地问题依然存在。后来翻到模块手册发现单总线的转换电流和电源电流加起来每节点大约 8 mA16 个节点就是 128 mA而我的远程电源走线用的是长距离细线线压降把远端节点的电压拉低到了 2.9V 左右。解决方法是把远程供电改成 5V 传输在模块附近用电容降压或者稳压到 3.3V。每个节点前端放一颗 100 μF 的电容做储能这样瞬时电流有保障电压跌落问题就消失了。后面我又扩展到 28 个节点轮询周期保持在 1.5 秒左右数据稳定。4.4 功耗实测记录标称和实际往往差一截PIC24FV16KA301 的低功耗性能不错但我按照手册里的参数估算过一版发现如果只做数字采集和 Modbus 从机待机功耗大概可以做到 20 μA 左右。实际上我测到的整板待机电流是 240 μA多出来的部分基本来自 RS-485 收发器的静态功耗、LDO 自身静态电流和 PJ85718DM 的空闲电流。如果想要进一步压低待机功耗可以考虑RS-485 收发器在待机时由 GPIO 控制关断电源。温度模块进入关断模式只留 I2C 地址唤醒。LDO 换成低静态电流型号。不过 HVAC 系统一般不需要电池供电220V 交流转 5V 再转 3.3V 的架构下几十微安的差异无关痛痒。如果做电池供电的温湿度记录仪这步调优就非常值得了。5. 常见问题与排查技巧现场实录速查表5.1 温度读数偏高且一直不降典型现象某台远程节点读到的温度恒定在 35℃但现场用红外测温枪测只有 28℃。我第一次碰到时差点怀疑传感器坏了后来把模块用手捂热再松开发现读数能变化说明传感元件正常。问题出在安装位置模块被塞进了一个密封的接线盒里接线盒被太阳直射局部温度偏高。解决办法是把探头延长出来让传感器部分接触被测介质而不是把整个模块埋在盒子里。5.2 单总线轮询偶尔丢节点这类问题要分三步排除第一步查电源电压远端节点是否低于最小工作电压。第二步查线缆屏蔽层是否只在单端接地数据线是否远离动力线。第三步查时序示波器看总线上的复位脉冲和存在检测看看波形下冲是否过大。我最终遇到的情况是动力线和信号线走在了同一个线槽里变频器启动瞬间数据线上出现几十伏的耦合尖峰导致模块复位。物理上把信号线挪到另一个线槽并且数据线上并了一个 TVS 管之后问题彻底消失。5.3 Modbus 通信偶发超时超时的原因百分之五十是接线接触不良百分之三十是收发器方向切换太晚百分之二十是波特率偏差。方向切换这里最容易踩坑PIC24 的 UART 发送完成后如果不等待移位寄存器为空就立刻把 DE 拉低最后几个字节会直接被截掉。固件里要用 while 等待 TXIF 和 TRMT 标志位然后再切换方向不能只等发送中断标志。5.4 看门狗频繁复位但找不到原因我的经验是先跑一个只闪 LED 的最小程序看是否复位。不复位问题出在应用程序大概率是某个 while 死等外设标志位。依然复位硬件问题可能是电源跌落或者强干扰。针对固件建议把所有 I2C 读写的等待循环加上超时判断比如循环 1000 次后无条件退出返回错误码。即使出错也保证了主循环能流转起来清狗不会饿死。5.5 排查速查表问题现象可能原因处理办法本地温度跳变 ±1℃电源纹波太大、I2C 速率过高加强滤波、降速到 100 kbps远程节点响应不稳定远端电压跌落提高供电电压、节点端加储能电容单总线全部无响应数据线接反或总线未上拉检查线序、确认上拉电阻Modbus 偶发超时收发器方向切换过早等待 TRMT 置位再拉低 DE整板待机电流偏大外设未进入低功耗模式配置模块掉电模式、关掉 RS-485 电源读数比实际高很多传感器被局部热源烘烤延长探头、避免密封位置6. 我的一点个人体会整套方案从选型到稳定运行前前后后花了两周时间真正卡的环节其实不在驱动代码而在硬件细节。温度模块虽然扛起了模拟前端的大头但供电、总线上拉、屏蔽接地这些外围工作依然是决定成败的关键。PIC24FV16KA301 的低功耗特性和灵活引脚映射给了整个设计很大余量特别是后期因为结构限制改了两次 PCB 布局引脚映射不用大改省了很多事。如果现在让我重来一次我会在一开始就把将来可能扩展的功能考虑进去比如预留一路备用 I2C 接口和一路 ADC 输入不要等现场提出新需求再去飞线。温度监测这类项目最大的成本往往不是元器件而是改板和现场排查。把基础架构做扎实后面加节点、加协议都是水到渠成的事。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表