资讯动态

51单片机双通道DHT11温湿度系统:硬件连接、时序与避坑指南

发布时间:2026/10/14 18:10:34 来源:尧图企业网站定制
简介这份资源面向单片机初学者与嵌入式课程设计者提供一套基于STC89C51的双通道DHT11温湿度实时显示方案解决环境数据采集与液晶显示联调的实际问题。压缩包共22个文件约68KB包含C语言源码与头文件、Keil工程配置、编译生成的hex与obj目标文件、汇编列表及构建日志等覆盖从代码编写到烧录验证的完整链路。硬件部分涉及DHT11单总线通信、LCD1602字符显示、按键模块以及电源、时钟、复位等基础电路软件则需处理时序控制与数据解析是理解传感器接口与人机交互设计的典型实例。目前已有1372人学习下载读者可据此搭建实物、对照源码梳理程序流程并借助工程文件快速复现调试过程适合作为嵌入式入门与综合实训的参考案例。1. 双通道 DHT11 温湿度系统为什么一块 51 单片机要挂两颗传感器很多做嵌入式课设或小型环境监测的开发者第一次看到“双通道 DHT11 实时温湿度显示系统”这个标题时第一反应是一颗 DHT11 不就够了为什么要挂两颗这正是这个方案最值得聊的地方。单点测温只能反映传感器附近那一小片空气的状态放在机箱里测的是板子温度放在窗边测的是窗边温度一旦你想做“室内外对比”“进出风口温差”“上下层温度梯度”单通道立刻不够用。双通道的价值不在于传感器多而在于它能让你在同一块 LCD1602 上同时看到两个位置的温湿度直接做差值判断。这个系统适合谁适合正在做单片机课程设计、想从“点亮一个传感器”进阶到“多传感器轮询 显示刷新 时序管理”的开发者也适合做小型仓储、温室、机房辅助监测的从业者用最低成本验证双点采集是否值得投入。STC89C51 这类经典 51 内核单片机资源有限RAM 只有 128 字节、Flash 也就几 KB正好逼着你去理解 DHT11 的单总线时序、LCD1602 的 4 位/8 位驱动方式以及“两个传感器怎么分时复用一根数据线或各占一根 IO”的工程取舍。把这块啃下来后面换 STM32、换 RS485 组网思路是通的。2. 双通道 DHT11 的硬件连接与单总线时序拆解2.1 为什么 DHT11 不能简单并联在同一根 IO 上DHT11 是单总线器件通信靠一根数据线完成“主机拉低启动 → 传感器响应 → 40 位数据逐位输出”。它的数据线是开漏结构同一时刻只能有一个器件在驱动总线。如果你把两颗 DHT11 的数据脚直接拧在一起接到同一个 IO两颗传感器会同时响应主机的启动信号然后同时往总线上吐数据结果就是电平互相打架读回来的 40 位数据全是乱的。这不是玄学是电气层面的冲突。常见做法有两种。第一种是“各占一根 IO”DHT11-1 接 P2.0DHT11-2 接 P2.1程序里分时分别读取互不干扰代码最简单代价是多占一个引脚。第二种是“单 IO 分时供电”两颗传感器数据脚并联但电源脚分别由两个 IO 控制读哪颗就给哪颗上电另一颗断电处于高阻态。这种方式省引脚但对供电 IO 的驱动能力和上电稳定时间有要求新手容易翻车。我一般推荐第一种51 的 IO 够用没必要给自己找麻烦。连接方式数据脚电源控制优点风险双 IO 独立P2.0 / P2.1常供电时序简单、稳定多占一个 IO单 IO 分时供电并联到 P2.0P2.1 / P2.2 控制省数据 IO上电稳定时间难调单 IO 分时供电MOS 管并联到 P2.0MOS 管切电源驱动能力强电路复杂2.2 DHT11 一次完整读取的时序参数DHT11 的时序对延时非常敏感51 单片机不加晶振校准很容易读失败。一次完整通信分四段主机启动、传感器响应、40 位数据、总线释放。主机先把数据线拉低至少 18ms然后拉高 20~40us接着释放总线等待传感器响应。传感器会先拉低 80us 表示收到再拉高 80us 表示准备发数据。之后每一位数据都以“50us 低电平 高电平”表示高电平持续 26~28us 代表 0持续 70us 代表 1。这里的关键是51 单片机在 12MHz 晶振下一个机器周期是 1us普通延时函数能凑合但如果用 11.0592MHz 晶振机器周期约 1.085us延时会有累积误差。很多新手读出来湿度一直是 0 或者 255八成是延时没对准。我的习惯是先用示波器或者逻辑分析仪抓一次波形确认启动信号和响应信号的时间再回头调延时函数比盲改代码快得多。// 主机启动信号拉低至少 18ms再拉高 20~40us void DHT11_Start(void) { DHT11_DATA 0; // 拉低数据线 Delay_ms(20); // 保持 20ms满足 18ms 要求 DHT11_DATA 1; // 拉高 Delay_us(30); // 保持 30us落在 20~40us 区间 DHT11_DATA 1; // 释放总线等待传感器响应 } // 读取一个字节8 位高位先出 unsigned char DHT11_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { while (!DHT11_DATA); // 等待 50us 低电平结束 Delay_us(40); // 延时 40us 后采样 dat 1; if (DHT11_DATA) // 仍为高说明是高电平 70us即数据 1 dat | 1; while (DHT11_DATA); // 等待该位高电平结束 } return dat; }上面这段代码里Delay_ms(20)和Delay_us(30)是启动时序Delay_us(40)是采样点。采样点选在 40us 是有讲究的数据 0 的高电平只有 26~28us40us 时已经变低数据 1 的高电平有 70us40us 时还是高。这样一次采样就能区分 0 和 1不用去精确测量高电平宽度。参数上Delay_us的精度直接决定成败建议用定时器或者示波器校准过的循环延时不要随手写for(i0;i10;i)就完事。2.3 LCD1602 的 4 位驱动与引脚分配LCD1602 支持 8 位和 4 位两种数据模式。8 位模式一次传一个字节占用 8 根数据线4 位模式分两次传高 4 位先传、低 4 位后传只占 4 根数据线。51 的 IO 本来就紧张双 DHT11 已经占了两个再加上 LCD 的 RS、RW、E 三根控制线8 位模式要占 11 个 IO4 位模式只占 7 个。所以这个方案里我一般用 4 位模式。引脚分配可以这样LCD 的 D4~D7 接 P0.4~P0.7RS 接 P2.5RW 接 P2.6E 接 P2.7。DHT11-1 数据脚接 P2.0DHT11-2 数据脚接 P2.1。注意 P0 口是开漏输出做 LCD 数据线时要外接 10K 上拉排阻否则电平拉不高LCD 会显示乱码或者干脆不亮。这个坑我见过太多次血泪经验就是P0 口驱动任何东西之前先问自己有没有上拉。// LCD1602 4 位模式写命令 void LCD_WriteCmd(unsigned char cmd) { LCD_RS 0; // 命令模式 LCD_RW 0; // 写模式 LCD_DATA (LCD_DATA 0x0F) | (cmd 0xF0); // 先发高 4 位 LCD_E 1; Delay_us(2); LCD_E 0; // 产生使能脉冲 Delay_us(100); LCD_DATA (LCD_DATA 0x0F) | ((cmd 4) 0xF0); // 再发低 4 位 LCD_E 1; Delay_us(2); LCD_E 0; Delay_us(100); }这段代码里LCD_DATA 0x0F是保留低 4 位不变把高 4 位替换成要发的数据。先发cmd 0xF0取高 4 位再发(cmd 4) 0xF0把低 4 位移到高 4 位发出。每次使能脉冲后延时 100us是给 LCD 控制器处理时间太快会丢命令。参数上Delay_us(2)是使能脉冲宽度一般要求大于 450ns2us 足够Delay_us(100)是命令执行时间清屏命令需要更久要单独延时 2ms 左右。3. 双通道轮询采集与 LCD1602 刷新逻辑的实现3.1 两个 DHT11 的分时读取流程双通道的核心是“分时”同一时刻只读一颗传感器读完再读另一颗。DHT11 两次采集之间建议间隔 1 秒以上因为它内部需要时间完成一次测量。如果你连续读第二颗可能还没准备好返回的数据就是上一次的旧值或者全 0。我的做法是主循环里放一个 1 秒的软定时时间到了先读 DHT11-1延时 100ms 再读 DHT11-2然后统一刷新 LCD。读取流程要加超时判断。DHT11 如果没接好或者坏了while(!DHT11_DATA)会死循环整个系统卡死。所以每个等待环节都要加计数器超过一定次数就跳出并返回错误码。这个错误码可以显示在 LCD 上比如“S1 ERR”方便排查是哪个通道出了问题。// 带超时的等待高电平/低电平 #define TIMEOUT 200 unsigned char DHT11_ReadData(unsigned char *temp, unsigned char *humi) { unsigned char buf[5], i, j; DHT11_Start(); if (DHT11_WaitLevel(0, TIMEOUT)) return 1; // 等待响应低电平超时 if (DHT11_WaitLevel(1, TIMEOUT)) return 2; // 等待响应高电平超时 for (i 0; i 5; i) { buf[i] DHT11_ReadByte(); } if (buf[4] ! (buf[0] buf[1] buf[2] buf[3])) return 3; // 校验和错误 *humi buf[0]; *temp buf[2]; return 0; // 成功 }这段代码里DHT11_WaitLevel是带超时的电平等待函数返回 0 表示等到返回 1 表示超时。buf[0]是湿度整数buf[2]是温度整数buf[4]是校验和。校验和错误返回 3说明数据在传输过程中被干扰了常见原因是数据线太长或者没有上拉电阻。参数上TIMEOUT设 200 次循环在 12MHz 下大约几百微秒足够覆盖正常响应时间又不会让系统卡太久。3.2 LCD1602 双通道显示布局与刷新节流LCD1602 只有两行每行 16 个字符。双通道显示要在一屏里塞下两组温湿度布局必须紧凑。我一般这样排第一行显示“T1:25C H1:60%”第二行显示“T2:26C H2:58%”。温度用整数湿度用整数省掉小数点字符数刚好够。如果要做温差显示可以把第二行改成“dT:1C dH:-2%”但这样就看不了第二通道的绝对值取舍看需求。刷新不能太频繁。LCD1602 的写命令需要时间如果你每次循环都刷屏幕会闪而且占用大量 CPU 时间。我的习惯是 1 秒刷一次和 DHT11 采集节奏对齐。刷新时不要整屏清空再写那样会闪直接定位到对应位置覆盖写视觉上更稳。LCD 的LCD_SetCursor函数负责定位第一行地址是 0x80第二行地址是 0xC0加上列偏移即可。// 双通道显示刷新每秒调用一次 void Display_Update(unsigned char t1, unsigned char h1, unsigned char t2, unsigned char h2) { LCD_SetCursor(0, 0); LCD_WriteString(T1:); LCD_WriteNum(t1); LCD_WriteString(C H1:); LCD_WriteNum(h1); LCD_WriteString(% ); // 末尾空格覆盖旧字符 LCD_SetCursor(1, 0); LCD_WriteString(T2:); LCD_WriteNum(t2); LCD_WriteString(C H2:); LCD_WriteNum(h2); LCD_WriteString(% ); }这段代码里LCD_SetCursor(row, col)把光标定位到指定行列LCD_WriteNum把整数转成字符写入。末尾的% 两个空格是为了覆盖上一次显示留下的多余字符比如上次湿度是 100这次是 60不覆盖就会显示“60%0”。参数上t1、h1、t2、h2都是 0~99 的整数DHT11 的湿度范围是 20%~90%温度是 0~50℃整数显示足够。3.3 主循环的时间片调度51 单片机没有操作系统主循环就是你的调度器。我一般把主循环分成三个时间片10ms 处理按键如果有、100ms 处理 LCD 局部刷新、1000ms 处理 DHT11 采集和整屏刷新。用一个定时器产生 10ms 中断在中断里累加计数主循环根据计数值判断该执行哪个任务。这样采集和显示互不阻塞系统响应更顺。注意 DHT11 读取期间不能被打断太久否则时序会乱。所以读 DHT11 的时候我会临时关掉总中断读完再开。关中断的时间大概几毫秒对按键扫描影响不大但能保证时序稳定。这个细节很多教程不讲但实际调试时如果你发现数据偶尔跳变八成就是中断打断了 DHT11 的时序。// 主循环时间片调度 void main(void) { unsigned char t1, h1, t2, h2; unsigned int tick 0; Timer0_Init(); // 10ms 定时中断 LCD_Init(); while (1) { if (tick % 100 0) { // 每 1 秒采集一次 EA 0; // 关中断保护 DHT11 时序 if (DHT11_ReadData(t1, h1) 0) { /* 通道 1 成功 */ } if (DHT11_ReadData(t2, h2) 0) { /* 通道 2 成功 */ } EA 1; // 开中断 Display_Update(t1, h1, t2, h2); } if (tick % 10 0) { Key_Scan(); // 每 100ms 扫描按键 } } }这段代码里tick由定时器中断每 10ms 加一tick % 100 0表示每 1 秒执行一次采集tick % 10 0表示每 100ms 扫描一次按键。EA 0和EA 1是关总中断和开总中断保护 DHT11 的时序不被定时器中断打断。参数上定时器初值根据晶振频率计算12MHz 下 10ms 定时器初值是 0xDC00 左右具体要看定时器模式。4. 双通道 DHT11 系统避坑与常见问题排查4.1 湿度读数一直是 0 或 255现象LCD 上湿度显示 0% 或者 255%温度正常或者也不正常。原因通常是 DHT11 启动信号不对或者采样点偏移。DHT11 对启动低电平时间要求大于 18ms如果你只延时了 10ms传感器根本不响应。另一个原因是Delay_us不准40us 采样点实际只有 20us把数据 1 误判成 0或者把 0 误判成 1读出来的字节全错。解决方法是先用示波器抓数据线波形确认启动低电平宽度和响应高电平宽度再回头校准延时函数。没有示波器就用逻辑分析仪几十块的那种够用。4.2 两个传感器数据互相串扰现象通道 1 和通道 2 的读数经常一样或者一个正常一个乱跳。原因如果是单 IO 分时供电方案可能是电源切换不够快上一颗传感器还没完全断电下一颗就开始通信总线电平被残留电荷拉低。解决方法是增加电源切换后的稳定延时或者干脆改成双 IO 独立方案。如果是双 IO 方案还串扰检查两颗传感器的数据线是不是走线太近产生了耦合分开走线或者加地线隔离。4.3 LCD1602 显示乱码或只亮不显现象LCD 背光亮但屏幕没有字符或者显示一排黑块。原因通常是 P0 口没加上拉电阻4 位模式下数据线电平拉不高LCD 控制器收不到正确数据。另一个原因是初始化序列不对4 位模式需要先发三次 0x33 唤醒再发 0x32 切换到 4 位很多人直接发 0x38 就完事结果 LCD 还在 8 位模式等着。解决方法是补上拉排阻并严格按照 4 位初始化时序写代码。对比度电位器也要调调得太偏也会看不到字符。4.4 系统运行一段时间后死机现象开机正常跑几分钟后 LCD 不再刷新按键也没反应。原因多半是 DHT11 读取时死循环了while(!DHT11_DATA)没有超时退出传感器一旦掉线程序就卡在那里。解决方法是所有等待环节都加超时计数器超时后返回错误码主循环继续跑。另外51 的看门狗如果开了要记得喂狗否则也会复位。我一般会在主循环里加一个 LED 闪烁指示闪就说明程序在跑不闪就是卡了比猜快。4.5 温度读数偏差大现象DHT11 读出来的温度比实际高 3~5℃。原因是传感器自发热或者安装在发热元件旁边。DHT11 的精度本身是 ±2℃如果你把它贴在稳压芯片或者单片机旁边读的就是板子温度。解决方法是把传感器用导线引出来远离热源并且两次采集之间保持 1 秒以上间隔减少自发热。如果还嫌精度不够那就不是 DHT11 的问题了该换 SHT30 或者 DS18B20但那是另一个方案的事。5. 从双通道到多点的扩展技巧与验证方法双通道跑通之后最自然的想法是扩展到三通道、四通道。但 51 的 IO 有限每增加一颗 DHT11 就多占一个 IO很快就不够用。这时候可以考虑用 74HC4051 模拟开关做通道扩展用 3 个 IO 选择 8 个通道数据线共用一根。代价是每次切换通道后要等模拟开关稳定再启动 DHT11时序上多一层延时。这个方案我试过8 通道轮询一圈大概 8 秒做环境监测够用做实时控制就偏慢。验证系统稳不稳定我一般做三个测试。第一是“长时间跑”连续运行 24 小时看 LCD 有没有花屏、数据有没有跳变。第二是“拔插测试”在运行中拔掉一颗 DHT11看系统能不能检测到错误并继续显示另一颗的数据而不是死机。第三是“对比测试”用一颗标准温湿度计放在旁边看两个通道的读数和标准值的偏差是否在 ±2℃ / ±5% 以内。这三个测试做完基本能判断方案能不能落地。// 错误状态显示通道故障时在 LCD 上提示 void Display_Error(unsigned char ch, unsigned char err) { LCD_SetCursor(ch, 0); // ch0 第一行ch1 第二行 LCD_WriteString(CH); LCD_WriteNum(ch 1); LCD_WriteString( ERR); LCD_WriteNum(err); // 错误码1 响应低超时2 响应高超时3 校验错 LCD_WriteString( ); }这段代码里ch是通道号err是错误码。把错误码显示出来排查时一眼就能看出是超时还是校验错。超时通常是接线问题校验错通常是干扰问题。参数上错误码 1 和 2 对应DHT11_WaitLevel的返回值3 对应校验和失败。显示时末尾加空格覆盖旧内容避免残留字符。我自己的习惯是每次调 DHT11 之前先把延时函数用示波器校一遍确认 1us、10us、1ms 三个档位准了再往下写。这个步骤花十分钟能省掉后面两小时的瞎猜。双通道系统看起来简单但两个传感器轮询、LCD 刷新、中断保护这几件事叠在一起任何一个环节没对齐都会翻车。把时序和超时这两件事做扎实后面换传感器、换屏幕、换单片机都是同一套思路。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑