1. 项目缘起与整体设计思路
嵌入式温度监测这个方向,看起来简单,实际上手才知道坑有多深。我最早接触这类需求是在一个环境控制类项目里,当时需要同时盯着本地机柜温度和远端管道温度,采样频率要求不高但稳定性要求极高,一旦数据漂移或者通信中断,后端的联动控制就会做出错误判断。后来陆续做了几个类似的小型监测系统,逐渐沉淀出一套比较固定的方案:用一颗带片上ADC的单片机做主控,搭配一颗数字温度传感器负责本地测量,再通过有线或无线链路把远端温度汇总回来。这套思路的核心器件选型,就是标题里提到的 PJ85718DM 和 PIC18F4550。
先说清楚这两个东西分别是什么角色。PIC18F4550 是一颗经典的 8 位单片机,带 USB 接口、多路 10 位 ADC、丰富的定时器和串口资源,在工控和暖通空调(HVAC)领域用了很多年,资料多、生态成熟,属于那种“你不用担心买不到、也不用担心没人踩过坑”的器件。PJ85718DM 则是一颗数字温度传感芯片,通过标准数字总线输出温度值,省去了模拟信号调理和 ADC 校准的麻烦,直接读寄存器就能拿到温度数据。两者搭配,一个负责采集与逻辑,一个负责感知,分工非常清晰。
为什么不用单片机自带的 ADC 接热敏电阻或者模拟温度传感器?这是很多人第一个会问的问题。模拟方案的成本确实可能更低,但代价是:你需要设计分压电路、考虑参考电压漂移、做多点校准、处理长线引入的噪声。而数字传感器把这些问题都在芯片内部解决了,输出的是已经校准好的数字量,单片机只管读就行。对于 HVAC 这种现场环境复杂、电磁干扰不小的场景,数字方案能省掉大量调试时间。我个人的经验是,除非对成本极度敏感且产量极大,否则数字温度传感器是更稳妥的选择。
整个系统的设计目标可以归纳成三条:第一,本地温度要实时、准确地采集,采样周期控制在秒级;第二,远端温度要通过通信链路汇总到主控,链路可以是 RS-485、UART 或者无线模块;第三,主控要把两路数据做统一处理,可以本地显示,也可以上报给上位机。这三条决定了硬件架构和软件状态机的设计方式。下面我会把每个环节拆开讲,包括选型理由、电路要点、通信协议设计、代码结构,以及我在实际调试中踩过的坑。
2. 核心器件解析与硬件设计要点
2.1 PIC18F4550 在温度监测中的角色定位
PIC18F4550 最大的特点是资源均衡。它有 32KB 闪存、2KB RAM,对于温度监测这种逻辑不复杂的应用绰绰有余;它有多路 10 位 ADC,虽然本项目主要用数字传感器,但保留 ADC 通道可以方便后续扩展(比如加一路模拟压力传感器);它有多个定时器,可以用来做采样周期定时和通信超时判断;它还有 EUSART 模块,直接支持异步串口通信,接 RS-485 收发器或者无线透传模块都很方便。
我选择它而不是更小的 8 位芯片,主要考虑是余量。温度监测本身不重,但一旦加上显示、按键、报警逻辑、通信协议解析,代码量会迅速膨胀。用资源紧巴巴的芯片,后期加功能时会被迫做大量优化,反而拖慢进度。PIC18F4550 的闪存和 RAM 足够你从容地写状态机、做缓冲队列、留调试接口。另外它的工作电压范围宽,工业级温度范围也够用,放在 HVAC 控制柜里不会有问题。
有一点需要提醒:PIC18F4550 的 ADC 参考电压可以选择内部或外部,如果后续真的要用模拟通道,建议用外部基准芯片,不要依赖电源电压做参考,否则电源波动会直接反映到采样值上。这个坑我在早期项目里踩过,当时用 VDD 做参考,结果电机一启动温度读数就跳,后来换成专用基准才稳定下来。
2.2 PJ85718DM 数字温度传感器的接口与特性
PJ85718DM 这类数字温度传感器的典型特征是:出厂校准、数字输出、接口简单。它通常通过两线或三线数字总线与主控通信,主控发送读取命令后,传感器返回温度寄存器值,再按数据手册给出的换算公式转成摄氏度。相比模拟传感器,它的优势在于抗干扰能力强——数字信号在传输过程中只要电平判读正确,就不会像模拟信号那样被噪声“污染”成错误的数值。
接线方面,一般需要电源、地、数据线,有的型号还需要时钟线。电源端一定要加去耦电容,而且要尽量靠近传感器引脚,这是数字器件稳定工作的基本要求。数据线如果走线较长,建议加串联电阻或者用屏蔽线,减少反射和串扰。我在一个 HVAC 项目里把传感器装在风管附近,数据线走了将近一米,最初没加任何保护,读数偶尔会跳变,后来在数据线靠近主控端加了一个小电阻和滤波电容,问题就消失了。
换算公式这块要特别注意。不同型号的温度传感器,寄存器格式不一样,有的是 12 位补码,有的是 16 位定点,分辨率可能是 0.0625 度也可能是 0.1 度。拿到芯片后第一件事就是翻数据手册确认格式,不要凭经验猜。我见过有人直接把两个字节拼起来当整数用,结果负温度全部读错,排查了半天才发现是补码没处理。
2.3 本地与远程温度的硬件架构差异
本地温度采集很直接:传感器和主控在同一块板子上或者很近,走线短,干扰小,直接读就行。远程温度则复杂得多,核心问题不是“怎么测”,而是“怎么把测到的数据可靠地传回来”。常见方案有三种:一是远端也放一颗单片机,本地采集后通过 RS-485 或无线模块发送;二是远端只放传感器,用长线直接连回主控;三是远端用带总线接口的传感器,挂在同一条总线上。
第一种方案最稳,因为远端单片机可以先做本地处理和校验,传输的是已经打包好的数据,抗干扰能力最强。第二种方案成本最低,但长线会引入压降和噪声,距离一长就不靠谱。第三种方案介于两者之间,适合多个测点分布在一条总线上的场景。我在实际项目中倾向于第一种,虽然多了一颗单片机,但换来的可靠性和可扩展性是值得的。如果远端测点很多,还可以让远端单片机带多个传感器,进一步摊薄成本。
供电也是远程方案必须考虑的问题。远端如果取电不方便,就要考虑低功耗设计或者电池供电,这时候传感器的功耗、单片机的休眠策略、通信模块的发射功率都会影响续航。HVAC 场景通常有市电可用,但如果是改造项目,取电点可能很远,这时候就要提前规划。
3. 通信协议与数据链路设计
3.1 本地采集与远端汇总的数据流
整个系统的数据流可以这样理解:本地传感器被主控周期性读取,得到本地温度;远端节点也周期性读取自己的传感器,然后通过通信链路把数据发给主控;主控收到后做校验、解析、合并,形成一份完整的温度快照。这份快照可以送到显示屏、可以触发报警、也可以通过另一个接口上报给上位机。
关键在于“周期性”和“校验”。周期性保证数据新鲜,校验保证数据可信。我通常会把采样周期设成 1 秒,通信周期设成 2 到 5 秒,因为温度变化本身很慢,没必要高频通信,反而增加出错概率。校验方面,最简单的做法是加校验和,稍好一点用 CRC,如果链路质量差,还可以加序列号判断是否丢包。
数据流的设计还要考虑异常情况:远端节点掉线了怎么办?数据校验失败了怎么办?主控不能因为一个远端节点失联就整个系统卡死。我的做法是给每个远端节点设一个超时计数器,超过一定时间没收到有效数据就标记为“失联”,用上一次的有效值或者一个明确的无效标志代替,同时触发告警。这样系统不会因为单点故障而崩溃。
3.2 串口通信参数与帧格式设计
串口通信是这类项目最常用的链路。PIC18F4550 的 EUSART 配置起来很直接,关键是参数要匹配:波特率、数据位、停止位、校验位。工业场景常用 9600 或 19200 波特率,8 数据位、1 停止位、无校验。波特率不要盲目求高,线缆质量一般、距离稍长的时候,高波特率反而容易出错。我一般先用 9600 跑通,确认稳定后再考虑提速。
帧格式我习惯这样设计:帧头 + 地址 + 命令 + 数据长度 + 数据 + 校验 + 帧尾。帧头用两个固定字节,比如 0xAA 0x55,方便接收方做同步;地址用于区分不同远端节点;命令区分是读温度还是配置参数;数据长度让接收方知道要收多少字节;校验用 CRC8 或 CRC16;帧尾可以再加一个固定字节做二次确认。这个格式看起来啰嗦,但实际用起来非常省心,尤其是多个节点挂在同一总线上的时候。
接收端的处理要用状态机,不能用一个简单的“收完一帧再处理”的阻塞逻辑。因为串口数据是异步到达的,你不知道下一字节什么时候来,阻塞等待会浪费大量 CPU 时间,还可能错过其他事件。状态机的方式是:每收到一个字节进一次中断,根据当前状态决定这个字节是帧头、是数据还是校验,收满一帧后置一个标志,主循环再处理。这样主循环可以同时做采样、显示、按键等其他事情。
3.3 抗干扰与数据校验的实操经验
工业现场的干扰来源很多:电机启停、继电器动作、变频器工作,都会在通信线上感应出尖峰。我遇到过最典型的情况是:平时通信正常,一旦空调压缩机启动,连续几帧数据出错。解决办法有几个层次:硬件上,通信线用双绞线,加终端电阻,必要时加隔离;软件上,加校验、加重传、加超时;协议上,加序列号,接收方发现序列号跳变就知道丢帧了。
校验方式的选择也有讲究。校验和计算简单,但检错能力有限,两个字节同时出错可能校验和还是对的。CRC 计算稍复杂,但检错能力强得多,尤其是对突发错误。我现在的习惯是:只要链路不是板内短距离,一律用 CRC。CRC8 够用就用 CRC8,数据量大或者要求高就用 CRC16。计算 CRC 可以用查表法,速度快,占用空间也不大。
还有一个容易被忽略的点:通信双方的字节序要一致。温度值通常是 16 位,高字节在前还是低字节在前,发送方和接收方必须约定好。我见过因为字节序不一致导致温度读出几百度的案例,排查时先怀疑传感器,再怀疑电路,最后才发现是协议解析反了。所以协议文档一定要写清楚,代码里也要有注释。
4. 固件实现与关键代码解析
4.1 主循环与定时采样任务调度
固件的主循环我一般写成“时间片轮询”结构,不用 RTOS,因为任务少,RTOS 反而增加复杂度和资源开销。具体做法是:用定时器产生一个 1ms 或 10ms 的基准节拍,在中断里给各个任务的计数器减一,主循环里检查计数器是否到零,到零就执行对应任务并重置计数器。这样采样、通信、显示、按键扫描都能按各自的周期运行,互不阻塞。
温度采样任务设成 1 秒一次,通信任务设成 2 秒一次,显示刷新设成 500ms 一次,按键扫描设成 20ms 一次。这些周期不是随便定的:温度变化慢,1 秒足够;通信太频繁没必要,2 秒平衡了实时性和链路负载;显示刷新太快人眼也看不出,500ms 刚好;按键扫描要快一点,否则手感差。每个任务的周期都可以根据实际需求调整,关键是不要把所有事情都塞进主循环顺序执行,那样任何一个环节卡住都会影响全局。
定时器中断里只做计数,不做实际业务逻辑,这是原则。中断里做太多事情会导致中断响应变慢,甚至丢失其他中断。业务逻辑全部放到主循环,中断只负责“打个标记”。这个习惯在资源紧张的 8 位单片机上尤其重要。
4.2 温度读取与换算的代码实现
读取数字温度传感器的流程一般是:发送读取命令、等待转换完成(有的传感器需要)、读取寄存器、换算成温度值。以常见的 12 位分辨率传感器为例,寄存器返回 16 位数据,高 12 位有效,低 4 位可能是标志位或者保留位。换算时先取出高 12 位,判断符号位,如果是负温度,要做补码转换,然后乘以分辨率(比如 0.0625)得到摄氏度。
代码里我会把换算封装成一个函数,输入原始寄存器值,输出浮点温度。这样上层逻辑不用关心底层格式,换传感器型号时只改这一个函数。浮点运算在 8 位单片机上比较慢,如果对速度有要求,可以用定点数代替,比如把温度放大 100 倍存成整数,显示时再插入小数点。我一般先用浮点跑通,确认功能后再根据性能决定是否优化。
下面是一段示意性的换算逻辑,实际使用时需要根据具体传感器的数据手册调整:
float convert_temp(uint16_t raw) { int16_t temp_raw; float result; /* 取高12位,假设低4位为标志位 */ temp_raw = (int16_t)(raw >> 4); /* 判断符号位,12位补码转换 */ if (temp_raw & 0x0800) { temp_raw |= 0xF000; } result = temp_raw * 0.0625f; return result; }这段代码的关键在于补码处理。12 位有符号数的范围是 -2048 到 2047,符号位是第 11 位。如果符号位为 1,说明是负数,需要把高 4 位补 1 变成 16 位补码,这样后续运算才能得到正确的负值。这个细节如果漏掉,负温度会读成很大的正数。
4.3 通信收发状态机的编写要点
串口接收状态机我通常用枚举定义状态:等待帧头1、等待帧头2、接收地址、接收命令、接收长度、接收数据、接收校验、等待帧尾。每收到一个字节,根据当前状态决定下一步。收到完整帧后,校验通过就置标志,校验失败就丢弃并重置状态机。
发送相对简单,把数据按帧格式打包好,逐个字节写入发送寄存器,等待发送完成标志即可。但要注意发送和接收不能互相干扰,如果用的是同一个串口,发送时接收中断仍然会触发,状态机要能正确处理。我一般会在发送前先关接收中断,发完再开,或者用双缓冲区分开发送和接收。
超时处理是状态机必须考虑的。如果帧头收到一半,后面的字节迟迟不来,状态机不能永远等下去。我的做法是给状态机加一个超时计数器,每收到一个字节重置计数器,主循环里检查计数器,超时就把状态机复位到等待帧头。超时时间根据波特率和帧长度估算,一般设成帧传输时间的三到五倍。
5. 常见问题排查与避坑经验
5.1 温度读数异常的分类排查
温度读数异常是最常见的问题,表现有几种:读数恒定不变、读数跳变剧烈、读数明显偏离实际、负温度读成正温度。排查时我习惯按“先软后硬、先近后远”的顺序。
读数恒定不变,先检查传感器是否真的在通信。用示波器或者逻辑分析仪看数据线上有没有波形,如果没有,可能是接线问题、供电问题或者传感器损坏。如果有波形但读数不变,可能是读取命令不对,或者读的寄存器地址不对。
读数跳变剧烈,通常是干扰或者接触不良。检查电源去耦、数据线屏蔽、接地是否良好。如果跳变只发生在特定设备启动时,基本可以确定是干扰,需要从硬件滤波和软件校验两方面入手。
读数明显偏离实际,先确认换算公式和分辨率是否正确。有的传感器默认分辨率是 9 位,需要配置成 12 位才能得到高精度,如果没配置,读数会偏大。还有的传感器上电后第一次转换结果不准,需要丢弃第一次读数。
负温度读成正温度,几乎可以肯定是补码处理有问题。回去检查符号位判断和高位扩展逻辑,这是最典型的坑。
5.2 通信丢包与误码的定位方法
通信问题定位,第一步是确认物理层。用示波器看波形质量,上升沿下降沿是否陡峭,电平幅度是否足够,有没有明显的振铃或毛刺。如果波形本身不好,软件再怎么改也没用,先解决硬件问题。
物理层没问题后,看协议层。统计丢包率和误码率,如果丢包率很低但偶尔出错,可能是校验不够强或者干扰偶发;如果丢包率很高,可能是波特率不匹配、帧格式不一致或者接收状态机有 bug。
我常用的一个技巧是在通信数据里加一个递增的序列号,接收方记录上一次的序列号,如果发现跳变就知道中间丢帧了。这样可以区分“完全没收到”和“收到了但校验失败”两种情况,对定位问题很有帮助。
还有一个容易忽略的点:发送方的发送间隔。如果发送太快,接收方还没处理完上一帧,下一帧就来了,会导致缓冲区溢出或者状态机混乱。加一个最小发送间隔,或者用应答机制,可以避免这个问题。
5.3 常见问题速查表
| 现象 | 可能原因 | 排查方向 | 解决建议 |
|---|---|---|---|
| 温度读数恒定 | 传感器未通信、命令错误 | 查波形、查命令 | 检查接线和读取命令 |
| 温度跳变剧烈 | 干扰、接触不良 | 查电源、查接地 | 加去耦、加滤波、用屏蔽线 |
| 负温度读成正 | 补码处理错误 | 查符号位逻辑 | 修正高位扩展代码 |
| 通信偶发误码 | 干扰、校验不足 | 查波形、查校验 | 加 CRC、加重传、加隔离 |
| 通信完全不通 | 波特率、接线错误 | 查参数、查线序 | 核对双方配置 |
| 远端节点失联 | 超时、掉电、链路断 | 查超时逻辑、查供电 | 加超时标记和告警 |
| 读数偏大 | 分辨率未配置 | 查配置寄存器 | 按手册配置分辨率 |
这张表是我这些年遇到问题后慢慢总结的,实际排查时不一定完全按顺序来,但至少能提供一个方向,避免盲目乱试。
6. 系统扩展与场景适配建议
6.1 多测点组网与地址分配
当测点从一个变成多个,组网方式就要提前规划。最简单的是一主多从,主控轮询各个从节点,每个从节点有唯一地址。地址分配可以硬件跳线、可以软件配置、也可以用拨码开关。硬件跳线简单但改起来麻烦,软件配置灵活但需要额外的配置接口,拨码开关折中,适合现场调整。
轮询策略上,我一般用顺序轮询,主控依次向每个地址发请求,收到应答后处理,超时就跳过。轮询周期根据节点数量和通信速率计算,节点多的时候周期会变长,如果实时性要求高,就要提高波特率或者减少节点数。还有一种方式是让从节点主动上报,但需要处理冲突,实现起来更复杂,除非有现成的总线协议支持,否则不建议自己造。
6.2 显示、报警与上位机对接
本地显示可以用段码屏、字符屏或者小尺寸图形屏,看信息量需求。温度监测一般显示当前值、最高值、最低值、报警状态就够了,字符屏足够。报警可以用蜂鸣器、LED 或者继电器输出,触发条件可以设上限、下限、变化率。上位机对接通常用串口转其他接口,把数据打包成约定格式发出去,上位机负责存储和展示。
这里有个经验:报警逻辑一定要有回差。比如上限设 30 度,如果一到 30 度就报警,温度在 30 度附近波动时会反复触发。加一个回差,比如超过 30 度报警,低于 29 度才解除,这样就不会频繁跳变。回差大小根据实际场景定,一般 0.5 到 1 度比较合适。
6.3 低功耗与长期运行稳定性考量
如果远端节点需要电池供电,低功耗设计就很重要。策略包括:降低采样频率、让单片机在两次采样之间休眠、关闭不用的外设、降低通信发射功率。休眠唤醒可以用定时器或者外部中断,唤醒后快速完成采样和通信,然后继续休眠。平均电流可以做到微安级,续航从几个月到几年不等,取决于电池容量和占空比。
长期运行稳定性方面,看门狗是必须开的。程序跑飞时看门狗能复位系统,避免死机。另外要定期检查传感器和通信链路的状态,发现异常及时告警。我还会在固件里加一个运行计数器,记录系统运行时间和复位次数,方便判断系统是否稳定。如果复位次数异常增加,说明有问题需要排查。
温度监测这个方向,技术门槛不高,但要做好做稳,细节非常多。从器件选型到电路设计,从协议制定到代码实现,从单点调试到组网运行,每个环节都有坑。我个人的体会是,不要追求一步到位,先把单点跑通,再逐步加功能、加节点、加场景。每加一个东西就充分测试,确认稳定后再继续。这样虽然看起来慢,但总体返工少,最终交付的质量也更有保障。