news 2026/10/16 7:34:06

HAL库DHT11温湿度传感器+OLED显示:零基础入门实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HAL库DHT11温湿度传感器+OLED显示:零基础入门实战

简介:这份资源面向嵌入式与物联网方向的初学者及开发者,提供基于STM32 HAL库驱动DHT11温湿度传感器并在OLED屏上实时显示数据的完整工程实例,可用于智能家居、环境监测等场景的入门实践与课程设计参考。压缩包共193个文件,约6.9MB,以h头文件、c源文件、o目标文件、crf交叉引用文件及d依赖文件为主,另含ioc配置、uvprojx与uvoptx工程文件、hex烧录文件、map映射文件等,覆盖HAL库外设驱动、DHT11单总线时序读取、OLED显示驱动等模块,工程结构完整可直接编译。目前已有772人学习下载。读者可从中获取STM32F0系列HAL库的GPIO、I2C、TIM等外设配置范例,理解DHT11四十位数据的解析方法,并掌握SSD1306类OLED屏的文本与坐标显示驱动思路,适合作为嵌入式物联网硬件交互与传感器采集的练手项目。

1. 从一块“会说话”的传感器开始:hal库dht11温湿度传感器+oled显示到底是什么

如果你刚接触单片机,看到“hal库dht11温湿度传感器+oled显示”这一长串词,第一反应大概是:每个字都认识,连起来就不知道在说什么。别急,我第一次看到也这样。你可以把它想象成一个极简的“天气小站”:DHT11负责感知周围的温度和湿度,OLED屏幕负责把这两个数字亮出来给你看,而HAL库就是帮你把这两件事串起来的“翻译官”。整套东西跑通之后,你手里会有一个能实时显示温湿度的小装置,成本很低,接线也不复杂,特别适合零基础新手拿来练手。它解决的问题很具体——让你第一次真正把“传感器读数据”和“屏幕显示数据”这两件物联网里最基础的事连起来。适合谁?适合刚学完单片机GPIO点灯、想找个能看见实际效果的小项目来巩固的人。你不需要先懂操作系统,也不需要先学复杂的网络协议,只要能让代码跑起来,就能看到数字在屏幕上跳。

2. 为什么值得从这个小项目入手:先搞懂三个关键角色

2.1 先弄明白HAL库到底帮你省了什么力气

如果你之前翻过单片机的参考手册,可能会被一堆寄存器名字吓到。以前写代码要直接操作寄存器,比如设置某个引脚为输出、拉高拉低、延时等待,每一步都得查手册、算二进制。HAL库的出现就是为了让你少干这些“体力活”。它把常用的硬件操作封装成函数,你调用HAL_GPIO_WritePin()就能控制引脚,调用HAL_Delay()就能毫秒级延时,不用再关心底层寄存器怎么变。对于新手来说,这意味你可以把精力放在“逻辑”上:先读传感器,再显示数据,而不是先花三天搞懂时钟树怎么配。入门的话建议先接受一个观念——HAL库不是让你变懒,而是让你先跑通流程,等以后想深挖再回头看寄存器也不迟。我第一次学的时候就是先用HAL库把项目跑起来,看到屏幕亮了,才有信心继续往下学。

2.2 DHT11为什么适合新手:单总线、慢速、够用

DHT11是一款数字温湿度传感器,它最大的特点就是“单总线”通信——只需要一根数据线就能和单片机交换信息。这根线既要发送开始信号,又要接收40位数据,听起来有点玄学,但实际用起来并不难,因为时序要求虽然严格,但速度很慢,单片机完全跟得上。它的测量范围是温度0到50摄氏度,湿度20%到90%,精度分别是±2度和±5%,对于日常环境监测足够用了。你不需要外接ADC,也不需要校准,上电就能读。很多新手会纠结“为什么不用更准的SHT30”,答案很简单:DHT11便宜、资料多、时序简单,翻车了也容易排查。入门阶段,先让一个传感器稳定工作,比追求高精度更重要。

2.3 OLED显示:让数据“看得见”才有成就感

如果只把温湿度读到单片机里,你只能通过串口打印看数字,那感觉就像在黑匣子里猜。OLED屏幕的作用就是把数据变成肉眼可见的东西。常用的0.96寸OLED分辨率是128×64,有四个引脚:电源、地、SCL、SDA,走I2C通信。I2C的好处是只需要两根线就能驱动屏幕,而且HAL库有现成的I2C函数,你只要把数据写到指定坐标,屏幕就会亮起来。第一次看到自己写的代码让屏幕显示出“Temp: 25C”的时候,那种反馈感是很强的。而且OLED本身不复杂,你不需要学图形库,只要会显示字符就行。这个项目里,OLED就是你的“输出窗口”,DHT11是“输入窗口”,HAL库是“胶水”,三者凑在一起,就是一个完整的微型物联网节点雏形。

3. 动手之前先避坑:新手最容易翻车的五个地方

3.1 现象:屏幕一直不亮,或者只亮一下就没反应

原因:I2C地址搞错了。OLED的I2C地址通常是0x78或0x7A,但有些模块是0x3C或0x3D,如果你直接抄别人的代码,地址对不上就完全没反应。 解决:先用I2C扫描代码把总线上所有地址打印出来,确认你的屏幕到底是哪个地址,再改代码里的宏定义。

3.2 现象:DHT11读出来全是0或者全是255

原因:时序不对。DHT11对延时很敏感,尤其是上电后的等待时间和读取40位数据时的电平判断。如果你用的延时函数精度不够,或者中途被中断打断,数据就会错。 解决:读DHT11的时候先关掉全局中断,用微秒级延时函数,并且确保数据线有上拉电阻(通常模块自带,但如果你自己接,记得加4.7k到10k的上拉)。

3.3 现象:屏幕显示乱码或者花屏

原因:初始化序列不对,或者供电不稳。OLED对电源比较敏感,如果单片机供电不足,屏幕会随机显示乱码。 解决:检查OLED的初始化代码是否完整,尤其是对比度和扫描方向设置。供电方面,尽量用稳定的3.3V或5V,不要和电机之类的大电流设备共用电源。

3.4 现象:程序编译通过,但下载后没反应

原因:引脚配置错了。比如你把DHT11的数据线接在PA5,但代码里写的是PA6,那肯定读不到。 解决:对照开发板原理图,确认每个引脚编号,然后在CubeMX里重新配置一遍,生成代码后再检查main.h里的引脚定义。

3.5 现象:读到的温湿度数值跳变很大

原因:DHT11本身精度有限,而且响应慢,如果你读得太频繁(比如每秒读一次),它可能还没完成上一次转换。 解决:两次读取之间至少间隔2秒,而且读完之后加一点延时再读下一次。不要用while循环连续读,那样只会得到一堆无效数据。

4. 从零跑通第一个例子:接线、配置、写代码

4.1 准备清单和接线方式

你需要一块单片机开发板(比如常见的STM32F103C8T6最小系统板)、一个DHT11模块、一个0.96寸OLED屏、几根杜邦线。接线分两部分:DHT11的数据脚接单片机的一个普通GPIO,比如PA5;OLED的SCL接单片机的I2C时钟脚,比如PB6,SDA接PB7。电源都接3.3V,地接GND。如果你用的是I2C屏幕,只需要四根线;如果是SPI屏幕,线会多一些,但新手建议先用I2C版本,简单。

4.2 用CubeMX生成初始化代码

打开CubeMX,选好芯片型号,然后做三件事:第一,把PA5配置成GPIO_Output,用于DHT11的数据线;第二,把PB6和PB7配置成I2C1的SCL和SDA;第三,配置一个定时器或者用系统滴答定时器做微秒延时。生成代码的时候,工具会自动帮你把HAL库初始化好,你只需要在main.c里填逻辑。注意:DHT11的数据线在读取时要切换输入输出模式,所以代码里要动态改引脚方向。

4.3 写DHT11读取函数

// 读取DHT11温湿度,成功返回0,失败返回1 uint8_t DHT11_Read(uint8_t *temp, uint8_t *humi) { uint8_t buf[5] = {0}; uint8_t i, j; // 主机发送开始信号:拉低至少18ms,再拉高20-40us HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); delay_us(30); // 切换为输入模式,等待DHT11响应 GPIO_InitTypeDef gpio = {0}; gpio.Pin = GPIO_PIN_5; gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &gpio); // 等待DHT11拉低80us,再拉高80us if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) { while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET); } else { return 1; // 没等到响应 } // 读取40位数据:每位以50us低电平开始,高电平长度决定0或1 for (i = 0; i < 5; i++) { for (j = 0; j < 8; j++) { while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET); delay_us(40); buf[i] <<= 1; if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET) { buf[i] |= 1; while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET); } } } // 校验:前4字节之和等于第5字节 if (buf[4] == (buf[0] + buf[1] + buf[2] + buf[3])) { *humi = buf[0]; *temp = buf[2]; return 0; } return 1; }

这段代码的关键点有三个:第一,开始信号必须拉低至少18毫秒,DHT11才会被唤醒;第二,读取每一位数据时,先等低电平结束,再延时40微秒判断高电平长短,长的是1,短的是0;第三,最后要做校验,防止数据错乱。delay_us需要你自己实现,可以用定时器或者空循环,但一定要准。如果你发现读出来全是0,先检查上拉电阻和延时精度。

4.4 写OLED显示函数

// 在OLED指定位置显示温湿度字符串 void OLED_ShowTempHumi(uint8_t temp, uint8_t humi) { char str[20]; // 显示温度 sprintf(str, "Temp: %d C", temp); OLED_ShowString(0, 0, (uint8_t *)str); // 显示湿度 sprintf(str, "Humi: %d %%", humi); OLED_ShowString(0, 2, (uint8_t *)str); }

这里假设你已经有了OLED的驱动库,比如OLED_ShowString函数,它负责把字符写到屏幕的指定行和列。sprintf用来把数字格式化成字符串,注意湿度后面要加两个百分号才能显示一个百分号。如果你没有现成的OLED库,可以先写一个简单的I2C写命令和写数据的函数,再实现显示字符。新手常犯的错误是忘了初始化OLED,或者坐标写超出范围(128×64屏幕,行号0到7,列号0到127)。

4.5 主循环里把两者串起来

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); OLED_Init(); // 初始化屏幕 OLED_Clear(); uint8_t temp = 0, humi = 0; while (1) { if (DHT11_Read(&temp, &humi) == 0) { OLED_ShowTempHumi(temp, humi); } else { OLED_ShowString(0, 4, (uint8_t *)"Read Error"); } HAL_Delay(2000); // 至少等2秒再读下一次 } }

主循环的逻辑很直白:先读DHT11,成功就显示温湿度,失败就显示错误提示,然后等2秒再循环。注意HAL_Delay(2000)不能省,否则DHT11会来不及响应。如果你发现屏幕刷新太慢,可以把显示部分放在读成功之后,但不要缩短延时。第一次跑通的时候,你可能会看到温度值比实际高一点,那是DHT11自身发热导致的,正常现象,不用纠结。

5. 下一步学什么:从这个小项目往外延伸

跑通第一个例子之后,你手里已经有了一个能工作的温湿度节点。接下来可以往三个方向走:第一,把数据通过串口传到电脑上,用串口助手看历史曲线,这样你就能记录一整天的变化;第二,加一个按键,让屏幕在摄氏度和华氏度之间切换,练习GPIO输入和状态机;第三,如果你对物联网感兴趣,可以试着把数据通过无线模块发到另一个单片机上,做一个远程监测的雏形。这三个方向都不需要推翻现在的代码,只需要在现有基础上加模块。我自己的习惯是每学一个新外设,就把它和之前的项目拼在一起,比如把DHT11的数据显示在OLED上之后,我又加了一个蜂鸣器,湿度超过80%就响一声。这样每次只学一点,但项目会越来越完整。

下面这张表可以帮你规划接下来的学习顺序:

阶段学什么大概花多久检验标准
第一步串口打印温湿度1天电脑能收到数字
第二步按键切换显示单位2天按一下屏幕变一次
第三步无线模块发送数据3到5天另一块板子能收到
第四步上位机画曲线一周能看到历史趋势图

不要急着一次全学完,我见过太多新手一口气买一堆模块,结果每个都只跑了一遍例程就放下了。从那以后我每次学新东西都强制自己只加一个模块,跑通、改参数、故意制造错误再修好,才算真正学会。希望这个DHT11加OLED的小项目能帮你迈出第一步,后面你会发现,物联网入门并没有想象中那么难。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:34:04

微信小程序分享体系全解析:转发、收藏与朋友圈实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 7:33:50

两串电池均衡实战:MP2642GR-Z前端与STM32F746VG主控的储能备电方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 7:33:50

MySQL性能优化实战:从索引设计到慢查询分析的全链路排查

我处理过太多“MySQL 变慢了怎么办”的问题&#xff0c;也见过太多人第一反应就是调my.cnf、加缓存、上分库分表&#xff0c;结果折腾一圈下来&#xff0c;瓶颈压根不在这。有一条 SQL 把线上接口拖到 3 秒&#xff0c;排查之后发现就是少了一个普通索引&#xff1b;也有项目把…

作者头像 李华
网站建设 2026/10/16 7:33:49

OpenClaw企业级部署实战:Kubernetes与GPU集群混合架构解析

只需要看一眼项目标题&#xff0c;大概就能脑补出这支团队踩过的坑了&#xff1a;“OpenClaw&#xff1a;应用与协同”这个系列做到第22篇&#xff0c;前21篇八成都在讲单机跑通、多机试跑、评测调优这些事儿&#xff0c;到了这一篇突然转向“企业级部署”&#xff0c;而且一上…

作者头像 李华
网站建设 2026/10/16 7:31:48

Spring Cloud Feign调用部分接口忽略认证的实现方案与排查指南

做微服务一年以上的团队&#xff0c;基本都会遇到这个需求&#xff1a;服务A用Feign调服务B的接口&#xff0c;但B上挂了统一的认证授权逻辑&#xff0c;导致A这套内部调用也被拦在半路。这个标题我实在熟&#xff0c;因为我自己就在好几个项目里被“部分接口忽略认证”折腾过。…

作者头像 李华
网站建设 2026/10/16 7:31:09

压缩包无损编码成PNG图片:Python像素直映射工具实战

简介&#xff1a;一款将ZIP压缩文件伪装成JPEG/PNG图片的轻量小工具&#xff0c;面向信息安全爱好者及有隐私保护需求的个人用户。压缩包与图片格式巧妙结合&#xff0c;转换后可通过修改后缀名还原解压&#xff0c;还可对内容进行加密处理&#xff0c;适合隐秘存储或安全传递敏…

作者头像 李华