资讯动态

基于STC89C51与DHT11的多点测温系统设计与Proteus仿真

发布时间:2026/9/13 20:07:09 来源:尧图企业网站定制
简介基于STC89C51单片机与DHT11数字温湿度传感器的双点测温方案包含Proteus仿真工程与Keil软件源码面向单片机课程设计、毕业设计及嵌入式入门学习者解决双路温度实时采集、LCD1602显示及独立上下限报警的比较逻辑问题。压缩包共24个文件体积仅有72KB涵盖C语言源程序、头文件、仿真原理图DSN/DBK、Keil工程配置uvproj/uvopt、Hex烧录文件及编译过程文件目录结构清晰解压后可直接打开仿真或烧录到开发板验证。目前已有199人浏览学习作品可作为同类多点测温项目的可靠参考。通过该方案读者可重点学习DHT11单总线时序的编写、双点数据的采集与切换、LCD字符拼接显示以及高温H/低温L报警标志的置位与清除逻辑仿真运行无需额外硬件便于在实验课前快速调试与答辩阶段现场演示。1. 包里的ds18b20.c与标题上的DHT11一份两点测温工程的文件构成解压开这个RAR第一眼确实容易愣一下标题写“基于STC89C51和DHT11”源码目录里躺着的却是ds18b20.c、ds18b20.h再加上main.c、lcd1602.c/h和一串.OBJ、.LST、.M51的Keil编译中间文件。这是很多课设工程整理时留下的痕迹DS18B20与DHT11都是单总线器件底层读位时序的骨架可以共用改成DHT11的40位温湿度帧之后旧文件名没逐个改回来。真正负责业务逻辑的是main.c。它维护两路DHT11的读值各自按预设温度上、下限做比较在LCD1602对应位置显示H高温报警或L低温报警Proteus仿真DSN可以直接跑通两路测温。这个定位很明确给单片机课设、毕业设计做原型验证的人拿来改参数就能用省掉从头搭时序和显示驱动的功夫。2. DHT11单总线时序与双测温点硬件连接2.1 DHT11为什么不能像DS18B20那样“一线挂多片”DS18B20能在一根总线上挂多个传感器靠的是每片独立的64位ROM序列号主机通过Skip ROM、Match ROM指令寻址。DHT11没有这套寻址机制它只认一个“主机拉低起始信号”的动作总线上一旦同时挂两个DHT11两个传感器的应答会互相踩踏数据帧直接错乱。所以常见的做法是一个DHT11占用一个独立IO口两点测温就用P2.0和P2.1分别接一路传感器软件对每个点分别发起起始信号、分别读取40位数据互不干扰。从器件参数看DHT11测温范围0~50℃、精度±2℃湿度范围20~90%RH采样周期典型值1秒。对室内环境监测、机房散热报警这类看趋势的场景够用如果要采集-10℃以下的低温数据它就不合适了要改用DS18B20或NTC方案。这也是拿到这个工程后要先确认的边界项目名叫“多点测温”实际指标受DHT11量程限制不能在超出规格的场景里硬套。2.2 STC89C51引脚分配两路DHT11与LCD1602STC89C51是增强型8051内核默认12T模式晶振用12MHz时一个机器周期正好1μs和DHT11时序里的微秒级延时能直接对上。硬件连接按表2-1分配即可。功能引脚说明第1路DHT11数据线P2.0需对VCC接4.7k上拉第2路DHT11数据线P2.1需对VCC接4.7k上拉LCD1602数据DB0~DB7P0.0~P0.78位模式实物P0需外接上拉排阻LCD1602 RSP2.5寄存器选择LCD1602 RWP2.6读写选择接地或由MCU控制LCD1602 ENP2.7使能信号晶振12MHz12T模式下机器周期1μs关于上拉电阻DHT11数据线空闲时必须由外部上拉保持高电平4.7k是数据手册的典型值别用1k去替代。Proteus仿真里省略上拉有时也能跑因为模型内部做了简化但实物照这个图焊少了上拉大概率读不到任何数据LCD上的温度长时间保持初值不刷新。2.3 40位数据帧格式与校验和运算DHT11一次完整应答输出40位8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、8位校验和全部高位在前。校验和规则是前四个字节相加取末8位与第五个字节相等才算有效。用C写校验就是一次加法加一个掩码unsigned char dht11_data[5]; unsigned char dht11_crc_ok(void) { unsigned char sum; sum dht11_data[0] dht11_data[1] dht11_data[2] dht11_data[3]; return (sum dht11_data[4]) ? 1 : 0; }这里的dht11_data[0]和dht11_data[1]分别是湿度整数和湿度小数dht11_data[2]、dht11_data[3]是温度整数和温度小数dht11_data[4]是校验字节。四字节之和超过255时自然溢出只保留低8位正好对应DHT11协议的校验方式。返回1表示数据帧可信可以拿去刷新LCD和触发报警返回0说明这帧数据脏了直接丢弃等下一轮采样不要拿脏数据去做H/L比较否则报警字符会误跳。需要注意一个位级坑温度整数字节的bit7如果是1表示负温度。室内测温场景默认按正数处理问题不大但工程如果拿到冬季户外去用必须在显示前判断符号位否则会出现“温度23℃”这种离谱读数。负温度的折算规则在第5章排错部分展开。3. Proteus 8仿真载入与Keil C51的HEX生成配置3.1 打开DSN工程并载入HEX固件包里的基于STC89C51和DHT11的多点测温方案仿真.DSN是Proteus 8 Professional工程建议用8.9以上版本打开太旧的版本会提示DSN格式不支持或直接丢元件。打开后能看到STC89C51主控、两路DHT11、LCD1602以及必要的阻容元件。载入固件路径双击原理图中的STC89C51芯片在Edit Component对话框的Program File一栏选择包内编译好的STC89C51_DHT11.hexClock Frequency保持12MHz点击左下角运行。仿真跑没跑起来主要看LCD1602正常时两行会分别出现P1:和P2:开头的温度读数报警位显示N或H/L。这里要强调的是代码每次重新编译后如果Keil的Output目录和Proteus里的HEX路径不一致Proteus加载的还是上一版固件。我一般把HEX直接输出到DSN同目录两边永远指向同一个文件省得出现“改了代码仿真没反应”的乌龙。3.2 DHT11的Proteus模型与4.7k上拉电阻自己从零搭仿真而不是打开现成DSN时元件放置有两个注意点。第一Pick Devices搜索框里输入dht11大小写不敏感元件通常归在Sensors分类下少数汉化版本里分类名显示为“传感器”搜索结果不变。第二两路DHT11数据线各接一个4.7k电阻到VCC电阻值直接在属性里改默认的1k和实物行为不一致。Proteus的DHT11模型支持仿真中途改温度。运行状态下双击DHT11模型属性面板里的Temperature从28改成35下一帧数据就会按新值输出。这个特性用来验证两点报警逻辑非常高效把第1路温度改到高于上限LCD对应位置马上出H改到低于下限出L。比在实物上吹热风或贴冰袋省事得多也是我拿到这个工程后先做的功能自检。3.3 Keil C51编译Create HEX File必须勾上源码包里的STC89C51_DHT11.uvproj用Keil uVision4或uVision5直接双击就能打开前提是电脑装了C51编译器而不是只有MDK-ARM。两者的.uvproj后缀看着一样但C51版才能编译8051内核判断方法是看Project Targets里能否选中STC89C51这类型号。打开工程后先做一步设置再编译AltF7进入Options for Target在Output标签页勾选Create HEX File。不勾这一项编译只生成.OBJ和.AXFProteus那边没有HEX可加载。# 勾选Create HEX File后Output目录才会出现以下产物 STC89C51_DHT11.hex # 烧录和仿真用的最终固件 STC89C51_DHT11.M51 # 存储器映射文件定位RAM溢出看这里 STC89C51_DHT11.lnp # 链接参数列表这个检查点很值钱因为很多人改完代码回Proteus运行发现还是旧行为折腾半天最后查出是HEX根本没更新。M51文件则是出现L51: SEGMENT TOO LARGE这类链接报错时才需要看确认变量被分配到了哪个DATA段一般调大内存模型就能解决。4. main.c报警比较与LCD1602的H/L显示逻辑4.1 用端口掩码复用两路DHT11读取代码Keil C51里的sbit是编译期固定的位地址不能作为函数参数传递所以两路传感器不能靠“传入sbit”复用代码。工程里更干净的写法是直接用端口掩码操作P2第1路用0x01对应P2.0第2路用0x02对应P2.1底层读写函数把掩码当参数接收。#define DHT11_1_MASK 0x01 // P2.0第1测温点 #define DHT11_2_MASK 0x02 // P2.1第2测温点 // 起始信号主机拉低18ms然后释放总线 void dht11_start(unsigned char mask) { P2 ~mask; // 该位置0其余引脚不受影响 delay_ms(20); // 20ms留出余量 P2 | mask; // 置1释放外部上拉恢复高电平 delay_us(30); // 等待传感器应答 } // 读取一字节每位先等低电平结束再延时40us采样 unsigned char dht11_read_byte(unsigned char mask) { unsigned char i, dat 0; for (i 0; i 8; i) { while ((P2 mask) 0); // 等该位低电平结束 delay_us(40); // 采样点 dat 1; if (P2 mask) { // 40us后仍为高数据位为1 dat | 0x01; while (P2 mask); // 等剩余高电平结束 } } return dat; }dht11_start里的P2 ~mask只把对应的数据线拉低P2.5、P2.6、P2.7这些接LCD的控制位不受影响这是掩码方式相对sbit的额外好处。dht11_read_byte的采样逻辑是每一位先低50μs分隔再以高电平的持续宽度区分0和1高电平持续26~28μs代表0持续70μs代表1。代码在低电平结束后延时40μs采样此时引脚为高就是1为低就是0。40μs这个值不能乱改太短会把0读成1太长可能错过一个完整位。4.2 上、下限比较规则与LCD刷新两路温度读出来后分别和该点预设的上限、下限比较。比较规则按摘要定义整理如下表。条件报警类型LCD显示字符温度大于该点上限高温报警H温度小于该点下限低温报警L下限至上限区间内正常N每个点有独立上下限这就是“多点”的设置位置第1点设20~30℃第2点可以设18~28℃改main.c里两个数组的初值即可。unsigned char temp_high[2] {30, 28}; // 两点温度上限 unsigned char temp_low[2] {15, 18}; // 两点温度下限 void lcd_show_point(unsigned char idx, unsigned char temp) { lcd_gotoxy(idx, 0); // 第idx行第0列 lcd_putstr(P); lcd_putchar(1 idx); lcd_putchar(:); lcd_putchar(temp / 10 0); // 十位低于10℃时显示0 lcd_putchar(temp % 10 0); // 个位 lcd_putchar(C); lcd_putchar( ); // 先清报警位防残影 if (temp temp_high[idx]) { lcd_putchar(H); } else if (temp temp_low[idx]) { lcd_putchar(L); } else { lcd_putchar(N); } }lcd_gotoxy(idx, 0)把第1点放在第0行、第2点放在第1行都在第0列开始显示。温度值先除10再模10转ASCII保证个位数温度也占用两个字符位例如6℃显示为06C不会挤掉后面的报警位。显示前先写一个空格再写H/L/N是为了覆盖上一次报警字符否则温度从报警值回到正常区间后LCD上会残留一个H或L看着像一直处于报警状态。4.3 轮询节拍与采样周期DHT11数据手册明确写着两次读取间隔不能小于1秒这是内部采样的固有限制。不少代码在这个点上翻车主循环里连续读两路接着一句delay_ms(500)看起来间隔不小但加上LCD刷新和按键扫描后实际节奏一乱传感器就返回全1或校验失败。稳妥的轮询结构是把两路读取放在一个节拍里读完立刻刷新LCD再整体延时2秒进入下一轮单轮耗时固定。while (1) { if (dht11_read_all(0, temp[0], hum[0])) { // 读第1路 lcd_show_point(0, temp[0]); } if (dht11_read_all(1, temp[1], hum[1])) { // 读第2路 lcd_show_point(1, temp[1]); } delay_ms(2000); // 保证两次采样的间隔1s }dht11_read_all内部就是“起始信号→读5字节→校验和→解析温湿度”的完整流程参数0和1分别对应两路的掩码返回1才刷新显示。返回0时不处理LCD保留上一次正确读数这比显示乱码值要友好。2秒的轮询间隔对温度报警场景完全够用DHT11本身的温变响应就慢没必要追求更快的刷新率。5. 从仿真到实物的边界处理时序余量、负温度与报警残影5.1 12MHz晶振下的微秒延时精度STC89C51在12MHz晶振、12T模式下一个机器周期就是1μs。包内代码的delay_us通常用_nop_()或空循环实现Keil优化级别不同实际延时会有几个微秒的漂移。调试时先把Optimization设为0级跑通后再逐级往上调如果调到高优化级别后DHT11读数开始跳变说明数据位采样点被编译器调整了给延时函数里的循环变量加volatile修饰可避免被优化掉。还有一点容易被忽略使用while ((P2 mask) 0)等待低电平时如果传感器损坏一直拉低这里会死循环正式代码里要给等待加超时计数超过阈值直接返回错误帧。5.2 负温度位处理与CRC失败排查温度整数字节的bit7为1时代表零下折算规则是把低7位取反加1结果加负号。举例读到0xFE时低7位是0x7E取反得0x01加1得2所以真实温度是-2℃。北方的冬季室内环境基本不会触发这个分支但代码里仍然建议保留判断避免换了使用场景后出现异常读数。排查CRC校验失败时按概率从高到低看三处数据线上拉电阻缺失、起始低电平时间不足18ms、两次读取间隔小于1秒。提示Proteus仿真中途修改DHT11温度后LCD没有变化先双击DHT11模型确认Temperature属性确实写入再检查主循环里的delay_ms(2000)是否被仿真速度设置拖慢了。5.3 实物移植时的显示残影与引脚干扰LCD上H/L残留的问题代码层写空格已经处理掉一部分实物上有两点额外注意。P0口外接上拉排阻不焊的话LCD数据线浮空第二行可能出现一串随机字符这不是代码问题是硬件没接地。另外两路DHT11的数据线如果贴着长排线走线读值会偶尔跳变这是相邻走线耦合造成的解决方法是每条数据线串一个100Ω电阻再进MCU引脚同时把两条线分开一段距离。本文还有配套的精品资源点击获取

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

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

免费获取报价