
简介这是基于AT89C52单片机的温度报警系统毕业设计资料包主要面向单片机课程设计、毕业设计及电子爱好者。资料覆盖完整开发链路内置378行带注释的C语言源码、Proteus仿真图、原理图、结构框图以及设计报告、开题报告、元件清单、实物照片、操作说明书和运行图片等从方案设计到焊接调试均有说明。压缩包整体约21.14MB文件类型以源码、仿真工程和Word/PDF文档为主便于按模块对照学习。系统采用AT89C52DS18B20方案实现099℃温度测量、四位数码管实时显示配合三按键设置上下限越限时蜂鸣器与LED声光报警设计报告达13578字适合直接参考或在此基础上扩展。目前已有319人学习对于需要快速完成温度报警类项目的人来说是一套可直接复用的完整参考资料。1. 温度报警系统的经典骨架为什么还选 AT89C52如果你打开一个名称为“基于AT89C52单片机的温度报警系统”的工程压缩包大概率会看到这样的结构一个.c主文件、一个DS18B20.c、一个LCD1602.c外加 Proteus 仿真图和一张接线表。这类项目在课程设计和毕业设计里出现频率极高但真正把它调通、改明白的人并不多。其中一个典型原因是AT89C52 在 Proteus 仿真中能跑换到实物上却因为延时误差、晶振差异、P0 口没加上拉电阻而失去响应。AT89C52 是 51 核心的经典代表工作电压 5V、带 8KB Flash、256 字节 RAM跑 12MHz 晶振时一个机器周期正好是 1µs这让软件延时计算变得很舒服。温度报警系统的完整闭环包括三件事温度采集、阈值判断、报警输出。采集常用 DS18B20单总线协议只有一根数据线时序要求严格输出则是 LED 蜂鸣器必要时加 LCD 显示当前温度。这套组合适合两类读者一类是刚接触单片机想通过完整项目理解外设驱动和中断另一类是已经有 51 基础想把 Proteus 仿真换成实物却总在时钟配置和上下拉电阻上吃亏的人。本文按实际调试的顺序展开先讲 DS18B20 的读取时序和阈值逻辑再讲 LCD1602 显示与报警输出然后落到按键调阈值的状态机设计最后补充 Proteus 仿真改实物的几个高频坑。你会发现温度报警系统的调度再复杂核心就是那几个时序函数和状态判断。2. 温度采集与阈值判断用 AT89C52 读 DS18B20 的时序与转换2.1 DS18B20 单总线协议和 12MHz 晶振的配合DS18B20 和单片机之间只有一根数据线DQ所有命令、数据都是按位传输的。单总线的难点在于时序窗口很短复位脉冲要求主机拉低 480µs 以上读时隙要求主机拉低 1µs 后在 15µs 内采样总线写 1 和写 0 的时隙差别也只在拉低时间上。这是 AT89C52 在 12MHz 下的优势。因为while循环里每条指令约花 1µs2µs软件延时不需要挂定时器就能精准控制。如果你换成 STC 单片机同样代码在 12MHz 下跑没问题但换到 11.0592MHz 晶振时循环次数就该重新算。这也是为什么建议先把硬件用的晶振频率固定下来再写延时函数。以下是一个常用于 12MHz 的延时原语作为所有时序函数的基础void delay_usx(unsigned int x) // 12MHz 下近似 x µs { while (--x); }参数x直接对应微秒数因为 12MHz 下一个机器周期约 1µs。但要注意调用这个函数本身也有额外指令开销所以读时序里我一般会把x微调减 1 或 2然后配合逻辑分析仪看波形。温度报警系统的采集精度不要求到 0.1°C 级别时序差几微秒问题不大但复位和读时隙的边界不能越过规格书范围。读取温度的核心动作是三步发送复位脉冲、跳过 ROM0xCC、启动温度转换0x44。然后等待 750ms再复位一次发跳过 ROM 和读暂存器0xBE连续读两个字节得到温度值。2.2 手写一个 DS18B20 最小驱动含符号温度转换下面这段代码去掉了 CRC 校验保留最核心的复位、写字节、读字节够温度报警系统用sbit DS P2^2; // AT89C52 的 P2.2 接 DQ bit ds18b20_reset(void) { bit presence; DS 0; delay_usx(500); // 拉低 480µs 以上实际放宽到 500µs DS 1; delay_usx(60); // 等待从机拉低存在脉冲 presence DS; delay_usx(420); // 读完存在脉冲后再等完整释放 return presence; // 返回 0 表示存在 } void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS 0; DQ_DELAY; // 约 2µs DS dat 0x01; delay_usx(60); // 写时隙保持至少 60µs DS 1; dat 1; } } unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; DS 0; DQ_DELAY; // 1µs 短延时 DS 1; // 释放总线 DQ_DELAY; // 跳到采样点 if (DS) dat | 0x80; delay_usx(60); // 完成一个读时隙 } return dat; }这段代码里的DQ_DELAY是一个编译期宏定义成_nop_()或者 1µs 延时。函数逻辑说明写字节时先把总线拉低再根据数据位决定是否继续拉低读字节时先由主机拉低后释放从机决定总线高低主机采样。ds18b20_reset的返回值presence是判断传感器在不在线的关键报警系统可以在主循环里检测它如果为 1 说明断线直接触发报警避免读到 0xFF 而误报。温度值转换是另一个容易出错的点。DS18B20 返回的 16 位数据低 4 位是小数部分高 5 位是符号扩展位。以 12 位精度为例负温度时高 5 位全为 1正温度全为 0。把两个字节拼成int后右移 4 位再乘 0.0625 就得到实际温度。float tmp_value (int)tmp 4; // 得到整数部分 if (tmp 0x8000) tmp_value - 16; // 补偿符号位 tmp_value (tmp 0x0F) * 0.0625f; // 加上小数部分注意(int)tmp 4对正数是算术右移负数时高位补 1所以先移位后减 16 是为了恢复真实负值。如果直接用tmp / 16.0f再乘以 0.0625f 会重复放大这是温度报警系统里最常见的数据换算错误。2.3 阈值比较的三种写法以及为什么建议用回差单片机温度报警系统的阈值比较看上去是if (temp upper) 报警但实际运行时温度会在阈值边界抖动尤其是空调刚停机或加热器反复启停时。单片机执行速度很快温度读数稍有波动报警器就会反复开启关闭。最简单的办法是引入回差也叫滞回比较。假设上限设为 30°C回差设为 2°C那么进入报警状态的条件是温度上升到 30.5°C退出报警状态的条件是温度下降到 28.5°C。这样避免在临界点抖动。状态进入条件退出条件说明正常温度 ≤ 上限温度 上限无报警上限报警温度 上限温度 ≤ 上限 - 回差回差越大抗抖动越强下限报警温度 下限温度 ≥ 下限 回差下限同理会触发代码上用char alarm_state记录当前报警状态而不是每次重新判断这样还能保存之前的状态为后面按键显示状态机做铺垫。浮点比较尽量把阈值存储成整数比如温度乘 10 存成int避免 51 单片机用float做大量比较时吃资源。3. 显示与报警输出LCD1602 驱动和 AT89C52 引脚分配3.1 温湿度报警系统的显示方案为什么先选 LCD1602温度报警系统可以不显示温度用两个 LED 指示超限就行但实际项目中按键调阈值时看不到当前值操作起来很麻烦。最常见的方案是 LCD1602两行 16 字符能显示一行温度、一行阈值波特率问题很少驱动也简单。LED 数码管虽然也能做但动态扫描会占用 CPU 时间和 DS18B20 的 750ms 转换等待叠在一起时调度体验较差。LCD1602 接在 AT89C52 的 P0 口比较好因为 P0 是开漏输出外部加上拉电阻后驱动能力稳定。控制线 RS 和 E 分别占用 P2.5 和 P2.6这样可以避免和 DS18B20 的 P2.2 冲突。3.2 LCD1602 最小初始化和字符串输出函数LCD1602 上电后需要等待约 15ms然后写三次初始化命令0x38再设置光标和清除屏幕。以下是最小初始化流程void lcd1602_init(void) { delay_usx(15000); // 上电等待 lcd1602_write_cmd(0x38); // 8 位模式双行5x7 点阵 lcd1602_write_cmd(0x38); // 重复 3 次是规格书要求 lcd1602_write_cmd(0x38); lcd1602_write_cmd(0x0C); // 显示开光标关 lcd1602_write_cmd(0x06); // 写入后地址自动加 1 lcd1602_write_cmd(0x01); // 清屏 }写命令前要检查忙标志还是使用固定延时这个取决于你的代码风格。忙标志读取要先把RW置 1再读DB7不少 Proteus 仿真中这一步没问题换到实物上经常因为引脚定义错而卡死。我一般直接用延时代替忙检测省去一个引脚而且固定延时在低温环境下不会出问题。输出温度时用sprintf格式化字符串char buf[17]; sprintf(buf, Temp:%5.1f C, temp_value); lcd1602_write_string(0x80, buf); // 第一行地址 0x80sprintf在 51 上比较耗 Flash如果你只有 8KB建议自己写一个数字转字符串的小函数或者用printf重定向到 LCD。温度报警系统里数据量不大sprintf可以接受但要注意 AT89C52 的栈空间有限格式化浮点时会用掉约 200 字节所以尽量把temp_value转成整数再格式化。3.3 报警输出的引脚选取蜂鸣器、LED 和继电器驱动报警输出通常有三种负载蜂鸣器、LED、继电器。AT89C52 的 P1 口是准双向口拉电流很小但灌电流可以到 20mA 左右所以点亮 LED 时要把 LED 负极接单片机引脚正极通过限流电阻接 5V。蜂鸣器分有源和无源有源蜂鸣器内部自带振荡源只需给电平就能响适合报警系统无源蜂鸣器需要用定时器产生频率代码复杂度高一些。继电器用在断电报警或联动加热器开关的场景这时必须加三极管或达林顿管驱动因为 AT89C52 引脚输出拉不到继电器线圈所需的 30mA 以上。常见的 8550 三极管接法基极经过 1kΩ 电阻接 P1.0发射极接 GND集电极接继电器线圈线圈并联一个 1N4007 续流二极管。注意 8550 是 PNP 管低电平导通如果你习惯高电平导通换 S8050 或直接选 NPN 的 ULN2003 模块。报警输出不能只做开关量要考虑防抖和优先级。优先级可以是断线报警 上限报警 下限报警断线时温度读成0xFF如果不去判断复位返回值就会被误判成 -0.0625°C 或 85°C导致报警逻辑紊乱。很多 AT89C52 项目在 Proteus 里没有断线场景实物一拔传感器就乱响根源就在这里。4. 按键调阈值与状态切换温度报警系统的状态机设计4.1 为什么不用死循环等待按键而用状态机温度报警系统的参数有两个上限和下限。最简单的方法是进入设置模式后轮询按键等待用户按完再退出。但这种写法在主循环里会让系统处于阻塞状态温度采集被暂停LCD 刷新也不及时用户体验很差。常见做法是用一个状态机管理整个系统的运行状态。状态枚举如下typedef enum { SYS_MONITOR 0, // 正常运行显示温度并判断报警 SYS_SET_HIGH, // 设置上限温度值不再刷新 SYS_SET_LOW, // 设置下限 } sys_state_t;每次主循环开始时读取按键值再根据当前状态决定按键事件要做什么。例如在SYS_SET_HIGH状态下KEY_UP使上限 1KEY_DOWN使上限 -1KEY_OK保存并跳到SYS_SET_LOW全部设置完成后返回SYS_MONITOR。按键需要消抖机械按键按下时会有约 5ms10ms 的电平抖动。温度报警系统不需要高精度响应用定时扫描 两次确认即可第一次读到低电平延时 10ms 再读一次还是低电平才算有效。下面是按键扫描和消抖的推荐写法unsigned char key_scan(void) { static unsigned char key_prev 1; unsigned char key_cur KEY_PORT 0x0F; // 读 4 个独立按键 unsigned char key_event 0; if (key_cur ! key_prev) { delay_usx(10000); // 跨过抖动区 key_cur KEY_PORT 0x0F; if (key_cur ! key_prev) { key_event key_cur (~key_prev); // 下降沿产生事件 key_prev key_cur; } } else { key_prev key_cur; } return key_event; }逻辑说明key_prev保存上次状态当检测到电平变化时延时重读二次确认后只在此次电平变化的下降沿返回事件值。这样长按不会触发多次点按一次触发一次。KEY_PORT 0x0F假定 4 个按键接在同一个端口的低 4 位按键另一端接 GND端口默认置高。4.2 阈值存储与 EEPROM 意义阈值如果只存在 RAM 里断电后恢复默认值 30°C / 10°C。有些场景需要记住用户设定值就需要掉电保存。AT89C52 本身没有 EEPROM常见做法是外挂 AT24C02或者用 STC 系列单片机自带的 EEPROM实际上是 Data Flash。如果项目指定 AT89C52不增加外部存储那就直接把阈值定义成const数组放在程序 Flash 里每次重新烧录时更新。但这样现场调参就很麻烦所以温度报警系统实物里通常还是会加一个 AT24C02。AT24C02 使用 I2C 总线SCL 和 SDA 各占一个引脚。写入时先发送设备地址0xA0再发送字节地址和数据注意页写上限是 8 字节不能跨页连续写。读取时发设备地址和字节地址后重新发送读命令0xA1然后连续读两个字节。阈值是整数存储时直接放 int 的低字节和高字节。4.3 状态机与温度采集的时序协调状态机切换时要注意 DS18B20 的转换节奏。温度转换命令发出后需要 750ms如果你在SYS_SET_HIGH状态下等待按键同时还在每 200ms 发一次转换命令DS18B20 会不断进入转换状态读取结果永远是上一次转换的未完成值。更合理的做法是设置模式下暂停温度刷新只显示当前阈值退出时重新发一次转换命令等 750ms 后再读。主循环的调度用简单的时间片即可不引入实时操作系统。伪代码结构while (1) { key_event key_scan(); handle_key_event(key_event); // 根据状态机处理 if (sys_state SYS_MONITOR) { if (time_to_read 750) { read_temperature(); check_alarm(); update_display(); time_to_read 0; } } }time_to_read由定时器中断累加每 1ms 加 1。这样 51 单片机在温度转换期间还能响应按键LCD 刷新也不至于顿卡。你还可以把check_alarm()放在定时器中断里快速响应但要注意中断里别做 LCD 写操作因为 LCD 时序要求较高放到主循环中安全。5. Proteus 仿真调试与实物移植温度报警系统从图纸到硬件的坑5.1 AT89C52 在 Proteus 里的仿真必备设置Proteus 仿真 51 单片机的步骤看似简单但很多 AT89C52 温度报警系统项目在仿真里乱码或定时器不准原因不是程序问题而是元件属性没设置。双击 AT89C52 芯片将Clock Frequency设为 12MHz这是第一个关键参数。晶振器件如果没有特别设置默认 1MHz二者不一致时定时器初值计算的机器周期全是错的。第二个关键是 P0 口。Proteus 里 P0 内部是开漏结构驱动 LCD1602 数据线时必须接上拉排阻电阻值用 10kΩ 常见的排阻模块RESPACK-8即可一端接 P0 口另一端接 5V。漏加上拉电阻的典型现象是 LCD 对比度有变化但显示不出字符数码管亮度偏低。第三个是 DS18B20 的 DQ 引线要加 4.7kΩ 上拉电阻到 5V。Proteus 中有些版本不加上拉也能读因为模型默认有弱上拉但实物上没有这个电阻单总线在长线传输时波形会很差。以下是一张 12MHz 晶振下常用延时和参数计算参考表适合烙在程序上反复对比参数值说明机器周期1µs12MHz 晶振12 分频DS18B20 转换时间750ms12 位期间不能发读取命令LCD1602 上电延时15ms稳压后约等待两次电源纹波蜂鸣器持续报警时间500ms 起超过 1s 容易听感疲劳阈值上下限相差≥ 5°C避免正常模式与双限报警冲突5.2 烧录与下载线的接法从仿真到实物的最后一步Proteus 仿真通过后往实物上移植时要重新检查三个细节晶振电路、复位电路、下载接口。AT89C52 需要外部晶振常见接法是 12MHz 晶振两端接 30pF 电容到 GND复位电路用 10µF 电解电容接 VCC引脚通过 10kΩ 电阻到 GND上电时 RST 引脚被拉高一段时间完成复位。实验室常见的 USB 转 TTL 模块是以 CH340 为核心的烧录时 P3.0RXD接模块 TXDP3.1TXD接模块 RXD不能同端对接。如果你用的是 STC 单片机烧录软件选择 STC-ISP把芯片型号选成对应的例如 STC89C52RC频率选 12MHz。注意 STC 烧录时冷启动先点下载再给板上电。这个习惯和 AT89C52 不同AT89C52 需要编程器。现在很多开发板直接把 STC 芯片做成兼容 AT89C52 引脚程序不用改但P0口上拉电阻在开发板上已集成接自定义电路时要自己补上。5.3 一个被低估的稳定性调试技巧软件看门狗不能被忽视温度报警系统如果长期通电运行如冷库和机房监控场景里可能一年不重启单片机受电源干扰或程序跑飞的风险就很大。AT89C52 没有内置看门狗只能用外部看门狗芯片或通过定时器模拟。定时器模拟看门狗的常见做法是用 Timer1 做定时中断主循环每 100ms 喂一次狗如果主循环卡死超过 500ms定时器触发软复位跳回main开头。喂狗代码放在主循环末尾void feed_wdt(void) { TH1 0xE0; // 每 100ms 刷新一次 TL1 0x00; }TH1/TL1的初值按 12MHz 和所选定时工作方式计算这里只是示意形式。跑飞以后 DS18B20 可能停留在读时序的某个中间状态直接在main开头重新初始化所有外设稳妥做法是把 DS18B20 复位函数放在初始化里调用一次确认存在脉冲后再继续执行。你在 Proteus 里按暂停键再继续仿真很容易卡死重启仿真却正常这个状态接近实物中的跑飞加模拟看门狗能明显提升稳定性。本文还有配套的精品资源点击获取