☰
DHT11温湿度OLED显示实战:从单总线时序到I2C驱动
2026/10/20 13:54:55 网站建设 项目流程

简介:这份资源面向嵌入式与单片机初学者及电子设计爱好者,围绕DHT11数字温湿度传感器与OLED显示屏的联动展开,解决温湿度采集与本地显示这一常见入门项目需求。压缩包共190个文件,约6.82MB,以C语言源文件与头文件为核心,包含34个.c、36个.h,并配有Keil工程文件(uvprojx)、启动文件、链接脚本、编译中间产物及烧录批处理脚本,覆盖从源码到可执行固件的完整工程结构,便于直接编译与二次修改。资源描述中详细介绍了DHT11采用电阻式感湿元件与NTC测温元件、单线制串行接口、校准系数存于OTP内存等特性,可帮助读者理解传感器时序与驱动原理。目前已有3710人学习下载,适合作为STM32平台温湿度采集与OLED显示项目的参考工程,读者可据此掌握传感器驱动编写、显示刷新与工程配置的完整思路。

1. 从一个“玄学读数”说起:DHT11 温湿度 OLED 显示到底能干什么

如果你玩过 DHT11,大概率遇到过这种情况:串口打印出来的温度一会儿 25℃,一会儿 0℃,湿度干脆变成 255%——这不是传感器坏了,而是时序没对上。DHT11 是单总线器件,靠一根数据线完成握手、传位、校验,对微秒级延时极其敏感。这个项目要解决的就是把“玄学读数”变成稳定显示:用单片机读取 DHT11 的温湿度,再驱动 OLED 屏实时刷新。它适合刚接触嵌入式、想找一个完整闭环练手的人——从 GPIO 时序、单总线协议,到 I2C 驱动 OLED、字符取模显示,一条链路全走通。你拿到的不只是几行示例代码,而是一个能直接烧录、能改、能扩展的最小系统。

2. 硬件与协议拆解:DHT11 单总线和 OLED 的 I2C 到底怎么接

2.1 硬件连接与供电注意

DHT11 有三个引脚(部分模块是四脚,多一个空脚):VCC、DATA、GND。DATA 必须接一个 4.7kΩ~10kΩ 的上拉电阻到 VCC,否则总线拉不高,读出来全是 0。OLED 常见的是 0.96 寸 128×64 的 SSD1306 驱动,I2C 接口只有 SCL、SDA 两根线,加上 VCC、GND 共四根。我一般把 DHT11 的 DATA 接到 PA0,OLED 的 SCL 接 PB6、SDA 接 PB7(以 STM32 为例,软件 I2C 可任意换脚)。供电统一用 3.3V,DHT11 虽然标称 3.3V~5.5V,但和 OLED 共 3.3V 最省事,不用电平转换。

注意:DHT11 上电后需要 1 秒稳定时间,刚上电立刻读大概率失败,初始化里先延时 1 秒再进主循环。

2.2 DHT11 单总线时序:一次完整读取的四个阶段

DHT11 的通信流程分四步:主机发开始信号、DHT11 响应、传输 40 位数据、校验。40 位数据是:湿度整数 8 位 + 湿度小数 8 位 + 温度整数 8 位 + 温度小数 8 位 + 校验和 8 位。校验和 = 前四个字节相加取低 8 位。DHT11 的小数部分实际固定为 0,但协议里占位,读的时候别丢。

// 主机发送开始信号:拉低至少 18ms,再拉高 20-40us void DHT11_Start(void) { DHT11_SetPinOutput(); // DATA 配置为输出 DHT11_LOW(); // 拉低总线 delay_ms(20); // 保持 20ms,保证 DHT11 能检测到 DHT11_HIGH(); // 拉高 delay_us(30); // 主机释放总线,等待 DHT11 响应 DHT11_SetPinInput(); // 切换为输入,准备读取 }

这段代码的关键在最后一步:拉高 30us 后必须把引脚从输出切成输入,否则主机会一直驱动总线,DHT11 拉不低,响应信号就丢了。delay_us(30) 是经验值,20~40us 都在容错范围内。

2.3 读一位数据:靠高电平持续时间区分 0 和 1

DHT11 响应后会先拉低 80us,再拉高 80us,然后开始传位。每一位都以 50us 低电平开始,接着是高电平:高电平持续 26~28us 表示 0,持续 70us 表示 1。所以读一位的核心是“等低电平结束 → 延时 30us → 采样 → 等总线回低”。

// 读取一个位:返回 0 或 1 uint8_t DHT11_ReadBit(void) { uint8_t retry = 0; while (DHT11_ReadPin() && retry < 100) { retry++; delay_us(1); } // 等待 50us 低电平结束 retry = 0; while (!DHT11_ReadPin() && retry < 100) { retry++; delay_us(1); } // 等待高电平开始 delay_us(40); // 延时 40us 后采样:0 的高电平已结束,1 的还在 if (DHT11_ReadPin()) { // 仍为高 → 数据 1 retry = 0; while (DHT11_ReadPin() && retry < 100) { retry++; delay_us(1); } // 等 1 的高电平结束 return 1; } return 0; }

retry 计数是防死循环的后悔药:如果传感器没接好,while 会卡死,加上 100 次上限就能超时退出。delay_us(40) 是采样点,取在 0 和 1 高电平持续时间的中间偏后,容错最好。

2.4 读完整帧并校验

// 读取温湿度,成功返回 0,失败返回 1 uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi) { uint8_t buf[5] = {0}; DHT11_Start(); if (DHT11_WaitResponse() != 0) return 1; // 等待 DHT11 响应,超时则失败 for (int i = 0; i < 5; i++) { for (int j = 0; j < 8; j++) { buf[i] <<= 1; buf[i] |= DHT11_ReadBit(); } } if (buf[4] != (buf[0] + buf[1] + buf[2] + buf[3])) return 1; // 校验失败 *humi = buf[0]; *temp = buf[2]; return 0; }

校验和这一步千万别省。我见过有人读出来湿度 255%,就是因为没校验,把响应阶段的杂波当数据用了。加上校验后,失败就重读,主循环里连续失败三次再报错,稳定性提升非常明显。

3. OLED 驱动与显示刷新:从取模到实时更新

3.1 SSD1306 初始化:几条命令搞定

OLED 用 I2C 通信,SSD1306 的从机地址通常是 0x78(写地址)。初始化就是按顺序发一串命令:关显示、设时钟、设复用率、设偏移、设起始行、设对比度、设扫描方向、开电荷泵、开显示。这些命令值在数据手册里都有,不用背,抄一次就行。

// SSD1306 初始化命令序列(部分) void OLED_Init(void) { OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); OLED_WriteCmd(0x80); // 设置时钟分频 OLED_WriteCmd(0xA8); OLED_WriteCmd(0x3F); // 设置复用率为 64 OLED_WriteCmd(0x20); OLED_WriteCmd(0x00); // 水平寻址模式 OLED_WriteCmd(0x8D); OLED_WriteCmd(0x14); // 开启电荷泵 OLED_WriteCmd(0xAF); // 开启显示 }

电荷泵命令 0x8D 后面必须跟 0x14,否则屏不亮——这是新手最容易翻车的地方,屏黑了半天以为接线错,其实是电荷泵没开。

3.2 显示字符串:字库取模与坐标换算

OLED 显示字符靠字库数组。常用 8×16 点阵,一个字符占 16 字节。显示时按页写:128×64 分 8 页,每页 8 行像素。坐标 (x, y) 里 y 要换算成页号 y/8,x 直接是列地址。

// 在指定位置显示一个 8x16 字符 void OLED_ShowChar(uint8_t x, uint8_t y, char ch) { uint8_t page = y / 8; // 计算页号 uint8_t *pfont = &Font8x16[(ch - ' ') * 16]; // 取字模 for (int i = 0; i < 8; i++) { OLED_SetPos(x + i, page); OLED_WriteData(pfont[i]); // 上半部分 OLED_WriteData(pfont[i + 8]); // 下半部分 } }

字库数组 Font8x16 用取模软件生成,取模方式选“列行式、逆向”,和这段代码的写入顺序对应。取模方式选错,显示出来就是乱码或镜像,这个坑几乎每个人都踩过。

3.3 主循环:读一次、显示一次、间隔两秒

DHT11 采样率不能太快,手册要求间隔不低于 1 秒,我一般用 2 秒。主循环结构很简单:读数据、格式化字符串、刷屏、延时。

int main(void) { uint8_t temp, humi; char disp[20]; delay_ms(1000); // 上电稳定 OLED_Init(); OLED_Clear(); while (1) { if (DHT11_ReadData(&temp, &humi) == 0) { sprintf(disp, "Temp: %d C", temp); OLED_ShowString(0, 0, disp); sprintf(disp, "Humi: %d %%", humi); OLED_ShowString(0, 2, disp); } else { OLED_ShowString(0, 4, "Read Error"); } delay_ms(2000); // 采样间隔 2 秒 } }

sprintf 在单片机上要注意栈空间,显示内容短没问题,但别在中断里用。如果编译报错找不到 sprintf,检查是否包含了 stdio.h,以及是否勾选了微库(MicroLIB)。

4. 避坑与排查:DHT11 读数为 0 或 255 的常见原因

4.1 现象:读数一直是 0,校验也过

原因通常是上拉电阻没接或阻值太大。DHT11 的数据线是开漏输出,没有上拉电阻时总线拉不高,读到的全是低电平。解决:在 DATA 和 VCC 之间并一个 4.7kΩ 电阻,用万用表量一下 DATA 对 VCC 的阻值,正常应该在 4.7kΩ 左右。

4.2 现象:湿度 255%、温度 0℃,校验偶尔通过

这是典型的时序偏差。delay_us 不准(比如用了系统滴答但没配好),导致采样点落在错误位置。解决:用示波器看 DATA 波形,确认高电平持续时间;没有示波器就把 delay_us 用循环空指令实现,别依赖中断。另外,读位函数里的 retry 上限调大一点,给慢速传感器留余量。

4.3 现象:OLED 不亮,但 I2C 地址能扫到

先查电荷泵命令 0x8D 后是否跟了 0x14。再查初始化顺序:必须在发显示命令前完成所有配置。还有一个隐蔽问题:有些模块的 I2C 地址是 0x7A 而不是 0x78,扫一下地址确认。解决:用 I2C 扫描代码打印所有应答地址,按实际地址改宏定义。

4.4 现象:显示正常但刷新几次后花屏

多半是 I2C 速率太高或没有应答等待。软件 I2C 里每个字节后要等从机 ACK,如果直接跳过,数据会错位。解决:在 OLED_WriteData 里加 ACK 检测,或者把 I2C 延时加大到 5us 以上。硬件 I2C 则检查是否开了时钟拉伸(Clock Stretching),SSD1306 需要这个。

4.5 现象:编译通过但下载后没反应

先确认单片机供电正常、复位电路没问题。再查启动文件里的堆栈大小,sprintf 和 OLED 缓冲区可能吃掉不少 RAM。解决:把栈调到 0x400 以上,或者改用轻量格式化函数。还有一点,DHT11 的 DATA 引脚在初始化时要先设为输出高,别一上来就输入,否则传感器可能进不了正常状态。

5. 二次开发:把温湿度显示改成带阈值报警的采集节点

跑通显示只是起点,真正有意思的是改。我一般会先加一个阈值判断:温度超过 30℃ 或湿度低于 20% 时,在 OLED 上反色显示一行警告,同时拉高一个 GPIO 驱动蜂鸣器。改动很小,但立刻从“玩具”变成“能用的节点”。

// 阈值报警逻辑,插在主循环显示之后 #define TEMP_HIGH 30 #define HUMI_LOW 20 if (temp > TEMP_HIGH || humi < HUMI_LOW) { OLED_ShowString(0, 6, "! ALARM !"); // 第 6 页显示警告 GPIO_SetBits(GPIOA, GPIO_Pin_1); // 拉高蜂鸣器引脚 } else { OLED_ShowString(0, 6, " "); // 清空警告行 GPIO_ResetBits(GPIOA, GPIO_Pin_1); }

这段代码里,OLED_ShowString 写空格是清行,比整屏清再重画省事。蜂鸣器引脚记得在初始化里配成推挽输出。如果你想再进一步,可以把数据通过串口发出去,接一个上位机画曲线;或者加一个按键,短按切换显示摄氏/华氏,长按进入阈值设置模式。设置模式用两个变量存阈值,写到 Flash 里掉电不丢——这一步会用到 Flash 读写,是下一个练手点。

验证改造是否成功,我习惯用“三步法”:第一步,拔掉 DHT11,看是否显示 Read Error,确认错误分支能进;第二步,用手捏住传感器,看湿度是否上升、温度是否向体温靠拢,确认数据有响应;第三步,用吹风机热风远距离扫一下,看是否触发报警,确认阈值逻辑生效。这三步走完,基本不会翻车。

从那以后我每次改完显示逻辑,都强制走一遍“拔传感器 → 捏传感器 → 热风扫”的验证流程,比盯着代码看半天管用。希望帮到你。

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

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

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

立即咨询