1. 项目背景与核心需求拆解温度监测这件事看起来简单真要做到“本地看得见、远程管得着”中间要踩的坑一点都不少。我这次做的项目核心就是用PJ85718DM这颗温度传感芯片搭配PIC24FJ1024GB610这颗16位单片机搭一套能同时覆盖本地显示和远程上报的温度监测系统。目标场景很明确嵌入式设备机箱内部的温度监控以及HVAC暖通空调系统的风道、回风、出风温度采集。先说为什么选这两个器件。PJ85718DM 是一颗数字温度传感器I2C接口精度和稳定性在同类里属于比较扎实的工作电压范围宽封装也小适合塞进空间紧张的板子上。PIC24FJ1024GB610 则是Microchip家PIC24F系列里资源相当充裕的一款1MB Flash、96KB RAM外设丰富带多个I2C、UART、SPI还有足够的GPIO去驱动本地显示和继电器输出。两者搭配一个负责“感知”一个负责“决策和通信”分工清晰。这个项目解决的核心问题是在同一个硬件平台上同时实现本地温度实时显示和远程温度数据上报并且保证两路数据的一致性。很多初学者做温度监测要么只做本地LCD显示要么只做串口上传真正把两条链路整合到一起、还要处理多传感器轮询和通信冲突的才是实际工程里最磨人的部分。适合谁来参考如果你正在做嵌入式环境监测、HVAC控制板、机柜温度管理这类项目或者你手上有PIC24F系列的单片机想找个完整的温度采集案例这篇内容可以直接拿去用。哪怕你用的是STM32或者其他平台里面的架构思路和排查方法也是通用的。2. 系统整体设计与选型考量2.1 为什么是“本地远程”双链路架构先聊架构。温度监测系统最常见的两种形态一种是纯本地传感器接单片机单片机接显示屏人站在设备跟前看另一种是纯远程传感器数据通过通信接口传到上位机或云平台。这两种各有各的局限。纯本地的问题是人得在现场HVAC系统装在吊顶里、机柜放在机房角落你不可能一直守着纯远程的问题是调试阶段没有即时反馈传感器接错线、地址配错你在远端看到的只是一串乱码或者固定值排查起来很痛苦。所以我的方案是双链路并行本地用OLED或段码LCD做实时显示远程用UART转无线模块或有线RS485上传。两条链路共享同一份传感器数据但刷新策略不同。本地显示追求“即时感”采样后立刻更新远程上报追求“稳定性”做数据打包和校验按固定周期发送。这样调试的时候看本地屏部署之后看远程端两边互为备份。这里有个设计决策值得展开说传感器数据是“采一次用两处”还是“两处各自采”。我选择的是前者即单片机统一轮询PJ85718DM把温度值存在全局变量或结构体里本地显示任务和远程上报任务都从这个结构体读数据。这样做的好处是数据源唯一不会出现本地显示25.3度、远程收到25.1度这种尴尬情况。代价是需要处理好数据更新和读取的时序后面讲任务调度的时候会细说。2.2 PJ85718DM的选型理由与关键参数PJ85718DM这颗传感器我选它的原因有几个。第一是I2C接口两根线就能挂多颗HVAC场景里经常要测多个点的温度比如回风、出风、盘管表面用I2C总线可以挂多颗传感器通过地址区分布线非常干净。第二是精度在0到50度这个HVAC常用区间内它的典型精度能到±0.5度以内对于空调控制来说完全够用。第三是低功耗待机电流很小适合长期在线监测的场景。实际使用中需要注意几个参数。工作电压方面PJ85718DM支持的范围比较宽但I2C总线的上拉电阻要接到和单片机逻辑电平匹配的电源上PIC24FJ1024GB610的I2C引脚是开漏输出上拉电阻一般选4.7kΩ如果总线走线较长或者挂了多颗传感器可以降到2.2kΩ增强驱动能力。转换时间方面单次温度转换需要几十毫秒如果轮询多颗传感器要算好总时间别让采样周期短于所有传感器转换时间之和。还有一个容易忽略的点传感器地址配置。PJ85718DM的I2C地址通常由硬件引脚决定有的型号可以通过ADDR引脚接不同电平来改地址。挂多颗的时候一定要先把每颗的地址确认清楚写进代码的地址表里。我见过有人把两颗传感器都配成同一个地址总线上数据冲突读出来的温度跳来跳去查了半天以为是电源问题。2.3 PIC24FJ1024GB610的资源分配思路PIC24FJ1024GB610的资源在这个项目里怎么分我大致列一下I2C1接PJ85718DM传感器总线可能挂1到4颗。UART1接远程通信模块做数据上报。UART2留作调试串口打印日志。定时器1做系统时基比如1ms中断用于任务调度和超时计数。定时器2做采样周期定时比如每500ms触发一次温度采集。GPIO驱动本地OLED的I2C或SPI接口以及几个状态指示灯。Flash存传感器地址表、校准参数、通信协议配置。这里的关键是定时器分工要清晰。我试过用一个定时器同时做时基和采样触发结果中断里逻辑太复杂偶尔出现采样丢失。后来改成定时器1专门做1ms系统滴答定时器2做采样周期中断里只置标志位主循环去处理具体任务稳定性明显提升。注意PIC24F系列的I2C外设在使用时要特别注意时钟 stretching 的处理。PJ85718DM在某些转换阶段会拉低SCL如果单片机I2C驱动没有正确处理等待会读到错误数据。3. 硬件连接与关键细节3.1 传感器接口电路与上拉电阻计算PJ85718DM和PIC24FJ1024GB610的连接核心就是I2C总线SDA、SCL两根线加上电源和地。电路本身不复杂但细节决定成败。上拉电阻的计算我一般按这个思路来I2C总线的上升时间要满足协议要求标准模式100kHz下上升时间不超过1000ns快速模式400kHz下不超过300ns。总线电容包括走线电容、引脚电容和器件电容经验值每颗器件约10pF走线每厘米约1pF。假设总线总电容C约100pF上拉电阻R和上升时间tr的关系近似为 tr ≈ 0.847 × R × C。要满足300nsR ≤ 300ns / (0.847 × 100pF) ≈ 3.5kΩ。所以快速模式下选2.2kΩ到3.3kΩ比较稳妥标准模式选4.7kΩ到10kΩ都可以。我实际用的是4.7kΩ因为我的总线不长挂的传感器也不多100kHz标准模式足够。如果你要挂4颗以上传感器或者走线超过20厘米建议降到2.2kΩ同时用示波器看一下波形确认上升沿没有太圆钝。电源去耦也不能省。每颗PJ85718DM的VCC引脚旁边放一个0.1μF的陶瓷电容靠近引脚放置。HVAC环境里电机、继电器动作频繁电源噪声大去耦电容能明显改善读数跳动。3.2 本地显示方案的选择与接线本地显示我试过两种一种是0.96寸OLEDI2C接口128x64分辨率另一种是段码LCD驱动简单但显示信息少。最后选了OLED因为能同时显示多路温度、单位、状态图标信息密度高。OLED接在PIC24FJ1024GB610的另一个I2C外设上或者用SPI接口。我用的是I2C地址0x3C和传感器总线分开避免地址冲突和总线负载过重。接线就是VCC、GND、SCL、SDA四根上拉电阻同样不能少。这里有个实操心得OLED的电源最好单独用LDO稳压不要直接和传感器、单片机共用一路电源。OLED在刷新时电流波动较大共用电源会导致传感器供电轻微波动反映在温度读数上就是偶尔跳变0.1到0.2度。分开供电后读数稳定性明显改善。3.3 远程通信接口的硬件设计远程通信我走的是UART转RS485的方案适合HVAC这种工业环境抗干扰能力强传输距离远。PIC24FJ1024GB610的UART1接一颗RS485收发器比如常见的半双工收发芯片DE/RE引脚用GPIO控制收发方向。RS485总线要加终端电阻一般在总线两端各接120Ω。如果节点少、距离短可以不接但长距离传输时终端电阻能明显减少反射和误码。A、B线要双绞最好用屏蔽双绞线屏蔽层单端接地。提示RS485收发器的DE引脚控制时机很关键。发送前拉高DE等发送完成标志置位后再拉低中间要留够最后一个字节的移位时间否则会截断数据。4. 软件架构与任务调度4.1 主循环加定时器中断的调度框架软件架构我采用的是前后台系统定时器中断做时基和标志置位主循环轮询标志执行具体任务。这种架构在PIC24F这种资源下足够用比上RTOS简单调试也直观。具体来说定时器1配置为1ms中断中断里维护几个软件计数器volatile uint16_t tick_1ms 0; volatile uint8_t flag_sample 0; volatile uint8_t flag_display 0; volatile uint8_t flag_upload 0; void __attribute__((interrupt, auto_psv)) _T1Interrupt(void) { tick_1ms; if (tick_1ms % 500 0) { flag_sample 1; // 每500ms采样一次 } if (tick_1ms % 200 0) { flag_display 1; // 每200ms刷新显示 } if (tick_1ms % 1000 0) { flag_upload 1; // 每1s上报一次 } IFS0bits.T1IF 0; }主循环里依次判断标志while (1) { if (flag_sample) { flag_sample 0; sample_all_sensors(); } if (flag_display) { flag_display 0; update_display(); } if (flag_upload) { flag_upload 0; upload_data(); } handle_uart_rx(); }这个框架的好处是任务解耦。采样、显示、上报各自独立某个任务耗时长了不会直接阻塞其他任务只是会延迟到下个周期。采样周期500ms、显示200ms、上报1s这三个周期互不干扰。4.2 多传感器轮询与数据一致性处理挂多颗PJ85718DM的时候轮询顺序和超时处理很重要。我的做法是维护一个传感器地址表依次读取每读一颗都做超时判断和CRC校验如果传感器支持。typedef struct { uint8_t addr; float temp; uint8_t valid; } sensor_t; sensor_t sensors[MAX_SENSORS] { {0x48, 0.0f, 0}, {0x49, 0.0f, 0}, {0x4A, 0.0f, 0}, }; void sample_all_sensors(void) { for (int i 0; i MAX_SENSORS; i) { float t; if (read_temp(sensors[i].addr, t) 0) { sensors[i].temp t; sensors[i].valid 1; } else { sensors[i].valid 0; // 读取失败标记无效 } } }数据一致性方面本地显示和远程上报都从sensors数组读但要注意读取时不要被采样任务打断。简单做法是在采样函数里先关中断更新完再开中断或者用双缓冲采样写A缓冲显示和上报读B缓冲采完切换。我用的是前者因为数据量小关中断时间很短不影响其他任务。4.3 本地显示刷新策略与界面布局OLED显示我设计了三行第一行显示传感器1和2的温度第二行显示传感器3和4的温度第三行显示通信状态和系统运行时间。刷新周期200ms人眼看着是连续的又不会太频繁导致I2C总线占用过高。刷新的时候只更新变化的区域不要整屏重绘。OLED整屏刷新一次要几十毫秒如果每200ms全刷I2C总线有相当一部分时间被显示占用传感器读取可能被挤到后面。我用的是局部刷新温度值变化时才更新对应字符区域。实操心得OLED的I2C地址和传感器地址如果在同一条总线上一定要确认不冲突。我遇到过OLED地址0x3C和某颗传感器地址撞车的情况表现是显示正常但传感器读数全错查了好久才发现是地址冲突。5. 温度采集与校准实操5.1 PJ85718DM的寄存器配置与读取流程PJ85718DM的读取流程典型的是发送地址写方向写配置寄存器设置分辨率、工作模式然后发送地址读方向读温度寄存器。温度值通常是16位高字节和低字节需要拼接和转换。int read_temp(uint8_t addr, float *out) { uint8_t buf[2]; // 写配置12位分辨率连续转换模式 if (i2c_write_reg(addr, CONFIG_REG, 0x60) ! 0) return -1; delay_ms(50); // 等待转换完成 // 读温度寄存器 if (i2c_read_regs(addr, TEMP_REG, buf, 2) ! 0) return -1; int16_t raw (buf[0] 8) | buf[1]; *out raw * 0.0625f; // 根据数据手册的LSB权重转换 return 0; }这里的0.0625是12位分辨率下的LSB权重实际值要查你用的具体型号数据手册。不同分辨率下LSB权重不同9位是0.510位是0.2511位是0.12512位是0.0625。配置和转换要对应上否则读数会差好几度。5.2 温度数据的滤波与校准方法原始读数会有跳动尤其是HVAC环境里电机启停、继电器动作的时候。我加了两级处理滑动平均滤波加限幅滤波。滑动平均用长度为8的窗口每次新采样进来替换最旧的值求平均。这样能平滑随机噪声又不会太滞后。限幅滤波是判断本次采样和上次的差值如果超过阈值比如2度就认为是异常值丢弃不用保留上次有效值。#define FILTER_LEN 8 float filter_buf[FILTER_LEN]; uint8_t filter_idx 0; float filter_temp(float new_val) { filter_buf[filter_idx] new_val; filter_idx (filter_idx 1) % FILTER_LEN; float sum 0; for (int i 0; i FILTER_LEN; i) sum filter_buf[i]; return sum / FILTER_LEN; }校准方面我用的是两点校准法在冰水混合物0度和恒温槽比如40度里各测一次记录原始读数算出偏移和增益写进Flash运行时应用。如果条件有限至少做单点偏移校准用一颗已知精度的温度计做参考把偏移量减掉。5.3 采样周期的计算与设定采样周期怎么定要考虑几个因素传感器转换时间、总线速度、任务负载。PJ85718DM在12位模式下单次转换约50到75ms挂4颗的话轮询一遍要200到300ms。所以采样周期不能短于300ms否则上一轮还没读完下一轮就来了。我定的是500ms留了余量。如果对实时性要求高可以降到400ms但要把I2C速度提到400kHz减少每颗的读取时间。如果对功耗敏感可以延长到1s甚至更长配合传感器的单次转换模式读之前唤醒读完休眠。注意采样周期和上报周期不要设成一样否则每次采样完立刻上报任务容易堆在一起。我设的是采样500ms、上报1s上报时取最近一次采样值这样任务分布均匀。6. 远程通信协议与数据上报6.1 自定义通信帧格式设计远程上报我设计了一个简单的帧格式包含帧头、地址、命令、数据长度、数据区、校验和帧尾。这样接收端能可靠地解析也方便扩展。字段长度说明帧头2字节固定0xAA 0x55设备地址1字节本机地址命令1字节0x01表示温度数据数据长度1字节数据区字节数数据区N字节各传感器温度每路2字节校验1字节从设备地址到数据区的累加和帧尾1字节固定0x0D温度值传输时我把浮点转成整数比如25.3度转成253接收端再除以10。这样避免浮点传输的字节序和精度问题也省带宽。6.2 UART配置与RS485收发控制UART配置成9600或19200波特率8数据位、1停止位、无校验。RS485半双工发送前拉高DE发送完成中断里拉低DE。void rs485_send(uint8_t *buf, uint8_t len) { RS485_DE 1; // 使能发送 for (int i 0; i len; i) { while (U1STAbits.UTXBF); // 等待发送缓冲空 U1TXREG buf[i]; } while (!U1STAbits.TRMT); // 等待最后一位移完 RS485_DE 0; // 切回接收 }关键是最后那个TRMT判断一定要等移位寄存器空再切回接收否则最后一个字节会被截断。我一开始没加这个判断接收端偶尔少一个字节查了半天才发现是DE切换太早。6.3 上报周期与失败重传机制上报周期1s如果发送失败或者没收到应答重传两次间隔200ms。重传两次还不成功就标记通信故障本地显示报警图标等下一个周期再试。接收端应答我用的是简单ACK收到正确帧后回一个0x06。发送端在发送后启动一个超时定时器比如300ms超时没收到ACK就重传。实操心得RS485总线上如果挂了多个节点一定要做总线仲裁或者分时发送避免两个节点同时发送导致数据冲突。我的做法是主机轮询从机只在被问到时才应答平时不主动发。7. 常见问题与排查技巧实录7.1 温度读数异常问题速查现象可能原因排查方法读数固定不变传感器未响应I2C通信失败用示波器看SDA/SCL波形确认地址正确读数跳变严重电源噪声大滤波不足加去耦电容增大滤波窗口读数偏差几度LSB权重配置错误核对数据手册确认分辨率设置多传感器读数串位地址冲突或轮询顺序错逐一单独读取确认每颗地址高温时读数漂移传感器自热或靠近热源降低采样频率远离发热器件7.2 I2C通信失败排查步骤I2C不通我一般按这个顺序查先看电源和地是否正常用万用表量传感器VCC引脚再看上拉电阻是否焊了、阻值对不对然后用示波器看SCL有没有时钟、SDA有没有数据最后确认地址和寄存器配置。有个隐蔽的坑PIC24F的I2C外设在某些情况下会进入死锁表现为SCL被从机拉低不放。这时候需要手动模拟时钟脉冲发9个SCL脉冲让从机释放总线然后重新初始化I2C外设。我在代码里加了超时检测连续多次通信失败就执行总线恢复。7.3 远程通信丢包与误码处理RS485丢包先查硬件终端电阻、双绞线、屏蔽接地。再查软件波特率是否匹配、DE控制时机、校验是否正确。我遇到过一次误码率高的情况最后发现是RS485收发器的电源和单片机电源没有共地导致逻辑电平判断错误。共地之后问题消失。如果环境干扰特别强可以在软件上加多次采样表决同一数据连发三次接收端取多数值。代价是带宽占用增加但对可靠性提升明显。8. 实际部署中的经验与优化建议8.1 低功耗优化思路如果设备是电池供电或者对功耗有要求可以做这些优化传感器用单次转换模式不采样时进入休眠单片机在空闲时进入Idle模式定时器中断唤醒OLED在无人操作时降低亮度或关闭RS485收发器不发送时进入低功耗模式。我实测下来优化前整机电流约30mA优化后能降到5mA以下如果采样周期延长到5s平均电流可以做到1mA以内。8.2 系统稳定性与看门狗配置看门狗一定要开。PIC24FJ1024GB610内置WDT配置成适当超时时间比如2s主循环里定期喂狗。如果程序跑飞或者某个任务卡死看门狗能复位系统避免设备彻底失联。喂狗的位置有讲究不要放在定时器中断里否则主循环卡死但中断还在跑看门狗永远不复位。要放在主循环里确保所有任务都正常执行到才喂狗。8.3 后续功能扩展方向这套框架后续可以扩展的方向不少。比如加SD卡做本地数据记录断网时数据不丢加RTC做时间戳每条温度数据带时间加继电器输出做温度联动控制超过阈值启动风扇或报警远程端可以接云平台做数据可视化和历史曲线。硬件上PIC24FJ1024GB610的资源还有富余加这些功能不用换主控。软件上任务框架已经搭好新增任务就是加个标志位和对应的处理函数扩展起来很顺手。我个人在实际操作中的体会是温度监测这类项目硬件设计占三成软件架构占三成剩下四成是调试和排查。把I2C总线、电源去耦、通信协议这三块做扎实后面基本就是一马平川。最怕的是前期图省事上拉电阻随便选、去耦电容省掉、协议不做校验后面出了问题再回头补花的時間是前期做好的好几倍。
阅读完成 · 觉得有帮助?