
简介面向单片机课程设计、毕业设计及工程实训场景这份基于C51的温度检测报警系统资料包适合需要完成类似课题的初学者或进阶学习者。系统以AT89C51为核心通过DS18B20采集环境温度实时显示在LCD1602上并支持按键自由设定上下限超限时触发高温或低温报警灯及电机正反转涵盖完整逻辑闭环。资源共9个文件压缩包约2.69MB包含C语言源码如LCD.c、Motor.c、设计报告docx、答辩pptx、Proteus工程pdsprj、工程截图jpg及README说明便于对照学习代码与仿真调试。已有150人浏览学习。资料可作为课程设计或初期项目立项的参考方案重点帮助理解传感器读取、液晶显示、按键交互与报警控制等模块的集成思路但需具备一定基础自行调试并扩展功能。1. 温度检测报警系统是C51课程设计里最容易翻车的高频选题温度检测报警系统是C51单片机课程设计中的高频选题也是翻车率最高的选题之一。很多学生拷贝网上源码烧进去后温度显示乱跳或停在85℃报警在阈值附近反复通断答辩时被问“为什么用DS18B20”“阈值存在哪里”就卡壳。这个题目真正练的是把最小系统、单总线时序、定时器、按键和输出驱动串成一个稳定闭环。下面按课程设计的完整链路来走从传感器选型、C51驱动实现、报警回差控制讲到Keil工程搭建最后落到报告和答辩PPT的技术整理。适合正在做这个课程设计的人也适合想拿C51做完整小项目练手的人。2. 系统结构设计与温度传感器选型依据2.1 C51最小系统与温度传感器三种方案对比C51课程设计一般以STC89C52RC或AT89S52为核心这类芯片内部没有ADC。温度检测要在“没有ADC”和“模拟/数字接口”之间做选择。常见三种方案热敏电阻分压、LM35模拟输出、DS18B20单总线数字输出。热敏电阻阻值随温度非线性变化需要查表标定LM35输出电压10mV/℃线性度好但要外接ADC0809或换带ADC的STC15系列外围电路变大DS18B20用单总线协议不占ADC一根数据线直接输出16位温度值12位分辨率下达到0.0625℃完全满足室温超限报警需求。下表从课程设计交付角度做对比方案信号类型是否需ADC精度外围电路代码复杂度答辩说服力热敏电阻模拟电阻值是低依赖标定中中一般LM35模拟电压是中高中一般DS18B20单总线数字否0.5℃/0.0625℃低中高高DS18B20看起来代码更复杂因为要处理严格的时序但课程设计要的就是这个训练点。方案确定后晶振选11.0592MHz还是12MHz会直接决定延时和波特率。11.0592MHz能把9600波特率误差降到0DS18B20时序按微秒计算时与12MHz差距不大如果还要用串口打印调试11.0592MHz更稳妥。复位电路用10uF电解电容和10k电阻接到RST引脚Proteus里不接也能仿真但实物不接复位电路上电瞬间程序可能跑飞。2.2 DS18B20单总线时序与C51引脚规划DS18B20的DQ引脚需要外接4.7k上拉电阻到VCC。测量时单片机先复位总线然后发送跳过ROM指令0xCC再发启动转换指令0x44等待转换结束12位精度最长750ms再次复位并发送读取暂存器指令0xBE连续读两个字节得到温度低字节和温度高字节。写0时总线低电平需保持60~120us写1时低电平保持1~15us后释放读时序由主机拉低总线1~15us随后释放并在15us前采样。C51主频12MHz时一个_nop_()大约耗时1us但单总线操作时不能开串口中断否则时序被截断。引脚分配参考LCD1602数据线接P2口DS18B20接P3.7蜂鸣器接P1.5LED接P1.6按键接P3.2和P3.3。P3口本身带第二功能如果开发板上有串口和外部中断注意别把DQ与RXD/TXD接在一起。下面是一个标准的初始化与存在脉冲检测代码#include reg52.h #include intrins.h sbit DQ P3^7; void Delay10us(unsigned char t) { while(t--) { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } } bit DS18B20_Reset(void) { bit presence; DQ 1; Delay10us(2); DQ 0; Delay10us(50); // 拉低约 500us DQ 1; Delay10us(6); // 释放后等约 60us presence DQ; Delay10us(50); // 等待存在脉冲结束 return presence; // 返回 0 表示传感器在位 }Delay10us的循环体里有5个_nop_()加上while(t--)和sbit操作12MHz下循环一次约10us参数50对应500us左右。如果改成11.0592MHz同样代码耗时约10.85us500us需求仍能满足。presence DQ放在释放后的6次延时之后也就是大约60us处DS18B20的存在脉冲通常发生在释放后的60~240us窗口所以这个采样点读到的电平时序上没问题。实际调试中如果连续返回1先查DQ引脚是否虚焊、上拉电阻是否漏贴、供电是否5VDS18B20在3.3V下驱动能力下降后会表现为随机复位失败。2.3 报警输出电路与硬件设计细节蜂鸣器若是5V有源蜂鸣器工作电流约20~30mAC51的IO口弱上拉模式只能提供几百微安级输出不能直接驱动。常见做法是加一个S8550三极管做开关IO口通过1k电阻接基极蜂鸣器接在集电极和VCC之间发射极接地。IO口输出高电平时三极管导通蜂鸣器响输出低电平时关闭。如果直接用IO口驱动蜂鸣器声音很弱甚至在报警状态被拉低为0V导致单片机复位。继电器控制加热设备时必须并联1N4007续流二极管否则断电瞬间的反向电动势会打坏IO口。课程设计如果只做声光报警用三极管驱动蜂鸣器就够如果报告里写了“控制加热器”就要把继电器保护电路画出来否则答辩被问到“感性负载怎么保护”会很被动。3. C51程序实现温度采集、整数换算与报警回差控制3.1 定时器1秒节拍与主循环设计温度采集程序最容易写崩的地方是主循环里用Delay(1000)等转换完成然后按键扫描失灵。我一般会让定时器0产生1s时间片主循环检测到标志后再执行一轮温度读取。定时器0初值按12MHz计算工作方式1单次最多定时65.535ms所以设置50ms中断一次累计20次就是1s。以下代码是定时器初始化和中断服务函数#include reg52.h bit t_1s_flag 0; void Timer0_Init(void) { TMOD 0x01; // 定时器0方式116位定时 TH0 0x3C; TL0 0xB0; // 初值50ms12MHz ET0 1; TR0 1; EA 1; } void Timer0_ISR(void) interrupt 1 { static unsigned int cnt 0; TH0 0x3C; TL0 0xB0; cnt; if(cnt 20) { cnt 0; t_1s_flag 1; } }TMOD0x01只把定时器0设置为16位定时不影响定时器1TH0/TL0重装必须放在中断入口最前否则中断处理时间过长会累积计时误差。interrupt 1是Keil C51规定的定时器0中断号STC89C52RC的中断向量与该标准一致。主循环检测到t_1s_flag后调用温度读取。需要强调读取DS18B20时序期间要关中断否则定时器中断插入会破坏单总线时序因此在读取函数入口执行EA0读取结束后EA1。定时器计数因此会产生几个毫秒误差但对秒级采集没有影响。3.2 写字节、读字节与整数温度换算DS18B20一个字节的收发需要按位操作。单片机向传感器写一个字节时先发最低位每个位周期拉低总线延时1~15us后写入位电平再保持60~120us读字节时主机产生一个短暂的拉低脉冲释放后采样总线状态。下面这段是常用的位时序实现void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for(i0; i8; i) { DQ 0; _nop_(); _nop_(); DQ dat 0x01; Delay10us(8); DQ 1; dat 1; } } unsigned char DS18B20_ReadByte(void) { unsigned char i, dat 0; for(i0; i8; i) { DQ 0; _nop_(); _nop_(); DQ 1; dat 1; _nop_(); if(DQ) dat | 0x80; Delay10us(8); } return dat; }写字节函数里Delay10us(8)让总线位电平保持80us左右如果Keil优化等级设置到3编译器可能把空循环优化掉导致延时失效。把Delay10us参数设为unsigned char t并放在单独C文件中能避免被过度优化。读字节函数在DQ1后只执行两个_nop_()就采样对应约15us内的采样窗口再继续等80us结束该位周期。读出的两个字节拼接成16位有符号数后还需要换算成带小数的温度值。读取温度的完整过程如下int DS18B20_ReadTemperature(void) { unsigned char high, low; int raw; EA 0; // 关中断保护单总线时序 DS18B20_Reset(); DS18B20_WriteByte(0xCC); // Skip ROM DS18B20_WriteByte(0x44); // 启动转换 while(!DQ); // 等待转换完成 DS18B20_Reset(); DS18B20_WriteByte(0xCC); // Skip ROM DS18B20_WriteByte(0xBE); // 读暂存器 low DS18B20_ReadByte(); high DS18B20_ReadByte(); EA 1; raw (high 8) | low; return raw; }while(!DQ)在传感器完成转换后会释放总线DQ变高循环退出。如果传感器没接好DQ一直为高这个等待会立即跳过读到的数据就是上一次暂存器里的旧值。换算时0.0625℃/LSB写成整数运算就是“乘以25再除以4”得到温度值乘以100的整数。例如原始数0x0191十进制401乘以25除以4结果是2506对应25.06℃。显示时把2506拆成千位、百位和个位即可。这种算法没有浮点误差在阈值比较时直接用temp_100 2800表示28.00℃非常直观。3.3 报警阈值设定与回差控制报警逻辑不能写成“温度大于上限就响”否则温度在阈值附近抖动时蜂鸣器会反复通断。工程做法是加入回差也叫滞回比较。设置上限TEMP_MAX和回差TEMP_DIFF触发报警时温度超过上限解除报警时温度必须低于TEMP_MAX - TEMP_DIFF下限报警反过来处理。以下是一段可直接移植的报警检查函数#define TEMP_MAX 28 // 上限 28.00℃ #define TEMP_MIN 18 // 下限 18.00℃ #define TEMP_DIFF 2 // 回差 2.00℃ bit over_high 0; bit over_low 0; void Alarm_Check(int temp_100) { if(!over_high) { if(temp_100 TEMP_MAX * 100) over_high 1; } else { if(temp_100 (TEMP_MAX - TEMP_DIFF) * 100) over_high 0; } if(!over_low) { if(temp_100 TEMP_MIN * 100) over_low 1; } else { if(temp_100 (TEMP_MIN TEMP_DIFF) * 100) over_low 0; } P1 | 0x60; // 默认关蜂鸣器和LED if(over_high || over_low) { P1 ~0x20; // 蜂鸣器响 P1 ~0x40; // LED 亮 } }阈值全部乘以100与temp_100同单位避免浮点。P1 | 0x60把P1.5和P1.6置1P1 ~0x20把蜂鸣器引脚拉低。有源蜂鸣器低电平驱动时响LED如果也是灌电流接法低电平点亮。开发板如果用的是高电平驱动逻辑要反过来。回差选取要看场景强电控制场合设2~5℃都合理纯声光报警设1~2℃即可。回差太大会造成控制目标偏移例如上限28℃、回差5℃意味着下次解除报警要降到23℃。下面是一组可直接写进报告的参数示例参数值说明TEMP_MAX28上限阈值TEMP_MIN18下限阈值TEMP_DIFF2回差防止报警抖动4. Keil5工程搭建、Proteus仿真与实物下载调试4.1 Keil5兼容C51和STM32安装时的共存配置很多电脑上先装了Keil MDK用于STM32打开后找不到C51器件于是觉得51编译器不见了。这其实是Keil的ARM和C51是两套独立组件。Keil官网提供C51安装包安装时可以选择与MDK相同或不同目录。安装完成后µVision里编译51工程时自动选择C51编译器编译STM32工程时选择ARMCC互不干扰。问题多出在LicenseFile - License Management里能看到C51和ARM的许可状态C51项目提示PRODUCT MISSING说明当前授权没有C51组件需要单独添加C51许可证。C51组件许可缺失时用学校正版License添加即可不要依赖来路不明的工具。STC89C52RC这类国产51型号在Keil原装设备库里没有需要用STC官方的STC-ISP软件导入。STC-ISP里有一个“Keil仿真设置”页签点击“添加型号和头文件到Keil中”选择Keil安装目录后软件自动写入STC官方设备数据库。导入后Device列表能看到STC89C52RC、STC15W4K32S4等型号同时把STC89C52.H之类的头文件复制到Keil的INC目录。这个操作只增加设备描述不改变C51编译器所以不会影响原有ARM工程。如果暂时无法安装STC设备库直接用Keil自带的AT89C52也能编译因为代码里用到的寄存器在标准51里都存在烧录时再通过STC-ISP选真实型号下载HEX即可。4.2 Proteus仿真中DS18B20和晶振频率的调试坑Proteus仿真这种系统最常见两个怪现象温度恒为85℃或者数值周期性跳变。85℃是DS18B20上电后的初始值说明程序发出的启动转换指令没有被模型执行常见原因是仿真原理图里的晶振频率远低于12MHz。Proteus放置8051时默认Clock Frequency为1MHz如果程序延时按12MHz计算单总线时序会被拉长12倍自然失败。正确做法是在单片机属性里把Crystal Frequency改为12MHz或11.0592MHz。第二个坑是Proteus的DS18B20模型对时序非常敏感Reset释放后等待存在脉冲的代码如果超过240us才采样模型会返回错误。调试时把虚拟示波器接到DQ引脚上观察复位低电平是否保持480~960us如果低电平过短检查Delay10us里是否有被Keil优化掉的空循环。仿真环境下供电和上拉电阻的寄生效应与实物不同仿真通过不代表实物一定通过反之亦然。现象可能原因处理方式温度恒为85℃晶振频率设置不对改Crystal Frequency为12M温度不变传感器引脚接触不良测DQ电压检查上拉电阻下载失败TXD/RXD接反交叉连接后重试4.3 HEX烧录、冷启动与串口打印温度编译生成HEX文件后用STC-ISP软件烧录STC89C52RC。STC单片机ISP下载必须冷启动先在STC-ISP界面选择串口、单片机型号和HEX文件点击“下载/编程”直到提示“正在检测目标单片机”后再给目标板断电重新上电。如果程序里用了外部晶振STC-ISP里还有一个“选择IRC频率”的选项选择与硬件一致的频率否则串口波特率或定时器初值会偏。下载失败时优先检查USB转TTL模块的TXD/RXD是否交叉连接以及目标板是否独立供电。调完下载可以加一个串口输出函数把temp_100格式化后发到串口助手快速判断温度读取是否正常。9600波特率下11.0592MHz晶振定时器1方式2初值设为0xFD。发送字节流可以直接按整数处理void Send_Temp(int temp_100) { unsigned char b[4]; b[0] temp_100 / 1000 0; b[1] (temp_100 / 100) % 10 0; b[2] (temp_100 / 10) % 10 0; // 发送012这样的三位整数再单独发小数位 }这里temp_100 / 1000得到十位temp_100 / 100 % 10得到个位temp_100 / 10 % 10得到十分位。串口助手显示“25 5”这样的非格式化数据也比没有输出强至少能区分读温度的问题和显示的问题。如果串口收到数据全是0xFF检查波特率初值是否写反或者晶振频率与STC-ISP里选择不一致。5. 报告撰写与答辩PPT里容易加分的三个实测技巧5.1 报告中的回差控制和整数运算论证课程设计报告不要平铺直叙“温度大于上限报警”只能算需求说明。在软件设计部分用一小节专门讲回差控制给出触发/解除阈值公式再附一组实测数据设定上限28℃回差2℃温度从27.9℃升到28.3℃触发报警降到27.5℃解除报警记录时间点。这组数据以表格呈现比贴一段代码更有说服力。再把温度整数换算算法temp_100 raw * 25 / 4写进报告注明“避免浮点库占用资源”就显示出你理解了嵌入式资源约束。源码组织上把DS18B20驱动、LCD显示、按键和报警逻辑分文件写报告里附源码文件清单评委扫一眼就知道不是单文件堆出来的。5.2 答辩PPT按“一页一问题”准备答辩PPT不要做成长篇代码展示。按10页左右的容量每一页对应一个可能的提问系统框图页对应“为什么选DS18B20”准备一个方案对比表软件流程图页对应“报警阈值存在哪里”解释存在STC单片机内部EEPROM或外部AT24C02中掉电不丢失实物演示页对应“传感器被拔掉会怎样”演示代码中Reset返回1时显示Sensor Error蜂鸣器不动作避免误报。这样评委的追问恰好落在你准备过的内容上比你猜题更可靠。PPT里放一张用串口记录的温度趋势图展示温度从室温爬升到报警阈值的完整过程以及报警解除点的位置能直观说明回差控制的必要性。5.3 演示前最后5分钟的自检清单答辩现场最容易翻车的是硬件而不是PPT。按这个顺序自检先用STC-ISP确认HEX已写入且校验通过用LED闪烁程序排除最小系统不起振的问题再烧温度检测程序用手捏住DS18B20的金属外壳2秒内温度应上升2℃以上最后把电源断开再重插一次确认冷启动后报警功能自动恢复。如果温度显示为85℃或负数先别改代码检查DQ线上拉电阻和第一次初始化时序。经过这套自检现场演示出问题的概率会降到很低最后给报告附上这份自检清单本身也可以作为“调试与验证”章节的内容来源评分时比单纯放截图更扎实。本文还有配套的精品资源点击获取