资讯动态

基于51单片机的DHT11温湿度监控系统设计与实现(含报警/串口)

发布时间:2026/9/8 23:41:22 来源:尧图企业网站定制
简介资源包内含基于51单片机的温湿度采集监控完整工程适合电子、嵌入式方向的初学者或开发者学习环境监测系统设计。工程以SHT10温湿度传感器配合DS1302日历时钟通过12864液晶屏显示当前温湿度与时间并实现调时及温湿度超标报警功能。压缩包共28个文件约84KB包含C语言源文件、头文件、Keil工程文件以及编译生成的hex、lst、obj等文件各功能模块分文件组织便于直接导入Keil进行编译和烧录也方便对照学习模块化编程。当前已有352人学习浏览。通过该资源可以掌握51单片机与SHT10传感器的通信时序、DS1302时钟芯片的读写方法、12864液晶屏的显示驱动以及温湿度阈值判断与报警电路的控制逻辑是一份可直接运行参考的入门级实践项目。对于课程设计或技能竞赛备赛这份资源能提供从传感器读取到显示、报警的完整代码框架帮助快速理解系统整体流程。 51单片机玩温湿度监控其实比想象中简单很多。前阵子帮一个朋友搭了一套环境监测的小系统核心就一片STC89C52、一个DHT11、一块LCD1602加上蜂鸣器和几个电阻电容就能实时采集温湿度、超限报警还能通过串口把数据发给上位机。整套下来成本不超过三十块钱但把单片机的外围电路、时序协议、显示驱动都给过了一遍。这篇文章就从选型、硬件搭建、编码到调试验证把整个过程完整拆开来讲适合正在学51、或者准备拿来做课程设计和毕业设计的同学参考。1. 整体设计思路为什么拿51来做温湿度监控1.1 这个项目到底解决了什么问题温湿度采集监控这个事在环境实验、仓储管理、农业大棚、机房运维甚至家里的地下室都算得上高频需求。但从技术角度看它最有价值的地方不是“测温度”本身而是把单片机开发里最常见的几大模块全串起来了传感器数据读取、时序协议解析、显示驱动、阈值判断和报警输出、异步串口通信。也就是说一个温湿度监控项目实际上就是一个综合练兵项目。用一个简单的数字温湿度传感器做数据源用51单片机做主控把结果显示在液晶屏上用按键设置上下限超限就驱动蜂鸣器报警同时通过串口把数据传到电脑上做记录。这样一套系统既能练到GPIO操作又能练到时序设计还能练到中断、定时器、串口通信——这些知识几乎是后续所有单片机开发的底座。1.2 方案选型的标准是什么我做方案选型时主要看三点是否容易上手、资料是否够多、能不能把核心知识点都覆盖到。基于这三点主控选择了STC89C52而非STM32。虽然STM32性能更强但对于刚入门的人来说51的寄存器数量少、中断系统简单、IO操作直白写代码时不需要面对复杂的时钟树配置和库函数封装可以更专注理解底层逻辑。传感器方面DHT11虽然测量精度比不上SHT30或者BME280但它使用的是单总线协议在一根数据线上完成时序握手和40bit数据传输这个过程本身就非常有学习价值。而且DHT11的温湿度数据带有校验和字段让初学者能接触数据完整性校验的概念。显示器件选LCD1602因为这个模块的驱动时序清晰、指令集固定网上例程极多出了问题也好排查。2. 硬件设计每个元器件的选型理由2.1 核心器件清单搭建一套完整可用的温湿度监控系统需要以下器件器件型号/规格数量作用主控单片机STC89C52 / AT89S521系统控制核心温湿度传感器DHT111采集温湿度数据液晶显示屏LCD1602带转接板或直插1实时显示数据蜂鸣器有源蜂鸣器 5V1超限声音报警按键轻触开关 6x6mm2设置报警上下限晶振11.0592MHz1提供外部时钟电容30pF 瓷片电容2晶振负载电容电解电容10uF / 22uF1复位电路定时电阻10kΩ、4.7kΩ、1kΩ、330Ω若干上拉/分压/限流排针/杜邦线若干无数连接各模块DHT11选择直插封装焊接方便引脚间距2.54mm可以直接插到洞洞板或者面包板上。这里提醒一下市面上很多便宜的DHT11模块已经集成了上拉电阻和滤波电容买模块比买裸传感器省事很多。但如果是做课程设计需要画原理图和PCB建议直接用裸传感器把上拉电阻和滤波电容自己画上去这样更规范答辩时也能讲出细节。2.2 引脚分配与接线逻辑51单片机的P0口和P2口是这次系统的主力我选用的引脚分配方案如下引脚功能连接目标P2.0单总线数据DHT11 DATAP0.0 - P0.7LCD数据总线LCD1602 D0 - D7P2.1LCD寄存器选择LCD1602 RSP2.2LCD读写选择LCD1602 RWP2.3LCD使能信号LCD1602 EP2.4蜂鸣器控制蜂鸣器驱动三极管基极P3.2INT0按键1输入模式切换/设定值增加P3.3INT1按键2输入设定值减少/确认P3.1TXD串口发送上位机/调试助手P0口在这个系统里必须加上拉电阻这一点很多人容易忽略。51的P0口是开漏输出内部没有上拉电阻直接驱动LCD1602时高电平输出能力很弱会造成液晶显示出现乱码、缺笔画甚至完全不显示。我在P0口上加了4.7kΩ排阻问题一次性解决。2.3 原理图搭建与最小系统设计整套系统的最小系统部分实际上就是51单片机的经典三件套晶振电路、复位电路、电源滤波。晶振用11.0592MHz配合两个30pF负载电容原因是这个频率经过串口波特率发生器分频后可以精确得到9600、4800等标准波特率而不会产生累积误差这样和电脑串口通信时就不会出现乱码。复位电路用经典的10uF电解电容加10kΩ电阻方案上电瞬间电容充电RST引脚维持一段时间的高电平让单片机完成复位动作。因为按键是接在外部中断引脚上我还加了10kΩ上拉电阻保证空闲时引脚为确定的高电平按下接地产生下降沿触发中断。DHT11的数据线是重点需要在DATA和VCC之间接一个4.7kΩ左右的上拉电阻。这个电阻不是可有可无的装饰DHT11采用的是单总线协议这条线上的状态切换本来就需要依靠主机和从机共同配合主机释放总线时靠上拉电阻把电平拉高从机拉低表示应答没有一个可靠的上拉时序波形的边沿会变得很钝轻则读到的数据偶尔跳变重则直接读不到数据。3. 软件实现核心逻辑与完整的驱动代码3.1 DHT11单总线协议解读DHT11的数据帧结构是这样的1字节湿度整数部分 1字节湿度小数部分 1字节温度整数部分 1字节温度小数部分 1字节校验和。每帧数据总共40bit高位在前一次完整通信传输一组温湿度数据。通信流程分三个阶段主机发起起始信号、DHT11响应、数据位读取。主机先把总线拉低至少18ms再释放这是给DHT11的唤醒信号然后就释放总线DHT11会主动拉低80us作为应答响应接着拉高80us表示准备发送数据。之后每个数据位都是通过高电平持续时间的长短来区分逻辑“0”和逻辑“1”50us左右就是070us左右就是1。这个时序最关键的技巧在于判断逻辑值的时候主机的采样点应该放在高电平阶段的中后段也就是大约40us到50us的位置。我在写驱动的时候代码处理逻辑是先等引脚变高然后延时40us再去读引脚状态。如果引脚这时仍然是高电平说明这是一个“1”否则就是“0”。这个采样时机的处理是保证DHT11读取稳定的核心。3.2 源代码实现从延时函数到主循环先写一个微秒级的延时函数。因为51单片机是12T模式晶振11.0592MHz单机器周期大约是1.085us一个空循环大概需要若干周期我一般用示波器标定过几次之后得到一个经验值参数。下面是完整实现#include reg52.h #include intrins.h #define uchar unsigned char #define uint unsigned int sbit DHT11_DATA P2^0; sbit LCD_RS P2^1; sbit LCD_RW P2^2; sbit LCD_E P2^3; sbit BEEP P2^4; uchar temp_int, temp_dec, humi_int, humi_dec; uchar temp_limit_high 30; // 温度上限 uchar temp_limit_low 5; // 温度下限 uchar humi_limit_high 80; // 湿度上限 uchar humi_limit_low 20; // 湿度下限 void delay_us(uint us) { while(us--) { _nop_(); } } void delay_ms(uint ms) { uint i, j; for(i ms; i 0; i--) for(j 110; j 0; j--); }接下来是DHT11驱动部分。整个流程严格按照上面讲的时序来走读取完毕之后把校验和数据对比校验通过才更新全局变量否则保留上次有效数据bit DHT11_Read() { uchar i, j, temp; uchar buf[5] {0, 0, 0, 0, 0}; // 主机发送起始信号 DHT11_DATA 1; delay_us(2); DHT11_DATA 0; delay_ms(20); // 拉低至少18ms DHT11_DATA 1; delay_us(30); // 上拉后等待响应 // 检测从机响应 if(DHT11_DATA 0) { while(DHT11_DATA 0); // 等待80us低电平结束 while(DHT11_DATA 1); // 等待80us高电平结束 // 读取40bit数据 for(j 0; j 5; j) { for(i 0; i 8; i) { while(DHT11_DATA 0); // 等待低电平结束 delay_us(40); // 关键采样点 if(DHT11_DATA 1) { // 如果40us后仍为高 temp 1; // 这是逻辑1 while(DHT11_DATA 1); // 等待高电平结束 } else { temp 0; // 逻辑0 } buf[j] (buf[j] 1) | temp; } } // 校验和验证 if((buf[0] buf[1] buf[2] buf[3]) buf[4]) { humi_int buf[0]; humi_dec buf[1]; temp_int buf[2]; temp_dec buf[3]; return 1; // 读取成功 } } return 0; // 读取失败 }这段代码里值得注意的有一个细节起始信号的拉低时间我写成20ms比协议要求的18ms留了一点余量。这样做的原因是有些DHT11批次对唤醒时间的敏感度不一样多给一点时间能让传感器更稳定地进入工作状态代价只是整个读取周期多了2ms而已对采集频率没有影响。3.3 报警判断逻辑与主函数组织报警逻辑不能太“急”刚上电或者传感器读数抖动时容易误报。我在判断条件里加入了一个确认机制连续三次采样都超限才真正触发报警。用小变量计数代替直接动作省去了复杂的滤波算法工程实现简单且实测有效void check_limit() { static uchar over_count 0; bit over_flag 0; if(temp_int temp_limit_high || temp_int temp_limit_low || humi_int humi_limit_high || humi_int humi_limit_low) { if(over_count 3) { over_flag 1; } } else { over_count 0; } if(over_flag) { BEEP 0; // 有源蜂鸣器低电平触发 } else { BEEP 1; } }主循环的逻辑比较直接上电初始化LCD和串口进入循环后每2秒读一次DHT11读到有效数据就刷新LCD并做报警判断同时把数据通过串口发送给上位机。2秒的采样间隔是有讲究的——DHT11本身的响应时间是1秒左右如果采样频率太高传感器可能还没有完成内部刷新读到的数据会有很长一段重复实际没有意义。这里给出LCD1602的初始化函数采用标准的8线模式操作流程是固定的功能设置、显示开关控制、清屏、写入模式设置void LCD_WriteCmd(uchar cmd) { LCD_RS 0; LCD_RW 0; P0 cmd; LCD_E 1; delay_us(50); LCD_E 0; } void LCD_WriteData(uchar dat) { LCD_RS 1; LCD_RW 0; P0 dat; LCD_E 1; delay_us(50); LCD_E 0; } void LCD_Init() { delay_ms(15); LCD_WriteCmd(0x38); // 8位模式2行显示5x7点阵 LCD_WriteCmd(0x0C); // 显示开光标关闪烁关 LCD_WriteCmd(0x06); // 写入后光标右移 LCD_WriteCmd(0x01); // 清屏 delay_ms(2); }LCD1602的时序参数网上到处都是但实际用下来E引脚的高低电平持续时间不能太短我在代码里加了50us的延时。51单片机速度不快不加延时有时候也能跑但加了这个延时之后在工业环境和长线缆情况下会稳定很多。串口发送部分使用TI中断标志轮询模式简单直接适用于数据量小的场景void UART_Init() { TMOD 0x20; // 定时器1工作在方式2 TH1 0xFD; // 波特率9600 TL1 0xFD; SCON 0x50; // 串口方式1允许接收 TR1 1; } void UART_SendByte(uchar dat) { SBUF dat; while(!TI); TI 0; } void UART_SendReport() { UART_SendByte(0xAA); // 帧头 UART_SendByte(temp_int); UART_SendByte(humi_int); UART_SendByte(0x55); // 帧尾 }4. 常见问题与排查技巧实录4.1 传感器读到的数据总是0xFF或者固定值这个是我被问过最多的问题而且绝大多数情况下不是因为代码不对而是硬件连接问题。DHT11的数据引脚如果没有正确接上拉电阻或者杜邦线接触不良单片机的引脚会一直悬空读出来的数据就会是随机的噪声或者是固定的0xFF。排查步骤先用万用表量一下DATA引脚的静态电压正常情况下总线空闲时应该是3.3V到5V的高电平如果量出来电压很低或者接近0V说明上拉电阻没接好或者传感器已经被拉低了。还有一种情况是DHT11本身损坏判断方法很简单把DATA线直接断开量一下VCC和GND之间的电压正常在4.8V到5.2V之间如果差太多说明供电有问题。4.2 LCD1602显示乱码或者只有第一行亮LCD1602显示乱码最常见的原因是P0口没有加上拉电阻。我用过不少51开发板有的板子已经集成了排阻有的没有如果直接接裸的LCD模块就会出现显示随机字符、笔画错乱的现象。解决办法就是加4.7kΩ排阻或者8个独立电阻。另一个容易忽略的问题是LCD1602的对比度引脚V0。这个引脚不能直接接地否则对比度太高屏幕上会看到满屏的小方块什么都看不清。正确的做法是V0接一个10kΩ电位器调节分压到合适位置。对比度调好的标准是背景微微透亮但字符清晰可辨没有底色干扰。4.3 蜂鸣器误报或者不响蜂鸣器误报大多数是阈值判断逻辑里没有加确认机制传感器偶发跳变一次就触发报警。前面代码里的连续三次确认机制就是解决这个问题的。另外一个常见误区是该用有源蜂鸣器却买了无源蜂鸣器有源蜂鸣器内部自带振荡电路给个低电平就能响无源蜂鸣器需要外部给一定频率的方波才会发声直接用直流电平驱动只会“咔”一声或者完全无声。如果确认是有源蜂鸣器但不响检查驱动电路。直接用单片机引脚驱动蜂鸣器有时候就是因为电流不够而不够响我用NPN三极管S8050做开关管来驱动基极通过1kΩ限流电阻接P2.4发射极接地集电极接蜂鸣器负极蜂鸣器正极接VCC。这种电路的好处是单片机的IO口只提供微小基极电流三极管导通后蜂鸣器直接从电源取电音量有保障。4.4 温湿度数据偶尔跳变严重这个问题往往和电源质量有关。DHT11对电源纹波比较敏感如果和继电器、电机或者其它大功率器件共用一个电源启动瞬间的电压跌落会导致采集数据出现离谱的跳变。解决思路有两个一是DHT11的VCC和GND之间加一个10uF的电解电容和0.1uF的瓷片电容做去耦二是如果系统有电机或继电器尽量分开供电或者用光耦隔离。还有一个我踩过的坑DHT11的引线不能太长超过20cm就开始在时序边缘出现问题。因为单总线协议依赖精确的上升沿时间长导线带来的分布电容会拖慢电平转换速度导致单片机的采样点判断错位。实在需要远程采集用I2C接口的传感器比如SHT30加延长线会更可靠抗干扰能力不一样。4.5 串口接收数据丢失或者乱码串口乱码先看波特率。11.0592MHz的晶振配9600波特率几乎不会出现误差如果换成了12MHz晶振就要仔细算一下分频误差很可能导致通信不稳定。另外确认单片机的发送引脚TXD和USB转串口模块的RXD相连别接反了。接收端丢数据则要检查协议设计。我的做法是使用固定帧格式帧头0xAA、数据字节、帧尾0x55上位机通过判断帧头帧尾来提取数据。这个办法虽然简单但比裸发数据要可靠得多因为每次读取数据都从完整的帧结构开始解析半包数据会被主动丢弃。5. 一些实测心得与系统优化建议整套系统实跑下来2秒一次的采样周期连续运行72小时温度数据误差基本稳定在正负1摄氏度以内湿度误差在正负百分之5左右这个精度对于常规环境监控完全够用。DHT11的上拉电阻我用过10kΩ和4.7kΩ两种4.7kΩ的波形边沿更陡抗干扰表现更好一些但功耗略高这个取舍看具体应用场景。如果你想把系统做得更贴近真实产品有几个扩展方向值得考虑。一是加入掉电保存功能用AT24C02存储报警阈值这样断电之后设置不丢失而不是每次上电都恢复默认值。二是加入时间戳功能用DS1302时钟模块给每条记录打上时间标签配合串口做历史数据回放。三是把LCD1602换成OLED屏I2C接口占用引脚少显示信息量也能更大可以直接画出温湿度曲线。在做这个项目的过程中我自己最深的体会是单片机的任何功能模块都不能只看原理必须要把时序图和实际波形对应起来理解。DHT11的那40bit数据如果用示波器挂上看一眼高低电平的形态和时序图上的描述一模一样瞬间就能明白采样点为什么这么放。如果没有示波器用逻辑分析仪也行几十块钱的8通道逻辑分析仪对学习数字时序非常有用。等你把这条时序吃透了再去学DS18B20、SPI接口的Flash存储、或者I2C的OLED屏会发现它们的套路都是类似的只是电平标准和传输格式不同。这就是做温湿度监控项目最大的收获。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价