ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单片机温度检测报警系统设计:DS18B20滤波、迟滞与串口上报

单片机温度检测报警系统设计:DS18B20滤波、迟滞与串口上报 简介以DS18B20智能温度传感器与AT89C2051单片机为核心的温度检测报警系统设计文档面向电子信息、自动化与嵌入式方向的课程设计、毕业设计及单片机入门实践者用于解决多点测温、阈值报警与远程数据传输的方案落地问题。全包仅含1个docx文件约15KB篇幅紧凑内容涵盖系统硬件电路构成、基于单总线的系统工作原理、DS18B20性能特点与测温原理解析、AT89C2051原理与特点说明以及RS-485串口通信与上位机监控的设计思路。文中给出的报警限度设置、声光报警与多点测温实现思路均可直接借鉴。目前已有48人学习适合需要撰写相关论文、搭建实验样机或对照器件手册查漏补缺的读者可作为低成本温度测控方案的技术参考。1. 从测温到报警这套系统的难点不在测很多人接到基于单片机高级温度检测报警系统设计这个题目第一反应是选个 DS18B20读回来跟阈值比一下超了就响蜂鸣器半天就能交差。可真正装到配电柜、恒温箱、孵化房或者实验室恒温槽里问题立刻冒出来温度读数隔几分钟跳一次蜂鸣器半夜误报继电器在阈值附近来回打摆子传感器线拉长到五米以后读数直接变成 85 摄氏度。所谓高级指的不是器件贵而是从传感器采样、数字滤波、迟滞判定、报警消抖到掉电记录这一整条链路都经得起现场考验。这套方案适合做单片机课程设计、毕业设计的学生也适合需要给小设备加一路温度监控的嵌入式工程师。下面按硬件选型、采样算法、报警执行、现场整定四条线讲清楚。2. 温度检测链路选型传感器、单片机与前端电路怎么配温度报警系统的可靠性上限在选型和接线阶段就定死了。传感器决定了量程和分辨率单片机决定了你能跑多复杂的滤波和状态机前端电路决定了信号在长线下还能不能保持干净。这三者要一起看不能分开拍脑袋。2.1 DS18B20、NTC 与热电偶的适用边界常见做法是按量程、精度、成本三个维度筛选。DS18B20 是数字输出一根数据线就能读温度出厂已校准省掉 ADC 和标定环节适合 -55 到 125 摄氏度、精度要求 0.5 摄氏度左右的场合也是课程设计和毕业设计里最常用的器件。NTC 热敏电阻便宜但要自己搭分压电路再接 ADC还得做查表或公式换算长期漂移需要标定。热电偶量程能到上千度可冷端补偿和放大电路复杂一般只在工业炉窑里用。方案量程典型精度接口是否需要标定适合场景DS18B20-55 ~ 125 摄氏度正负 0.5 摄氏度单总线否恒温箱、水温、机房NTC 10K-40 ~ 125 摄氏度正负 1 摄氏度分压加 ADC是低成本家电、电池包K 型热电偶-200 ~ 1300 摄氏度正负 1.5 摄氏度需专用放大是炉窑、工业管道LM75-55 ~ 125 摄氏度正负 2 摄氏度I2C否板载环境温度选型时注意一个容易忽略的点DS18B20 有 9 到 12 位可配置分辨率12 位时分辨率 0.0625 摄氏度但单次转换要 750 毫秒9 位只要 93.75 毫秒分辨率 0.5 摄氏度。报警系统如果要求响应快可以把分辨率降到 10 位转换时间约 187 毫秒兼顾速度和精度。2.2 单总线拓扑与上拉电阻的取值DS18B20 的数据线 DQ 必须接一个上拉电阻到电源常用 4.7 千欧。这个电阻不是可选项缺了它读出来全是 0 或者直接识别不到器件。线长超过 3 米时上拉可以适当减小到 2.2 千欧增强上升沿驱动能力同时建议用屏蔽双绞线屏蔽层单端接地。供电方式有两种。外部供电是把 VDD 接 3.3 伏或 5 伏DQ 接单片机 IO稳定性最好长线首选。寄生电源是把 VDD 和 GND 短接靠 DQ 线在总线高电平时偷电存到内部电容只适合短线、单器件的场景。多器件挂同一条总线时每颗 DS18B20 的 64 位 ROM 地址都不同可以用搜索 ROM 命令枚举也可以直接把地址写死在代码里后者更省事但要防止换件后地址失效。/* DS18B20 单总线基础时序STC/51 平台晶振 11.0592MHz */ #include reg52.h #include intrins.h sbit DQ P3^7; /* 数据线接 P3.7 */ /* 微秒级延时_nop_ 在 11.0592MHz 下约 1 微秒 */ void delay_us(unsigned int us) { while (us--) { _nop_(); _nop_(); _nop_(); _nop_(); } } /* 复位脉冲拉低至少 480us再释放等待器件应答 */ unsigned char ds18b20_reset(void) { unsigned char ack; DQ 0; delay_us(500); /* 保持低电平 500us */ DQ 1; delay_us(60); /* 释放后 15~60us 内采样 */ ack DQ; /* 器件拉低表示存在 */ delay_us(420); /* 等时序走完 */ return ack; } /* 写一个字节低位先发 */ void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DQ 0; _nop_(); DQ dat 0x01; /* 写 1 则尽快拉高写 0 保持低 */ delay_us(60); DQ 1; dat 1; } }这段代码只做时序不含温度换算目的是说明单总线对时间很敏感复位低电平要够 480 微秒释放后 15 到 60 微秒内必须采样应答写时隙之间要留恢复时间。实际工程里如果单片机主频变了delay_us 的循环次数要重新标定用示波器量最稳妥。很多读不到器件的问题根源就是延时不准。2.3 单片机选型51、STC 与 STM32 的取舍标题里没指定平台但选型直接影响报警精度和并发能力。STC89C52 这类经典 51 只有 3 个定时器和约 512 字节 RAM跑单路温度加蜂鸣器够用成本低是蓝桥杯单片机和课程设计的主流。STC15W 系列是 1T 内核同频下速度快 12 倍内置 ADC 和 EEPROM做多路采样和掉电存储更方便。STM32F103 有 12 位 ADC、多个 USART 和 DMA适合做多通道、带串口上报和上位机联动的场合。我一般这样分单路 DS18B20 加声光报警用 STC89C52 或 STC15W 就够需要四路以上、带 RS485 组网或者要跑 PID 控温的直接上 STM32。选 51 时要注意 IO 口是准双向口做输入前先写 1选 STM32 时注意 DS18B20 需要微秒级延时HAL 库的 HAL_Delay 只有毫秒级得用 DWT 或者定时器做延时基准。3. 采样到判定滤波、换算与报警阈值算法传感器接好只是起点真正决定误报率的是从原始字节到报警结论这一段。这一段要解决三件事把 16 位原始数据换算成带符号温度、把跳变滤掉、把阈值附近的抖动变成一次干净的报警。3.1 DS18B20 原始数据换算与负温度处理DS18B20 读出来的 16 位数据低 4 位是小数位高 11 位是符号加整数部分。正温度直接乘 0.0625 摄氏度即可负温度要以补码方式取反加一再算。很多代码只测正温度没问题一到冷库环境就出负数错乱就是漏了符号位。/* 读取一次温度返回值为 0.0625 摄氏度为单位的有符号数 */ int ds18b20_read_temp(void) { unsigned char tl, th; int raw; ds18b20_reset(); ds18b20_write_byte(0xCC); /* 跳过 ROM适合总线只有一颗 */ ds18b20_write_byte(0x44); /* 启动温度转换 */ delay_ms(750); /* 12 位分辨率需要 750ms */ ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); /* 读暂存器前两字节是温度 */ tl ds18b20_read_byte(); th ds18b20_read_byte(); raw (th 8) | tl; if (raw 0x8000) { /* 负数补码转原码 */ raw (~raw 1) 0xFFFF; return -(raw * 10 / 16); /* 放大 10 倍便于整数运算 */ } return raw * 10 / 16; /* 正数同样放大 10 倍 */ }这里把温度放大 10 倍存成整数是为了在 51 平台上避免浮点运算。0.0625 摄氏度乘 10 等于 0.625用raw * 10 / 16近似后误差在一个刻度以内对报警判定足够。如果一定要小数显示可以在显示层再做一次除法。温度换算里有两个经典异常值上电默认值是 85 摄氏度说明没真正启动转换返回 -127 摄氏度通常是器件没应答或者 CRC 校验失败。工程代码必须对这两个值做过滤否则上电瞬间一定误报。3.2 中位值平均滤波把跳变压下去单次采样容易受工频干扰和总线噪声影响一次跳变就可能触发报警。常见做法是连续采 N 次去掉最大值和最小值再对剩下的求平均这叫中位值平均滤波也叫防脉冲干扰平均滤波。N 取 5 到 11 之间取奇数便于对称剔除。滤波参数推荐取值作用代价采样次数 N5 ~ 11奇数抗随机脉冲干扰占用 RAM 与时间剔除个数首尾各 1 个去掉最大最小对持续偏移无效采样间隔50 ~ 200 毫秒避开转换忙期响应变慢数据单位0.1 摄氏度整数省浮点运算精度受整数除法限制#define FILTER_N 5 int filter_buf[FILTER_N]; /* 中位值平均滤波输入为放大 10 倍的温度整数 */ int temp_filter(int new_val) { int i, j, tmp, sum 0; static unsigned char idx 0; filter_buf[idx] new_val; idx (idx 1) % FILTER_N; /* 冒泡排序N 很小代价可接受 */ for (i 0; i FILTER_N - 1; i) { for (j 0; j FILTER_N - 1 - i; j) { if (filter_buf[j] filter_buf[j 1]) { tmp filter_buf[j]; filter_buf[j] filter_buf[j 1]; filter_buf[j 1] tmp; } } } /* 去掉最小和最大各一个对中间三个求平均 */ for (i 1; i FILTER_N - 1; i) { sum filter_buf[i]; } return sum / (FILTER_N - 2); }逻辑上环形缓冲区保留了最近 N 次读数排序后剔掉首尾剩下的取平均既能抑制单个尖峰也能平滑随机噪声。参数上N 越大越平滑但滞后越明显如果被测对象升温很快比如加热台N 取 5 就够取 11 会导致报警晚到好几秒。FILTER_N 必须大于 2否则FILTER_N - 2会变成除零。3.3 报警阈值、回差与消抖计数温度刚好停在阈值上时噪声会让读数在阈值上下反复穿越报警器就会响个不停这就是打摆子。解决办法是设迟滞也就是回差报警阈值设 60.0 摄氏度解除阈值设 58.0 摄氏度两者差 2 摄氏度一旦报警就必须降到 58 以下才解除。#define ALARM_HIGH 600 /* 报警阈值 60.0 摄氏度单位 0.1 */ #define ALARM_CLEAR 580 /* 解除阈值 58.0 摄氏度 */ #define DEBOUNCE_CNT 3 /* 连续 3 次越限才确认 */ unsigned char alarm_state 0; unsigned char over_cnt 0; /* 状态机形式判定返回 1 表示处于报警态 */ unsigned char alarm_judge(int temp) { if (!alarm_state) { if (temp ALARM_HIGH) { if (over_cnt DEBOUNCE_CNT) { /* 连续越限计数 */ alarm_state 1; over_cnt 0; } } else { over_cnt 0; /* 未越限则清零避免偶发脉冲累计 */ } } else { if (temp ALARM_CLEAR) { if (over_cnt DEBOUNCE_CNT) { alarm_state 0; over_cnt 0; } } else { over_cnt 0; } } return alarm_state; }这段逻辑的要点是消抖计数必须在未越限时清零否则三次分散的瞬时尖峰也会凑成一次报警。另外报警状态和蜂鸣器输出要分开报警态保持蜂鸣器可以做间歇鸣叫这样既有提示又不会一直吵。如果设备还带继电器控温回差要设得比传感器精度大一般 1 到 3 摄氏度太小区间内继电器会频繁吸合触点寿命急剧下降。4. 报警执行与上报蜂鸣器、继电器、串口的落地配置判定出报警状态之后执行环节要做三件事驱动声光报警、输出联动信号、把事件记录下来并上报。这一段最容易出问题的是驱动能力和电气隔离处理不好会烧 IO 口或者干扰单片机复位。4.1 蜂鸣器与继电器的驱动电路选型单片机 IO 口灌电流有限51 一般不超过 20 毫安STM32 单脚约 25 毫安。有源蜂鸣器工作电流常在 20 到 30 毫安直接接 IO 会拉低电平甚至损坏端口要用三极管驱动。NPN 三极管如 S8550、S8050 配 1 千欧基极电阻即可蜂鸣器两端并联续流二极管防止关断反峰。继电器线圈吸合电流更大5 伏继电器常要 70 毫安以上建议用 ULN2003 达林顿阵列或者光耦加三极管组合。继电器触点控制 220 伏负载时必须做电气隔离和阻容吸收强电部分和单片机地要分开走否则继电器的电弧干扰会直接让单片机跑飞。执行器件驱动方式典型电流注意事项有源蜂鸣器S8050 加 1 千欧20 ~ 30 毫安并联续流二极管继电器模块ULN2003 或光耦70 ~ 90 毫安强弱电分离布线LED 指示灯串 1 千欧限流5 ~ 10 毫安可直连 IO可控硅光耦 MOC3063视负载而定需过零触发4.2 用状态机组织报警和联动逻辑把报警逻辑写成状态机比一堆 if 嵌套好维护。常见状态有正常、预警、报警、传感器故障四种。传感器故障单独作为一态很重要读数 85 或 -127 时不能按温度判定要直接进故障态并给出不同于超温的提示音比如故障是长鸣、超温是断续鸣。typedef enum { ST_NORMAL 0, /* 正常 */ ST_WARN, /* 预警接近阈值 */ ST_ALARM, /* 超温报警 */ ST_FAULT /* 传感器故障 */ } sys_state_t; sys_state_t state ST_NORMAL; void state_machine(int temp) { if (temp 850 || temp -1270) { /* 放大 10 倍后的异常值 */ state ST_FAULT; BUZZER 0; /* 长鸣 */ RELAY 1; /* 故障时切断加热 */ return; } switch (state) { case ST_NORMAL: if (temp ALARM_HIGH - 20) state ST_WARN; break; case ST_WARN: if (temp ALARM_HIGH) state ST_ALARM; else if (temp ALARM_HIGH - 40) state ST_NORMAL; break; case ST_ALARM: if (temp ALARM_CLEAR) state ST_WARN; break; case ST_FAULT: if (temp ! 850 temp ! -1270) state ST_NORMAL; break; } }这种写法的好处是每个状态的进入和退出条件都在一处调试时打印状态编号就能看出逻辑走向。预警态的存在可以让操作员在真正超温前有时间处理比如先亮黄灯、风机提前启动。4.3 串口上报协议与上位机解析带串口上报的系统帧格式要固定否则上位机解析很痛苦。推荐用帧头加长度加数据加校验的结构波特率 9600 或 1152008 位数据位、1 位停止位、无校验。每帧包含设备地址、温度值、状态位、时间戳低字节。/* 上报帧AA 55 | len | addr | temp_h | temp_l | state | crc */ void uart_report(unsigned char addr, int temp, unsigned char st) { unsigned char frame[8]; unsigned char crc 0; unsigned char i; frame[0] 0xAA; /* 帧头 1 */ frame[1] 0x55; /* 帧头 2 */ frame[2] 0x05; /* 数据长度 */ frame[3] addr; /* 设备地址 */ frame[4] (temp 8) 0xFF; /* 温度高字节 */ frame[5] temp 0xFF; /* 温度低字节 */ frame[6] st; /* 状态位 */ for (i 0; i 7; i) { crc ^ frame[i]; /* 简单异或校验 */ } frame[7] crc; for (i 0; i 8; i) { SBUF frame[i]; /* 51 平台发送寄存器 */ while (!TI); TI 0; } }异或校验只能发现单字节错误要求高的话换成 CRC16。上位机侧用 Python 解析时关键是先同步帧头再按长度取数据遇到校验失败直接丢弃并向后滑动一个字节重新找帧头这样能自动从错位中恢复。import serial ser serial.Serial(COM3, 9600, timeout1) buf bytearray() while True: buf ser.read(64) while len(buf) 8: if buf[0] ! 0xAA or buf[1] ! 0x55: buf.pop(0) # 未同步滑动一字节 continue frame buf[:8] crc 0 for b in frame[:7]: crc ^ b if crc ! frame[7]: buf.pop(0) # 校验失败继续找帧头 continue temp (frame[4] 8) | frame[5] if temp 0x8000: # 负温度还原 temp -((~temp 1) 0xFFFF) print(addr%d temp%.1f state%d % (frame[3], temp / 10.0, frame[6])) buf buf[8:]这段解析的核心是两条容错策略帧头不同步时滑动一字节校验失败时也滑动一字节。很多上位机程序卡死就是因为收到半帧后一直等剩余字节加了滑动窗口和超时清空就能稳定运行。5. 现场整定与排错参数怎么调、故障怎么定位系统装到现场后软硬件都不会一次到位真正的功夫在整定和排错上。下面这些参数和现象是我在恒温设备上反复遇到过的。先说阈值整定。报警阈值不能只按目标温度设要留出传感器误差和系统滞后。目标温度 60 摄氏度、传感器精度正负 0.5 摄氏度、箱内温度波动正负 1 摄氏度时报警阈值设 62 到 63 摄氏度比较合理解除阈值设 60 摄氏度回差 2 到 3 摄氏度。回差太小会打摆子太大则温度降下来还一直报警。再说采样周期。DS18B20 十二位分辨率转换要 750 毫秒如果主循环里每次都等转换程序基本被占满。常见做法是用定时器每 1 秒触发一次采样采样期间处理显示和按键转换等待放在定时器中断外这样人机交互不会卡。排错方面这几类现象对应关系很清楚。现象可能原因排查动作读数恒为 85 摄氏度未启动转换或延时不足检查 0x44 命令与 750 毫秒等待读数恒为 -127 摄氏度器件无应答或接线松量 DQ 上拉电压、示波器看复位应答读数周期性跳变上拉电阻偏大或线太长换 2.2 千欧上拉、改屏蔽线蜂鸣器误报后自恢复阈值附近噪声穿越加大回差、增加消抖次数继电器动作时单片机复位强电干扰或电源跌落强弱电分离、加去耦电容掉电后参数丢失未写入 EEPROM用 STC 内置 EEPROM 或外挂 24C02还有几个容易忽略的细节。第一看门狗要开。现场电磁干扰强程序跑飞后靠看门狗复位比死机强但喂狗要放在主循环里不能放在中断里否则中断正常主循环卡死时看门狗也救不回来。第二关键报警事件要存 EEPROM带时间戳存最近若干条掉电后能查到是哪天哪时超的温这对追溯很有用。第三多路测温时不要让所有 DS18B20 同时转换虽然有跳过 ROM 的并发转换命令但读回时仍需逐个寻址器件多了总线负载会变重建议分组轮询。调试温度报警系统最有效的工具是示波器和串口打印配合示波器看单总线时序对不对串口打印把每次滤波前后的原始值都发出来一看数据曲线就知道是硬件噪声还是算法问题。参数整定没有万能值先按上面推荐的区间设一版再根据实际温度曲线微调回差和消抖次数通常两三轮就能稳定下来。带 PID 控温的系统还要注意积分饱和报警阈值和 PID 输出限幅要一起考虑避免加热器全功率冲过阈值才被报警拦住。本文还有配套的精品资源点击获取
返回列表