☰
51单片机DHT11温湿度资源包:Keil代码+Proteus仿真+Word文档
2026/10/28 7:21:34 网站建设 项目流程

简介:这份资源面向单片机与嵌入式入门学习者,围绕DHT11温湿度传感器、51单片机和LCD1602液晶显示,构建了一套完整的环境监测与超温报警实践方案。项目通过C语言程序读取温湿度数据并实时显示,同时设置温度阈值,超限时驱动蜂鸣器发声、红灯点亮,适用于温室、仓库等温度敏感场景的监控学习。压缩包共22个文件,约324KB,包含c与h源码、hex烧录文件、uvproj与uvopt工程配置、pdsprj仿真工程、lst与obj编译中间文件,以及一份实验报告docx,覆盖代码、仿真与文档三类材料。目前已有1093人学习下载。读者可据此掌握传感器数据采集、LCD显示驱动、阈值判断与报警控制的完整实现流程,并借助仿真工程验证硬件连接与程序逻辑,是学习51单片机编程与物联网应用开发的实用案例。

1. 从一块 DHT11 说起:这套单片机温湿度资源到底能帮你省下多少时间

如果你正在做 51 单片机课程设计、毕业设计或者电子竞赛入门题,大概率绕不开温湿度采集这个经典命题。DHT11 便宜、时序简单、资料满天飞,但真正动手时你会发现:网上代码一抓一大把,能一次跑通的没几个。要么时序延时对不上,要么 LCD1602 显示乱码,要么 Proteus 仿真里 DHT11 根本不响应。这套「基于单片机控制温湿度(代码+仿真+word)」的资源,本质上就是把这套流程里最耗时的三块——可编译的 Keil C 代码、能直接跑的 Proteus 仿真工程、以及配套的 Word 设计文档——打包好了。它适合三类人:赶课设 deadline 的学生、想快速验证 DHT11 时序的嵌入式新手、以及需要一份完整文档模板的从业者。下面我按实际拆包和复现的顺序,把这份资源里里外外讲清楚。

2. 拆开资源包:代码、仿真、文档三件套各自解决什么问题

2.1 代码部分:Keil C51 工程结构与 DHT11 驱动逻辑

拿到资源后,代码部分通常是一个标准的 Keil uVision 工程目录,里面至少包含main.c、dht11.c、dht11.h、lcd1602.c、lcd1602.h以及.uvproj工程文件。我一般会先看dht11.c里的时序函数,因为这是整个项目最容易翻车的地方。DHT11 是单总线协议,一次完整通信大概 4ms 左右,对延时精度要求比较高。常见的实现方式是用delay_us()做微秒级延时,但 51 单片机的晶振频率直接决定延时循环次数,所以代码里一定会有一个宏定义或者注释说明晶振频率。

// dht11.c 典型时序片段(晶振 11.0592MHz 或 12MHz) void DHT11_Start(void) { DHT11_IO = 0; // 主机拉低总线 delay_ms(20); // 保持至少 18ms,保证 DHT11 能检测到起始信号 DHT11_IO = 1; // 主机释放总线 delay_us(30); // 等待 20-40us,让 DHT11 拉低响应 } // 读取一个字节,返回值就是温湿度数据的高低位 unsigned char DHT11_ReadByte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { while (!DHT11_IO); // 等待 50us 低电平结束 delay_us(40); // 延时 40us 后采样,区分数据 0 和 1 dat <<= 1; if (DHT11_IO) dat |= 1; while (DHT11_IO); // 等待剩余高电平结束 } return dat; }

这段代码的逻辑说明:DHT11_Start()负责发起通信,拉低 20ms 是硬性要求,短了 DHT11 不认。DHT11_ReadByte()的核心在于那个delay_us(40),DHT11 用高电平持续时间区分 0 和 1——26-28us 表示 0,70us 表示 1,采样点放在 40us 处刚好能区分开。参数方面,如果你用的是 12MHz 晶振,delay_us的循环次数要重新算;11.0592MHz 和 12MHz 的误差在 DHT11 容忍范围内,但 24MHz 或更高就必须调整。我见过有人直接抄了 STM32 的 DHT11 代码改 51,结果延时完全不对,读出来全是 0xFF,这就是没看晶振匹配的典型翻车。

2.2 仿真部分:Proteus 工程里 DHT11 模型怎么接才不报错

Proteus 里的 DHT11 模型和实物有一点差异:仿真模型对时序的容忍度比实物低,尤其是起始信号拉低时间如果不够 18ms,仿真里会直接不响应。资源包里的.dsn文件通常已经接好了 DHT11、LCD1602 和单片机最小系统。你需要确认三件事:第一,DHT11 的数据脚是否接在代码里定义的 IO 口上,比如P2^0或P3^7;第二,LCD1602 的 RS、RW、E 三个控制脚是否和代码一致;第三,单片机型号是否选对,AT89C51 和 AT89C52 在仿真里差别不大,但 STC 系列需要额外加载库。

# Proteus 仿真前检查清单(我一般按这个顺序过一遍) 1. 打开 .dsn 文件,双击单片机,确认 Program File 指向编译出的 .hex 2. 双击 DHT11,确认 Data 引脚网络标号和代码里 #define DHT11_IO P2^0 一致 3. 检查晶振频率设置:双击单片机,Clock Frequency 填 11.0592MHz 或 12MHz 4. 运行仿真,看 LCD1602 第一行是否显示 "Temp: XX C" 第二行 "Humi: XX %"

这里有个血泪经验:Proteus 8.9 之后的版本对 DHT11 模型的响应速度做了调整,如果你用的是老版本.dsn文件,可能会遇到仿真里温湿度值一直不变的情况。解决办法是右键 DHT11 属性,把Timing里的Response Time调成默认值或者稍微加大一点。另外,仿真里 DHT11 的初始值通常是固定的,比如 25°C、60%RH,不会像实物那样随环境变化,这是正常现象,别以为是代码读错了。

2.3 文档部分:Word 设计报告里哪些章节可以直接复用

Word 文档一般是课程设计报告的标准结构:摘要、方案论证、硬件设计、软件设计、仿真结果、心得体会。其中硬件设计里的最小系统电路图、DHT11 接口电路、LCD1602 显示电路,以及软件设计里的主程序流程图、DHT11 时序图,都是可以直接拿来用的。但要注意,文档里的代码截图可能和你实际编译的版本有细微差异,比如 IO 口定义不同,这时候要么改代码,要么改文档,别让两者对不上。我一般会先把代码跑通,再根据实际代码去修正文档里的引脚描述和流程图,这样答辩的时候不会被问住。

3. 从零复现:Keil 编译、Proteus 联调、实物验证的完整链路

3.1 Keil 工程编译与 HEX 文件生成

拿到代码后第一步是编译。打开.uvproj文件,Keil 会提示器件型号,如果你没装对应的 Device Family Pack,可能会报错。51 单片机一般选Atmel下的AT89C51或AT89C52,如果代码里用了 STC 的扩展寄存器,那就得选 STC 系列并安装 STC 的 Keil 支持包。编译前检查Options for Target里的Output选项卡,勾选Create HEX File,否则 Proteus 找不到可加载的程序。

// main.c 主循环典型结构 void main(void) { unsigned char temp, humi; LCD1602_Init(); // LCD 初始化,必须放在最前面 while (1) { if (DHT11_ReadData(&temp, &humi) == 0) { // 读取成功返回 0 LCD1602_ShowTempHumi(temp, humi); // 刷新显示 } delay_ms(1000); // 采样间隔至少 1s,DHT11 响应速度有限 } }

逻辑说明:DHT11_ReadData内部会调用起始信号、读取 40 位数据、校验和验证。如果校验失败,函数返回非 0,主循环里可以选择不刷新显示,保留上一次的有效值。参数方面,delay_ms(1000)是采样间隔,DHT11 datasheet 建议不低于 1s,实际用 2s 更稳。编译成功后,在Objects目录下会生成.hex文件,记住这个路径,下一步要用。

3.2 Proteus 加载 HEX 与仿真参数设置

Proteus 里双击单片机,在Program File栏选择刚才生成的.hex,Clock Frequency填和你代码里delay_us匹配的晶振频率。如果你不确定代码是按哪个频率写的,可以看dht11.h里的注释,或者直接试 11.0592MHz 和 12MHz 两个值,哪个读出来数据正常就用哪个。运行仿真后,LCD1602 应该显示温湿度值。如果显示乱码,先检查 LCD1602 的初始化代码里0x38、0x0C、0x06这几个指令有没有写对;如果 DHT11 一直读失败,把仿真速度调慢一点,Proteus 默认的仿真速度有时候跑不过 DHT11 的微秒级时序。

# Proteus 仿真调试常用操作 1. 菜单 Debug -> Start/Stop Video Recording 可以录仿真过程,方便回看时序 2. 右键 DHT11 -> Edit Properties -> 可以手动设置温湿度值,测试显示逻辑 3. 如果 LCD 不亮,检查 VSS、VDD、V0 三个脚:VSS 接地,VDD 接 5V,V0 接电位器中间脚

这里有个玄学问题:同样的代码和仿真文件,在别人电脑上跑得好好的,到你这里就是不行。大概率是 Proteus 版本差异或者系统区域设置影响了小数点显示。我一般会先把仿真文件里的 DHT11 换成手动设置的固定值,确认 LCD 显示正常,再换回 DHT11 模型排查时序问题。这样能把问题范围缩小到 DHT11 驱动本身,而不是整个系统。

3.3 实物搭建:最小系统、上拉电阻与电源去耦

如果你要焊实物,51 单片机最小系统包括晶振电路、复位电路、电源。DHT11 的数据脚需要接一个 4.7kΩ 到 10kΩ 的上拉电阻,因为 DHT11 是开漏输出,没有上拉电阻读出来全是高电平。LCD1602 的对比度调节电位器不能省,否则屏幕一片白或者一片黑。电源部分,DHT11 和 LCD1602 共用 5V,但最好在 DHT11 的 VCC 和 GND 之间并一个 0.1uF 的去耦电容,否则温湿度读数会跳变。

// 实物调试时可以在 main.c 里加一段串口打印,方便看原始数据 void UART_Init(void) { TMOD |= 0x20; // 定时器1 工作方式2 TH1 = 0xFD; // 波特率 9600(11.0592MHz) TL1 = 0xFD; TR1 = 1; SCON = 0x50; // 串口方式1,允许接收 } // 在 DHT11_ReadData 成功后调用 printf("Temp=%d, Humi=%d\r\n", temp, humi);

逻辑说明:串口打印不是必须的,但在实物调试阶段非常有用。你可以通过串口助手看到 DHT11 返回的原始温湿度值,和 LCD 显示的值对比,判断是传感器问题还是显示问题。参数方面,TH1和TL1的值对应 9600 波特率,如果你用 12MHz 晶振,这个值要改成0xF9或0xFA,否则串口输出乱码。上拉电阻我一般用 4.7kΩ,实测比 10kΩ 更稳定,尤其是杜邦线较长的时候。

4. 避坑与排查:DHT11 读数为零、LCD 乱码、仿真不响应的常见原因

4.1 现象:DHT11 读出来温度湿度全是 0 或者 255

原因:最常见的是延时函数不匹配。51 单片机的delay_us通常是软件循环,晶振频率变了,循环次数没变,实际延时就会差很多。另一个原因是数据脚没有上拉电阻,或者上拉电阻太大,导致总线释放后电平拉不上去。还有一种情况是 DHT11 供电不足,USB 转串口模块的 5V 输出电流不够,DHT11 工作不稳定。

解决:先确认晶振频率和代码里的延时是否匹配,用示波器或者逻辑分析仪看数据脚波形,起始信号拉低时间是否达到 18ms,DHT11 响应的低电平是否出现。如果没有仪器,就换一个已知能跑的延时函数,或者直接把晶振换成代码里注释的频率。上拉电阻换成 4.7kΩ,供电单独用稳压电源或者开发板上的 5V。

4.2 现象:LCD1602 显示乱码或者只亮背光不显示字符

原因:初始化顺序不对,或者忙标志没有判断。LCD1602 上电后需要延时一段时间再发指令,有些代码里LCD1602_Init()前面没有加delay_ms(15),导致初始化失败。另外,数据口和代码里的定义不一致也会乱码,比如代码用P0,实际接的是P2。

解决:在LCD1602_Init()最前面加delay_ms(15),然后依次发送0x38、0x0C、0x06、0x01。检查lcd1602.h里的#define LCD_DATA_PORT P0和实际接线是否一致。如果还是乱码,把对比度电位器调一下,有时候只是对比度不对,字符其实已经显示了。

4.3 现象:Proteus 仿真里 DHT11 不响应,LCD 一直显示初始值

原因:Proteus 的 DHT11 模型对时序要求比实物严格,尤其是起始信号拉低时间。另外,仿真速度设置太快,微秒级延时在仿真里被压缩,DHT11 模型来不及响应。还有一种情况是.dsn文件里的 DHT11 模型版本和 Proteus 版本不兼容。

解决:双击 DHT11,把Timing里的Response Time调大一点,或者把仿真速度调到最慢。如果还是不行,在 Proteus 里换一个 DHT11 模型,或者直接用DS18B20替代测试显示逻辑。我遇到过最坑的一次是 Proteus 8.13 里 DHT11 模型有 bug,换成 8.9 版本就正常了,所以版本兼容性也得留意。

4.4 现象:编译通过但 HEX 文件加载后单片机不运行

原因:Keil 工程里选的单片机型号和 Proteus 里的不一致,比如 Keil 选的是 AT89C52,Proteus 里用的是 AT89C51,寄存器地址有差异。或者Options for Target里的Start地址不对,有些工程默认从0x0000开始,但代码里用了中断向量偏移。

解决:统一 Keil 和 Proteus 里的单片机型号。检查Options for Target->Target选项卡里的Off-chip Code memory和Off-chip Xdata memory设置,一般 51 单片机不需要改。如果代码里用了interrupt关键字,确认中断向量地址没有被占用。

4.5 现象:实物运行一段时间后温湿度值漂移或者跳变

原因:DHT11 对电源噪声敏感,尤其是和 LCD1602、电机等负载共用电源时。另外,DHT11 的采样间隔太短,内部电容还没恢复,读出来的值会偏高或偏低。

解决:在 DHT11 的 VCC 和 GND 之间并一个 0.1uF 陶瓷电容,电源走线尽量短。采样间隔从 1s 改成 2s 或更长。如果还是漂移,把 DHT11 用延长线远离单片机和其他发热元件,DHT11 本身对温度响应有滞后,这是正常现象。

5. 进阶玩法:把 DHT11 数据推到串口屏、上位机和多机通信

5.1 用串口把温湿度发到上位机并画曲线

如果你已经不满足于 LCD1602 显示,想把数据记录下来或者画成曲线,最直接的办法是用串口发送到电脑。51 单片机通过 UART 发送格式化的字符串,电脑端用串口助手或者 Python 脚本接收并解析。我一般会在main.c里加一个printf重定向,把TI和SBUF关联起来,这样就能直接用printf输出温湿度值。

// 串口发送重定向,配合 printf 使用 char putchar(char c) { SBUF = c; while (!TI); TI = 0; return c; } // 主循环里 printf("T:%d,H:%d\r\n", temp, humi);

逻辑说明:putchar是标准库函数,Keil C51 里重定向后printf就会走串口。参数方面,波特率 9600 足够,数据格式T:25,H:60方便解析。电脑端可以用 Python 的pyserial库读取串口,用matplotlib实时画曲线。这个玩法适合做课设的「数据上位机显示」加分项,代码改动量很小,但效果很直观。

5.2 多机通信:两块 51 单片机通过串口互传温湿度

如果你想做多点温湿度监测,可以用两块 51 单片机,一块采集 DHT11 并通过串口发送,另一块接收并显示。通信协议可以自定义,比如0xAA作为帧头,后面跟温度、湿度、校验和。发送端和接收端的波特率必须一致,晶振频率也要一致,否则累计误差会导致通信失败。

// 发送端帧格式:0xAA + temp + humi + checksum void Send_Frame(unsigned char temp, unsigned char humi) { unsigned char sum = temp + humi; SBUF = 0xAA; while (!TI); TI = 0; SBUF = temp; while (!TI); TI = 0; SBUF = humi; while (!TI); TI = 0; SBUF = sum; while (!TI); TI = 0; }

逻辑说明:帧头0xAA用于接收端同步,校验和用简单的累加和,够用且计算量小。接收端在中断里判断帧头,然后依次接收三个字节,最后校验。参数方面,波特率建议用 9600 或 19200,太高了 51 单片机在 11.0592MHz 下误差会变大。这个方案适合做「多点温湿度监测系统」的课设题目,扩展性强。

5.3 从 DHT11 迁移到 DHT22 或 SHT30 的注意事项

如果你觉得 DHT11 精度不够(温度 ±2°C,湿度 ±5%RH),想换成 DHT22 或 SHT30,代码改动主要在时序层。DHT22 的起始信号拉低时间只要 1ms,数据格式是 40 位,温湿度各 16 位,精度更高。SHT30 是 I2C 接口,完全不同的驱动方式,但 51 单片机没有硬件 I2C,需要用软件模拟。

// DHT22 起始信号和 DHT11 的差异 void DHT22_Start(void) { DHT22_IO = 0; delay_ms(1); // DHT22 只要 1ms,DHT11 要 20ms DHT22_IO = 1; delay_us(30); }

逻辑说明:DHT22 的时序比 DHT11 快,延时函数要重新调整。数据解析时,DHT22 的温度值最高位是符号位,湿度是 16 位整数,需要除以 10 得到实际值。参数方面,DHT22 的采样间隔建议不低于 2s,SHT30 可以更快。迁移的时候,先把新传感器的驱动单独调通,再替换到主程序里,别一次性改太多,否则出问题不好定位。

5.4 一个我常用的验证习惯

每次拿到新的温湿度代码,我不会直接烧到实物上跑,而是先在 Proteus 里用固定值测试显示逻辑,再用串口打印原始数据,最后才接实物。这样能把问题分层:显示问题、驱动问题、硬件问题,一层一层排查。从那以后我每次做温湿度项目,都强制走一遍「仿真固定值 -> 串口原始数据 -> 实物验证」的流程,省下来的调试时间比写代码的时间还多。希望这套资源能帮你少走点弯路,把精力花在真正有意思的地方。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询