
简介本资源是一套完整的基于51单片机的温湿度监测与报警系统开发资料面向嵌入式初学者、课程设计学生及电子类实训人员解决环境参数采集、阈值设定、越限报警与本地可视化显示等典型单片机应用问题。压缩包共44个文件涵盖Proteus仿真工程含可运行仿真图、Keil C源代码工程含main.c、DHT11.c、lcd1602.c等核心模块及对应头文件与编译输出文件、原理图SchDocPDF预览、功能说明文档、元件清单XLS格式及流程图BMP格式全面支撑从电路设计、程序调试到仿真验证的全流程学习。资源包大小为801KB结构清晰、模块分离明确所有代码均带注释LCD1602驱动与DHT11通信协议实现完整支持按键设置温湿度上下限并实时刷新显示报警逻辑可靠。目前已有282人学习下载是入门单片机传感器应用与人机交互开发的高实用性参考方案。 做课程设计或者电子实习的同学十有八九会撞上这个经典组合51单片机、DHT11温湿度传感器、LCD1602液晶屏再加一个Proteus仿真。我这次把一个编号“1593”的温湿度报警课题完整跑了一遍原理图、流程图、物料清单、仿真图、源代码一路梳理下来发现这个项目表面看着简单真正要一次跑通还是有几个容易被忽略的坎。这篇就把整个设计和踩坑过程掰开揉碎讲清楚适合正在做课程设计、练手单片机的同学也适合想了解DHT11单总线时序、LCD1602显示逻辑的初学者参考。先说这个课题核心做什么用51单片机读取DHT11的温度和湿度数据在LCD1602上实时显示当温度或湿度超过设定阈值时驱动蜂鸣器报警。交付物包括可仿真的Proteus工程、可烧录的KEIL源码以及配套的原理图和物料清单。整体难度属于单片机入门偏综合麻雀虽小但五脏俱全数字量采集、字符显示、报警驱动、仿真调试这几个环节都覆盖了对后面做更复杂的系统是很好的铺垫。1. 项目整体设计与方案选型1.1 功能需求拆解拿到这个课题第一步不是急着写代码而是把需求拆成明确的模块。这个温湿度报警系统本质上是三个动作的循环采集数据、显示数据、判断并报警。采集部分用DHT11输出的是数字信号不需要ADC这是它比模拟输出的传感器方便的地方。显示部分用LCD1602两行十六个字符足够显示“温度xx℃ 湿度xx%”这类信息。报警部分就是处理一个开关量温度或湿度越限就打开蜂鸣器。把这个流程画成流程图其实很简单系统上电之后先初始化LCD1602给DHT11一个稳定时间然后进入主循环。主循环里依次执行读取DHT11数据、校验数据、换算显示、与阈值比较、控制蜂鸣器最后延时到下一次采集。要注意DHT11的采样周期最低是1秒所以主循环里至少要有1秒的延时不能像点亮LED那样高频扫描。1.2 为什么是“51单片机 DHT11 LCD1602”这个组合很多初学者会问现在STM32、ESP32这么普及为什么课程设计还在用51单片机因为51单片机在这个场景下是最合适的选择。温湿度采集本身是慢速过程DHT11的数据速率只有几十赫兹51单片机即便是12MHz晶振下的经典8051内核处理这个量也绰绰有余不存在性能瓶颈。其次51单片机资料极其丰富遇到问题随便搜就有答案郭天祥那本经典教程里相关例程都是现成的对课程设计来说效率最高。DHT11同样是入门温湿度传感器的标准答案。它把温湿度传感器、ADC、单总线协议封装在一个四脚元件里只需要一根数据线和单片机通信对于刚接触单片机通信协议的读者来说单总线协议比IIC、SPI要直观很多。LCD1602就更不用说了字符型液晶是单片机显示方案的必修课虽然现在很多项目用OLED甚至TFT彩屏但1602的初始化时序、读写时序一旦吃透后面用其他屏都会轻松很多。1.3 Proteus仿真的价值与边界Proteus在这个项目里的角色是“踩坑成本为零的试验场”。实物焊接之前先在仿真里把电路接对了、程序调通了能省下大量时间和器件损耗。而且Proteus里的示波器、虚拟终端这些调试工具排查时序问题比实物还直观。比如DHT11的波形在实物里要用逻辑分析仪才能看Proteus里直接用虚拟示波器就能抓。但必须提醒你一点仿真跑通不等于实物一定成功。Proteus里的元件模型是理想化的DHT11的时序宽容度、LCD1602的对比度要求、蜂鸣器驱动能力都和实物有差异仿真里能用的代码搬到实物上可能要微调延时参数。正确的做法是仿真用来验证逻辑实物验证电气特性两个都跑通了才算是真会了。2. 硬件电路设计原理图核心模块与物料清单2.1 单片机最小系统电源、晶振与复位51单片机要跑起来最小系统必须有三个部分电源、晶振电路、复位电路。电源这块DHT11、LCD1602、蜂鸣器都是5V供电直接用USB口的5V或者稳压芯片输出的5V就行注意加一个100uF左右的滤波电容防止电机、蜂鸣器这类器件启动时拉低电压。晶振电路我这次选的是12MHz晶振配合两个30pF的负载电容。为什么选12MHz而不是11.0592MHz因为本设计不需要串口通信12MHz下机器周期刚好是1us软件延时的时间计算非常方便。如果后续你要加串口和上位机通信再换成11.0592MHz不迟。晶振接法是把晶振接到XTAL1和XTAL2引脚两个负载电容分别从晶振两端接地形成并联谐振回路。复位电路这里有个新手很容易忽略的细节51单片机是高电平复位上电瞬间电容相当于短路10uF电容充电期间RESET引脚维持高电平单片机复位电容充满后电流走10k电阻到地RESET引脚回到低电平单片机开始运行。仿真里很多人直接不接复位电路也能跑但实物不接复位电路偶尔会出现上电程序状态不确定的情况所以不要省。2.2 DHT11接口电路与上拉电阻DHT11有四个引脚VCC接5V、GND接地、DATA接单片机IO、还有一个悬空脚原厂四脚封装里第四脚实际不连接。DATA引脚不是直接接单片机就完事它需要一个上拉电阻到VCC典型值是4.7k。上拉电阻的作用是保证总线空闲时处于高电平因为DHT11的通信方式是主机拉低总线发起通信、从机拉低总线应答如果没有上拉电阻总线释放后电平就悬空了。讲个小细节Proteus仿真里DHT11模型有时不加上拉也能跑但我建议仿真也把上拉画上和实物保持一致。上拉电阻在硬件电路里属于“看起来不起眼、没了就出鬼”的元件DHT11读取数据全是杂波八成是上拉问题。数据线连接单片机哪个引脚呢我习惯接P2.0避开P0口是因为P0口是开漏输出、需要额外上拉P2口是准双向口接普通传感器更方便。2.3 LCD1602显示电路P0上拉和对比度LCD1602的接口包括RS寄存器选择、RW读写选择、EN使能和8根数据线D0~D7。RS接P2.0、RW接P2.1、EN接P2.2、数据线接P0口这是最常见的接法程序和教程里的例程基本都对得上。RW引脚在只写不读的情况下可以直接接地省一个IO口但会失去读取忙标志的能力只能靠延时等待LCD处理完成。这里必须强调P0口上拉的问题。51单片机的P0口内部没有上拉电阻是开漏输出口直接接LCD1602的数据线高电平是拉不上去的LCD会乱码。解决办法是在P0口到VCC之间接一个10k排阻没有排阻用8个10k电阻也行这是实物必须有的电路。Proteus仿真里也要放上拉排阻否则显示异常。LCD1602的VL引脚是液晶对比度调节脚实物上必须接一个10k电位器进行分压调到一个合适电压一般是1V左右液晶才能显示出清晰的字。仿真里这个引脚直接接地通常就能显示但实物不调对比度屏幕上要么是白茫茫一片要么全是黑方块。2.4 蜂鸣器报警电路与三极管驱动的参数计算报警电路看起来只是接一个蜂鸣器实际上有个很关键的知识点51单片机的IO引脚驱动能力有限标准8051引脚拉电流只有几百微安灌电流也就20mA左右直接驱动蜂鸣器不仅声音小还可能烧坏引脚。所以必须用三极管做电流放大。我这次用的是NPN型三极管S8550或者9013也行典型接法是三极管基极通过4.7k电阻接单片机P2.3发射极接地集电极接有源蜂鸣器的负极蜂鸣器正极接VCC。当P2.3输出高电平时三极管导通蜂鸣器通电发声输出低电平时三极管截止蜂鸣器不响。电阻值怎么算单片机输出高电平约4.5V三极管基极-发射极压降约0.7V基极电阻上的压降就是4.5-0.73.8V除以4.7k电阻得到基极电流约0.8mA。S8550的直流放大倍数普遍在100以上集电极电流可达80mA驱动一个工作电流30mA左右的蜂鸣器完全够用。你要是把电阻换太大比如10k基极电流只有0.38mA三极管可能进入不了饱和区蜂鸣器声音发闷换太小比如1k基极电流偏大但也不会坏功耗大一点。4.7k是综合出来的稳妥值。2.5 完整物料清单按照上面的电路设计一份可以直接采购的物料清单如下。做仿真只需要单片机、传感器、液晶、电阻电容、蜂鸣器这些核心器件做实物还要备上杜邦线、面包板或PCB板、电源模块。序号元件型号/规格数量作用说明1单片机AT89C51 / STC89C521主控芯片STC89C52兼容且下载更方便2温湿度传感器DHT111采集温度和湿度单总线数字输出3液晶屏LCD16021两行十六字符显示温湿度数据4晶振12MHz1提供时钟信号5瓷片电容30pF2晶振负载电容6电解电容10uF/16V1复位电路用7电阻10kΩ1复位电路用8电阻4.7kΩ2DHT11上拉、蜂鸣器基极限流9排阻10kΩ 8脚1P0口上拉替代方案是8个10k电阻10电位器10kΩ1LCD1602对比度调节11三极管S85501蜂鸣器驱动的电流放大12有源蜂鸣器5V1报警发声13轻触按键6x6mm2可扩展用于调节报警阈值14电源USB 5V或5V适配器1系统供电15面包板/PCB按需1电路搭建载体有源蜂鸣器和无源蜂鸣器要注意区分。有源蜂鸣器内部带振荡电路通电就响单片机只需要输出高/低电平控制通断无源蜂鸣器需要单片机输出一定频率的方波才能发声。本设计用的是有源蜂鸣器买的时候一定看清楚别买错了。3. 软件设计从主流程到关键代码3.1 程序架构与主流程设计软件部分我用的是KEIL C51工程结构很常规一个主文件里包含DHT11驱动、LCD1602驱动和主循环。为什么不把DHT11驱动和LCD驱动拆成模块文件对这个规模的项目来说全部写在一个文件里更方便阅读和调试等以后项目大了再拆分也不迟。但函数要划分清楚让代码读起来有层次感。主程序流程图可以描述成这个样子上电后先调用LCD1602初始化函数设置液晶为8位模式、双行显示、开显示关光标。然后延时几百毫秒让DHT11上电稳定。进入主循环后第一步调用DHT11读取函数拿到温湿度数据第二步判断读取是否成功通过校验和判断第三步把数据转换成字符串用LCD显示函数输出到液晶屏第四步把温湿度与阈值比较在闹钟阈值之上就置位蜂鸣器引脚否则关闭蜂鸣器最后延时1秒进入下一轮循环。这里有个实践中的经验DHT11读取失败时不要影响显示可以保留上一次显示的数据或者显示错误提示。我采用的是读取失败就显示“-”占位这样调试时能从屏幕上直接看出传感器是否通信正常。3.2 DHT11时序驱动最容易翻车的部分DHT11的核心是单总线协议整个项目最有含金量的就是这段代码。如果你能不看例程、独立写出DHT11时序驱动说明对单片机时序控制已经入门了。我把这段拆开讲清楚。DHT11通信的第一个阶段是主机发送起始信号主机先把数据线拉低保持至少18毫秒然后释放总线。这个18毫秒是关键不能扣时间。DHT11检测到这个低电平后会等待一下再发出响应信号。第二步是DHT11响应它先把总线拉低80us表示收到起始信号然后拉高80us表示准备发送数据。主机检测到响应信号后就可以开始读取40位数据。数据位怎么区分0和1DHT11发送每一位数据时先把总线拉低50us然后拉高。如果高电平持续26到28us就结束这一位是0如果高电平持续70us这一位是1。主机只要在数据位开始后的40us左右采样总线电平低电平就是0高电平就是1。这就像两个人约好说话前先“嗯”一声之后话长的代表1话短的代表0。读取一个字节的代码可以这样写unsigned char DHT11_ReadByte(void) { unsigned char i; unsigned char dat 0; for (i 0; i 8; i) { while (DHT11_PIN 0); // 等待50us低电平结束 delay_us(40); // 数据位中间时刻采样 dat 1; if (DHT11_PIN 1) // 高电平还在说明是1 { dat | 0x01; while (DHT11_PIN 1); // 等待高电平结束准备读下一位 } } return dat; }注意while (DHT11_PIN 0)这个语句如果通信异常总线一直低电平程序会卡死在这里。严谨的做法是加超时判断但课程设计里通常不较真知道这个坑就行了。DHT11的40位数据由5个字节组成湿度整数、湿度小数、温度整数、温度小数、校验和。校验和等于前四个字节之和的低8位比如四字节之和是0x14D校验和就是0x4D。代码里必须校验否则任何一根线上的干扰都可能造成数据错误。我见过有人不校验直接把数据显示出来数据偶尔跳变还查了半天其实问题就出在没做校验。完整读取函数void DHT11_ReadData(unsigned char *temp_int, unsigned char *humi_int) { unsigned char buf[5] {0}; unsigned char i; DHT11_PIN 0; // 起始信号拉低总线 delay_ms(20); // 保持低电平20ms DHT11_PIN 1; // 释放总线 delay_us(30); // 等待DHT11响应 if (DHT11_PIN 0) // DHT11拉低总线开始响应 { while (DHT11_PIN 0); // 响应信号的低电平80us结束 while (DHT11_PIN 1); // 响应信号的高电平80us结束 for (i 0; i 5; i) { buf[i] DHT11_ReadByte(); } // 校验和判断 if ((buf[0] buf[1] buf[2] buf[3]) buf[4]) { *humi_int buf[0]; *temp_int buf[2]; } } }延时函数注意一点KEIL里软件延时要防止被编译器优化掉。简单的方法是把延时函数参数设为volatile或者用_nop_()这种内建指令实测下来用两个嵌套for循环基本没问题。我用的是12MHz晶振一个_nop_()时间是1us延时40us就写40个_nop_()但那样太累用for循环加示波器校准一下就好。3.3 LCD1602显示驱动与初始化时序LCD1602的初始化有固定的命定时序一般分四步写命令0x38设置8位接口、双行显示、5x7点阵写命令0x0C关闭光标写命令0x06设置地址自动加一写命令0x01清屏。之后就可以往数据区写要显示的字符了。液晶的写操作分两种写命令和写数据。区别在于RS引脚的电平RS为低电平时是写命令RS为高电平时是写数据。时序上先把RS和RW设置好再把数据放到P0口最后给EN一个下降沿液晶就把数据锁存进去了。下降沿就是在EN引脚上先高后低用一个短延时隔开。void LCD1602_WriteCmd(unsigned char cmd) { LCD_RS 0; LCD_RW 0; LCD_DATA cmd; delay_us(5); LCD_EN 1; delay_us(5); LCD_EN 0; } void LCD1602_WriteData(unsigned char dat) { LCD_RS 1; LCD_RW 0; LCD_DATA dat; delay_us(5); LCD_EN 1; delay_us(5); LCD_EN 0; }显示一行字符串的时候要注意第一行地址是0x80加上0x00第二行地址是0x80加上0x40。比如在第一行第一格显示字符就写命令0x80在第二行第一格显示就写命令0xC0。这个地址映射很多人记不住写程序时经常在第二行显示跑偏其实是地址算错了。显示温湿度时因为DHT11的数据是整数和小数分开的直接用两个字节拼成字符串就行。为了省RAM我用sprintf拼接字符串但51单片机RAM很小sprintf会占用不少资源。简单做法是手动拼dis_buf[0] T; dis_buf[1] :; dis_buf[2] temp_int / 10 0; dis_buf[3] temp_int % 10 0; dis_buf[4] 0xDF; // 度数符号 dis_buf[5] C;3.4 报警逻辑与阈值处理报警逻辑本身很简单但有几个设计决策值得说。阈值是写死在代码里还是通过按键调节这个课题标题没提按键所以我选择在代码顶部用宏定义写死#define TEMP_ALARM_H 30 // 温度上限30℃ #define HUMI_ALARM_H 80 // 湿度上限80%RH主循环里读取到温湿度后直接和阈值比较任何一个超过阈值就拉高蜂鸣器引脚否则拉低。这里有一个细节报警要加一个小的滞回区比如温度超过30℃开始报警降到28℃才停止报警防止传感器数值在阈值附近抖动导致蜂鸣器一响一停的“呼吸效应”。在简单课程设计里可以不做但知道了这个思路答辩的时候能加分。如果以后要扩展按键调阈值只需在主循环里扫描两个按键一个加一个减把阈值变量改为可变的unsigned int再在LCD第二行显示出来。这个扩展方向和原题目是兼容的想加分可以提前留好代码接口。4. Proteus仿真搭建与排错实录4.1 仿真工程搭建的完整流程我用的Proteus版本是8 Professional不同版本操作略有差异但核心流程一致。第一步新建工程在元器件列表里搜索放置以下元件AT89C51、DHT11、LM016L这就是LCD1602在Proteus库里的名称、RESPACK-8上拉排阻、RES、CAP、CAP-ELEC、CRYSTAL、BUTTON、BUZZER、NPN三极管。然后按原理图接线。这里提醒几处容易接错的地方AT89C51的EA引脚必须接VCC它决定单片机使用片内程序存储器不接的话仿真运行时程序根本不会执行XTAL1和XTAL2接晶振和负载电容复位引脚接复位电路P0口接排阻到VCC再接到LM016L的数据线。接线完成后进入KEIL编译工程。在KEIL里要设定输出HEX文件点Option for Target在Output选项卡勾选Create HEX File。编译之后到工程目录下找到.hex文件回到Proteus双击AT89C51单片机芯片在Program File里加载这个HEX文件设置晶振频率为12MHz点击运行。这时候LCD1602上应该能显示温湿度数据了。Proteus里的DHT11模型还挺有意思双击它可以调整温湿度数值或者通过模型自带的按钮模拟温湿度变化用来测报警功能很方便。实测下来温度调到设定值以上时蜂鸣器会立刻响响应非常及时。4.2 常见问题与排查速查表做这个项目过程中最耗时间的不是写代码而是排查那些看起来莫名其妙的现象。我整理了一个问题速查表很多问题是共通的。现象可能原因排查方法LCD1602无显示或白屏对比度引脚VL没调好初始化时序错误EN引脚接触不良先调10k电位器再查初始化命令最后检查EN引脚连接LCD显示黑色方块VL电压不对P0口没有上拉数据线接错调VL电压检查排阻是否连接核对D0-D7引脚顺序DHT11数据全为0或255起始信号延时不够上拉电阻没接数据线接错引脚检查起始信号延时是否达到18ms接上4.7k上拉核对DQ引脚程序总是卡死在DHT11读取时序不对导致总线电平一直低while循环没有超时用示波器查看DHT11波形在while循环里加超时计数蜂鸣器不响三极管接反基极电阻太大蜂鸣器选成无源的检查三极管E/C引脚换4.7k基极电阻确认是有源蜂鸣器仿真完全没反应HEX文件没加载EA引脚没接高电平晶振频率设置不对双击单片机确认HEX路径EA接VCC设置晶振12MHz编译成功但没有HEX文件KEIL输出设置没勾选HEXOption for TargetOutput选项卡勾选Create HEX File4.3 我踩过的一个典型坑DHT11模型在不同Proteus版本里的差异有一件事想单独说一下。Proteus不同版本里DHT11这个元件库差异比较大有些版本里直接搜DHT11就能找到有的版本搜出来叫DHT11 Sensor还有的版本可能根本搜不到只能找替代模型。我遇到过模型坐标引脚顺序和实物不一样的情况接线时按数据手册的四脚定义接仿真却怎么都不通后来查了模型说明才发现引脚顺序不同。所以搭建仿真前建议双击DHT11模型看一下引脚定义别想当然。Proteus里DHT11模型通常有两个状态可以在属性里调整双击后在元件属性里或者通过仿真按钮可以改变温度值有的版本还支持通过外部引脚输入模拟信号。如果实在找不到DHT11可以用DS18B20做温度部分的仿真替代练手但项目的完整性会打折。DHT11时序在仿真里还有一个特点仿真模型的时序宽容度比较大如果程序在某些细节上写得不够标准实物会上不了电仿真却能跑通。比如起始信号我写成10ms实物DHT11可能不响应但Proteus依然能读出数据。所以仿真通过后做实物时一定不要随便缩减延时。4.4 从仿真到实物的几点提醒如果你打算在仿真跑通之后做实物提前说几个心得。第一面包板接线前先用万用表测一下电源确认5V输出正常有些USB供电设备电压不足4.5VDHT11会工作不稳定。第二LCD1602的对比度电位器是必调的实物不像仿真VL接地就能显示必须调到合适阻值才能看清字符。第三蜂鸣器焊接时注意三极管引脚S8550的CBE排列和9013不一样装反了蜂鸣器怎么都不响。第四DHT11数据线在实物上尽量短避免在面包板上飞太长线引入干扰。我做过一次数据线悬空二十多厘米读数偶尔跳变缩短后问题消失。第五P0口的排阻一定不能省实物上如果P0口没上拉LCD显示的数据会缺笔画甚至花屏。最后再说一个扩展方向。这个项目如果觉得内容不够深可以在现有基础上加两个按键调阈值加一个继电器控制风扇或加湿器还可以把报警数据通过蜂鸣器编码成不同的响法。我在实际做的时候发现DHT11单总线时序一旦吃透再去碰DS18B20、甚至IIC总线的传感器都会顺手很多所以花时间把这段时序代码研究明白非常值得。本文还有配套的精品资源点击获取