基于PIC18F67K40与PJ85718DM的双路温度监测系统设计与实现

发布时间:2026/10/11 1:19:59
基于PIC18F67K40与PJ85718DM的双路温度监测系统设计与实现 1. 项目缘起与整体设计思路嵌入式温度监测这件事说起来简单做起来坑不少。我最早接触这类需求是在一个HVAC控制板的项目里当时的要求很朴素本地要能看到机房回风温度远程中控室也要能读到同一路数据而且两边的读数不能打架。最开始想用一颗带Wi-Fi的SoC一把梭后来发现现场电磁环境复杂无线方案在金属风管旁边丢包丢到怀疑人生于是老老实实回到本地传感器MCU有线远程的经典组合。这套方案的核心就是标题里提到的两颗器件PJ85718DM负责把温度变成数字量PIC18F67K40负责采集、处理、显示和转发。先把这个项目的定位说清楚。它解决的是双路温度监测问题一路是贴近热源的本地温度用于面板显示和本地联锁另一路是远程温度通过通信总线送到上位机或中控PLC。适合谁来参考如果你正在做暖通空调控制、冷库监控、机房环境采集、工业烤箱温控这类项目并且希望用一颗主控同时搞定本地显示和远程上报那这套思路可以直接抄。哪怕你是刚上手PIC18系列的新手只要跟着把I2C时序和UART协议调通剩下的就是体力活。为什么选PIC18F67K40而不是更便宜的PIC16这里有个选型逻辑。PIC18F67K40是8位机里比较富的一颗128KB Flash、3.5KB RAM、自带LCD驱动模块、多个UART和I2C/SPI外设还有充足的可编程I/O。HVAC场景往往需要驱动段码LCD做本地显示如果外挂一颗LCD驱动芯片BOM成本和PCB面积都上去了。K40自带LCD控制器直接省掉一颗芯片这是它在这个场景里最大的价值点。另外它的工作温度范围覆盖工业级风管附近温度波动大商用级芯片容易在冬天启动时出问题。再说PJ85718DM。这是一颗数字温度传感器I2C接口出厂校准不需要外部调理电路。相比热敏电阻加运放的方案它的优势在于第一数字输出免去了ADC标定和温漂补偿第二I2C总线可以挂多颗未来要扩展多点测温只需改地址第三精度在常温区间足够HVAC使用。热敏电阻方案虽然便宜但每颗都要单独标定量产时的一致性管理很痛苦。我踩过的坑是早期用NTC加分压结果一批板子因为电阻公差导致读数偏差两三度返工标定花了两周。换成数字传感器后这个问题直接消失。整体架构可以这样理解PJ85718DM通过I2C挂在PIC18F67K40的MSSP模块上MCU周期性读取温度寄存器做滑动平均滤波一路送LCD显示一路通过UART打包成协议帧发给远程主机。远程主机可以是另一颗MCU、工控机或者带串口的触摸屏。如果传输距离超过几米UART要转成RS-485差分信号这个后面会细说。整个数据流是采集—滤波—显示—上报四步每一步都有讲究下面逐个拆。2. 核心器件解析与硬件连接要点2.1 PJ85718DM的寄存器与读数机制PJ85718DM这类数字温度传感器的核心就是几个寄存器温度值寄存器、配置寄存器、地址寄存器。温度值通常是16位高字节整数部分低字节小数部分分辨率常见0.0625度或0.125度。读的时候要注意必须一次性把两个字节读完如果只读高字节内部指针可能停在错误位置下次读就乱了。我见过有人分两次读结果温度每隔一次就跳变查了半天以为是传感器坏了其实是读时序的问题。I2C地址方面这类传感器一般有7位地址其中几位可以通过ADDR引脚拉高拉低来配置。这意味着同一条总线上最多能挂8颗同型号传感器做多点测温时非常方便。但要注意地址引脚不能悬空必须明确接VCC或GND悬空会导致地址不确定总线扫描时可能时有时无。我在一块样板上就吃过这个亏ADDR脚忘了处理结果上电后有时能读到有时读不到用逻辑分析仪抓波形才发现地址在飘。上拉电阻的选择也有讲究。I2C总线的SDA和SCL必须接上拉典型值4.7kΩ。如果总线电容大走线长、挂的器件多上拉要减小到2.2kΩ甚至1kΩ否则上升沿太缓高速模式下会误判。但上拉太小又会导致低电平灌电流过大超过器件的驱动能力。经验公式是上拉电阻最小值 (VCC - VOL) / IOL其中IOL是器件低电平灌电流能力一般3mA左右。3.3V系统下最小约1kΩ。实际选型时我一般先用4.7kΩ如果波形上升沿超过1微秒再往下调。2.2 PIC18F67K40的MSSP与LCD模块配置PIC18F67K40的MSSP模块可以配置成I2C主模式。配置步骤大致是设置SSPCON1的SSPM位选择I2C主模式设置SSPADD寄存器写入波特率然后使能MSSP。波特率计算公式是SSPADD (Fosc / (4 * 目标波特率)) - 1。假设系统时钟16MHz想要100kHz的I2C速率SSPADD (16M / (4 * 100k)) - 1 39。这个值要写对写错了通信直接不通而且不会报错只是波形频率不对新手很容易在这里卡住。LCD模块是K40的亮点。它支持最多8个公共端和多个段能直接驱动段码屏。配置时要设置LCDCON寄存器选择偏压和驱动模式还要配置LCDPS分频。段码屏的对比度靠VLCD引脚上的电阻分压网络调节这个电阻值要根据屏的规格来选选大了对比度不足选小了功耗高。我一般先用可调电阻调出合适对比度再量出阻值换成固定电阻。这里有个实操细节LCD模块和I2C模块共用一些引脚资源分配引脚时要查数据手册的引脚复用表别把LCD段和I2C的SDA/SCL分到同一个脚上。我见过一个设计PCB画完了才发现冲突只能飞线解决非常难看。建议在原理图阶段就把引脚分配表列出来逐个核对。2.3 电源与去耦的实战经验数字温度传感器对电源噪声比较敏感尤其是HVAC板子上往往有继电器、风机驱动这些大电流负载。PJ85718DM的VCC脚旁边必须放0.1μF陶瓷电容而且要尽量靠近引脚。如果板子上有电机或继电器建议再并一个10μF钽电容做低频滤波。我遇到过温度读数偶尔跳变0.5度的情况查了很久最后发现是继电器吸合瞬间电源跌落导致的加了电容后问题消失。地线处理也不能马虎。模拟地和数字地如果混在一起大电流回流会在小信号地上产生压降影响传感器。虽然数字传感器比模拟传感器抗干扰强但电源和地的干净程度仍然直接决定读数稳定性。我的做法是传感器和MCU的地尽量单点汇聚到电源入口大电流负载的地单独走最后在电源处汇合。3. 固件实现从采集到上报的完整链路3.1 I2C读取温度的标准流程先给一段可参考的读取流程用C语言描述基于PIC18的MSSP库函数风格。实际写的时候起始、停止、发送字节、接收字节这些底层函数要先封装好。#define SENSOR_ADDR_W 0x90 // 写地址 #define SENSOR_ADDR_R 0x91 // 读地址 #define TEMP_REG 0x00 // 温度寄存器 int16_t read_temperature(void) { uint8_t msb, lsb; int16_t raw; I2C_Start(); I2C_Write(SENSOR_ADDR_W); I2C_Write(TEMP_REG); I2C_Start(); // 重复起始 I2C_Write(SENSOR_ADDR_R); msb I2C_Read_ACK(); lsb I2C_Read_NACK(); I2C_Stop(); raw ((int16_t)msb 8) | lsb; raw 4; // 根据分辨率右移 return raw; }这段代码的关键点重复起始条件不能省否则读出来的可能是别的寄存器。读最后一个字节要发NACK告诉传感器读完了如果发ACK传感器会继续输出下一个寄存器的数据。右移位数取决于传感器分辨率0.0625度对应右移4位0.125度对应右移3位这个要查手册确认移错了温度就整体偏了。温度值转换成实际摄氏度如果raw是带符号的12位值乘以0.0625即可。负温度的处理要注意传感器一般用补码表示直接按有符号数处理就行。我见过有人把负温度读成很大的正数就是因为没做符号扩展。3.2 滑动平均滤波的参数选择原始读数会有±0.25度左右的抖动直接显示会跳得眼花。滑动平均是最简单有效的办法。窗口大小怎么选太小滤波效果差太大响应慢。HVAC场景温度变化缓慢我一般用8点或16点窗口。8点窗口的响应延迟大约是采样周期的8倍如果1秒采一次延迟8秒对空调控制来说完全可以接受。实现上用一个环形缓冲区每次新数据进来替换最老的数据然后求平均。注意求和变量要用32位8个12位数据加起来可能超过16位范围。这个细节不注意温度会在某个点突然跳变因为16位溢出了。#define FILTER_SIZE 8 int32_t temp_buffer[FILTER_SIZE]; uint8_t buf_index 0; int16_t filter_temperature(int16_t new_val) { int32_t sum 0; temp_buffer[buf_index] new_val; buf_index (buf_index 1) % FILTER_SIZE; for (uint8_t i 0; i FILTER_SIZE; i) { sum temp_buffer[i]; } return (int16_t)(sum / FILTER_SIZE); }除了滑动平均还可以加一个限幅滤波如果新值和上次值差异超过2度认为是干扰直接丢弃。这个在继电器频繁动作的板子上特别有用。3.3 本地LCD显示与远程UART协议设计本地显示部分把滤波后的温度值拆成整数和小数查段码表送到LCD数据寄存器。刷新率不用太高2Hz足够刷太快反而增加功耗和EMI。显示格式建议固定宽度比如25.5C避免数字位数变化导致显示跳动。远程上报的协议设计是重点。我推荐用简单的帧格式帧头地址命令数据长度数据校验帧尾。比如字段字节数说明帧头20xAA 0x55设备地址1本机地址命令10x01表示温度上报数据长度1后续数据字节数数据N温度值高字节在前校验1前面所有字节异或帧尾10x0D校验用异或最简单虽然强度不高但对短帧足够。如果环境干扰大可以换成CRC8。帧头用两个字节是为了降低误同步概率单字节帧头容易在数据里出现相同值导致错位。UART波特率选9600还是115200如果走RS-485长距离9600更稳115200在几十米线上容易误码。上报周期1秒一次9600波特率完全够用一帧也就十几个字节。别盲目追求高速稳定压倒一切。3.4 RS-485远传的硬件与软件配合UART本身是单端信号传输距离有限几米以内还行超过十米就要转差分。RS-485用一对双绞线传差分信号抗共模干扰能力强几百米没问题。硬件上需要一颗收发器芯片比如常见的半双工收发器RO接MCU的RXDI接TXRE和DE接一个GPIO控制方向。方向控制的时序是关键。发送前把DE拉高等发送完成中断触发后确认最后一个字节的停止位已经移出再把DE拉低。如果拉低太早最后一个字节会被截断拉低太晚总线占用时间过长影响其他节点。我的做法是在TX完成中断里加一个小延时或者查TCSTIF标志确认发送移位寄存器空。总线上要接终端电阻一般120Ω接在总线两端。中间节点不接。如果距离短、节点少不接也能凑合但长距离一定要接否则信号反射会导致误码。这个电阻在PCB上要预留位置调试时根据实际情况决定焊不焊。4. 常见问题排查与避坑实录4.1 温度读数异常的问题速查现象可能原因排查方法解决读数固定不变I2C未通信成功逻辑分析仪抓波形检查地址、上拉、时序读数跳变±0.5度电源噪声示波器看VCC纹波加去耦电容负温度显示为大正数符号扩展错误检查数据类型用int16_t并正确移位每隔一次读数错误寄存器指针错位检查读时序确保重复起始和NACK远程数据偶尔错帧校验不足或干扰统计误码率加CRC、降低波特率LCD显示闪烁刷新率过高或电源不稳降低刷新率加滤波电容这张表是我这几年攒下来的基本覆盖了八成以上的现场问题。遇到问题先对照查能省很多时间。4.2 I2C通信失败的排查思路I2C不通是最常见的问题。排查顺序建议这样先用万用表量SDA和SCL的静态电平正常应该是高电平被上拉电阻拉高。如果量出来是低说明总线被某个器件拉死了可能是器件没上电或者损坏。然后量上拉电阻两端电压确认电阻没虚焊。接下来用逻辑分析仪看波形。重点看起始条件是否正常地址字节是否和预期一致从机有没有发ACK。如果从机没ACK说明地址不对或者器件没工作。地址不对的情况很常见有人把7位地址和8位地址搞混写地址是7位左移一位读地址再或1。这个一定要查手册确认。如果波形正常但读不到数据检查寄存器指针。有些传感器上电后默认指针不在温度寄存器需要先写一次寄存器地址。还有人忘了在读写之间加延时传感器转换需要时间读太快拿到的是旧数据或无效数据。4.3 长距离通信的稳定性优化RS-485长距离通信出问题八成是接地和终端电阻。总线两端的地电位如果差太多共模电压超过收发器范围就会误码。解决办法是用隔离收发器或者确保两端共地。共地线要和双绞线一起走别只走信号线。终端电阻只在总线最远两端接中间节点接了反而增加负载。如果总线分支太多每个分支都会产生反射建议用菊花链拓扑避免星型连接。我见过一个现场八个节点星型接法通信时好时坏改成手拉手后立刻稳定。波特率和线长的关系也要注意。9600波特率下普通双绞线可以跑1200米115200波特率下建议不超过100米。如果必须高速长距离用低电容专用电缆或者降低波特率。别跟物理规律较劲。4.4 实操心得与独家技巧分享几个文档里不会写但很实用的点。第一调试I2C时先写一个最简单的扫描程序把总线上所有能响应的地址打印出来确认传感器地址。这个程序十分钟能写好能省几小时排查时间。第二温度传感器尽量远离发热元件比如LDO、功率电阻否则测的是板子温度不是环境温度。我见过传感器放在稳压器旁边读数比实际高5度。第三量产时给每块板子留一个校准接口虽然数字传感器一致性好但PCB布局差异仍可能导致零点偏移留个后门方便后期修正。还有一点关于看门狗。HVAC设备往往无人值守程序跑飞了必须能自恢复。PIC18F67K40自带WDT使能后定期喂狗。但要注意I2C读取如果卡死喂狗循环可能也卡住。我的做法是把I2C操作加上超时计数超时就复位MSSP模块重新初始化而不是死等。这个技巧救过我好几次现场。5. 系统联调与扩展思路5.1 本地与远程数据一致性验证联调阶段最怕本地显示和远程读数对不上。验证方法很简单用一个标准温度源比如恒温水浴或校准过的温度计同时对比。如果偏差在0.5度以内说明链路正常。如果远程比本地低可能是上报的数据没经过滤波或者协议解析时字节序搞反了。字节序是个高频坑。MCU是小端但协议里我建议统一用大端高字节在前这样跨平台解析不容易错。发送时手动拆字节接收时手动拼别用指针强转否则换平台就出问题。数据一致性还要考虑时间戳。本地显示是实时的远程上报有周期延迟如果上位机做趋势图要记录接收时间而不是采集时间。如果对时间敏感可以在协议里加一个采集时刻的字段用MCU的定时器计数填充。5.2 多点测温的扩展方法单点玩通了扩展多点就是改地址的事。PJ85718DM这类传感器通过ADDR引脚可以配出多个地址一条I2C总线挂8颗覆盖8个测点。固件里把读取函数参数化传入不同地址循环读取即可。注意总线电容会随器件数量增加上拉电阻要相应减小或者降低I2C速率。如果测点超过8个可以用I2C多路复用器扩展总线或者换用SPI接口的传感器。SPI没有地址概念靠片选区分理论上可以挂很多但引脚占用多。选哪种取决于你的MCU引脚余量和布线难度。多点数据的远程上报建议在协议里加测点编号上位机按编号分通道显示。别把所有温度拼成一帧发那样解析麻烦也不利于单点故障隔离。5.3 低功耗场景的优化如果设备是电池供电功耗就要精打细算。PIC18F67K40有休眠模式I2C传感器也有单次转换模式测完就睡。策略是定时器唤醒MCU读一次温度上报然后全部休眠。休眠电流可以做到微安级电池寿命从几天延长到几个月。LCD背光是耗电大户如果不需要常亮可以做成按键唤醒。传感器在休眠期间功耗极低但要注意上电后的第一次转换需要等待时间别刚唤醒就读读到的是无效值。手册里一般会写转换时间按那个时间延时后再读。低功耗和通信实时性是一对矛盾。休眠期间收不到远程命令如果上位机需要随时查询就不能深度休眠。折中方案是周期性唤醒监听比如每秒醒一次醒着的时间尽量短。这个平衡点要根据实际需求调。5.4 电磁兼容的现场处理HVAC现场电磁环境恶劣继电器、变频器、接触器都是干扰源。除了前面说的电源滤波和地线处理还可以在通信线上加共模电感在传感器电源上串磁珠。PCB布局时I2C走线尽量短远离高频开关节点必要时包地处理。软件上可以加数字滤波和重试机制。读失败就重试三次三次都失败就上报故障而不是错误数据。这个逻辑很重要宁可报故障也不能报错误温度否则控制系统可能做出错误决策。我在一个冷库项目里就设置了温度合理性检查超出-40到80度范围直接标记为传感器故障避免误动作。外壳屏蔽也有帮助。如果设备装在金属箱里箱体接地能挡掉大部分空间干扰。但要注意金属箱会屏蔽无线信号如果设备有无线模块天线要引到箱外。这个项目用有线通信金属箱反而是优势。6. 写在最后的几句实在话这套方案我从样机到小批量跑了不下五个版本最大的体会是数字传感器虽然省心但省心不等于不用心。I2C的时序、电源的干净、协议的健壮性每一样都得抠。新手最容易犯的错是急着写应用层底层通信没调稳就往上堆功能结果问题一层套一层排查起来痛苦不堪。我的建议是分步验证先让I2C能稳定读到温度再加滤波再加显示再加通信每一步都确认稳定了再往下走。另外别迷信手册上的典型电路。手册给的是理想情况实际板子上的寄生参数、器件公差、环境干扰都会让结果偏离。留足调试余量该加的电容电阻都加上PCB上多留几个测试点后期排查会感谢自己。温度监测这个事精度重要但稳定性更重要。一个偶尔跳变的精确读数不如一个始终如一的稳定读数。