尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

STM32驱动DHT11温湿度传感器:单总线协议与代码实现详解

STM32驱动DHT11温湿度传感器:单总线协议与代码实现详解
📅 发布时间:2026/7/18 4:26:54

在实际嵌入式开发中,温湿度传感器是环境监测类项目最基础也最常用的组件之一。DHT11 作为入门级数字温湿度传感器,因其成本低、接口简单、资料丰富,常被用于教学演示和原型验证。但新手在连接 DHT11 时,最容易卡在时序控制、数据校验和通信稳定性上,导致读出的数据全是 0 或明显异常。

本文将围绕 STM32F1 系列单片机(以野火指南者开发板为例)驱动 DHT11 传感器的完整流程,从传感器工作原理、硬件连接、时序解析到代码实现、数据校验和常见问题排查,带你完成一个可稳定运行的室内温湿度查询项目。学完后你不仅能掌握 DHT11 的驱动方法,还能理解单总线协议的关键要点,为后续使用 DS18B20、DHT22 等单总线设备打下基础。

1. 理解 DHT11 的工作机制和单总线协议

1.1 DHT11 传感器是什么

DHT11 是一款复合型温湿度传感器,内部集成了湿度传感元件、NTC 温度测量元件和一个 8 位单片机,对外提供校准后的数字信号输出。其温度测量范围 0~50°C,精度 ±2°C;湿度测量范围 20%~90%RH,精度 ±5%RH。虽然精度一般,但胜在价格低廉、接口简单,适合对精度要求不高的室内环境监测场景。

1.2 单总线协议如何工作

DHT11 采用单总线(1-Wire)协议进行通信,即只用一根数据线完成双向数据传输。这根数据线平时由上拉电阻拉到高电平,主机(MCU)和从机(DHT11)通过拉低或释放总线来传递信号。

单总线通信的核心是严格的时序要求。DHT11 的通信流程分为三步:

  1. 主机发起起始信号:主机拉低总线至少 18ms 后释放,等待 DHT11 响应。
  2. DHT11 响应信号:DHT11 拉低总线 80μs 后拉高 80μs,表示准备发送数据。
  3. 数据传输:DHT11 依次发送 40 位数据(湿度整数、湿度小数、温度整数、温度小数、校验和),每位数据以 50μs 的低电平起始,高电平的持续时间区分 0(26~28μs)和 1(70μs)。

注意:DHT11 的小数部分实际总是 0,但协议保留了该字段。校验和是前 4 个字节之和的低 8 位,用于验证数据完整性。

1.3 为什么时序控制如此关键

由于单总线没有时钟线,双方依赖时间间隔来识别比特流。如果 MCU 的延时精度不够,或中断打断了关键时序,就会导致数据错位。因此驱动 DHT11 时,必须关闭全局中断或使用高精度定时器来保证时序严格性。

2. 准备硬件环境和软件依赖

2.1 所需硬件清单

组件规格/型号备注
主控MCUSTM32F103C8T6(野火指南者)其他 STM32F1 系列也可
温湿度传感器DHT11注意是 3.3V 版本
杜邦线母对母3 根
开发板供电USB 转 TTL 或 ST-Link保证 3.3V 稳定输出
上拉电阻4.7kΩ 或 5.1kΩ开发板可能已集成

2.2 软件工具和库版本

工具/库版本用途
Keil MDK5.30+代码编写和编译
STM32CubeMX6.5.0+引脚配置和代码生成
STM32F1 HAL 库1.8.4+硬件抽象层驱动
串口调试助手任意查看输出数据

2.3 硬件连接方式

DHT11 一般有 3 个引脚(4 引脚版本中 NC 悬空):

  1. VCC:接 3.3V(注意不要接 5V,除非传感器支持且 MCU 耐受 5V 输入)
  2. GND:接 GND
  3. DATA:接 MCU 的 GPIO 引脚(如 PA0),并通过 4.7kΩ 电阻上拉到 VCC

在野火指南者开发板上,可选择 PA0 作为数据引脚,因其方便连接且不易与其他外设冲突。

3. 使用 STM32CubeMX 配置工程

3.1 创建新工程并选择 MCU

打开 STM32CubeMX,点击 "New Project",在 Part Number 搜索框中输入 "STM32F103C8",选择对应的 MCU 型号。确认引脚数为 48,Flash 为 64KB,然后点击 "Start Project"。

3.2 配置系统时钟

在 "Pinout & Configuration" 标签页的 "System Core" 中点击 "RCC",将 High Speed Clock (HSE) 设置为 "Crystal/Ceramic Resonator"。这样外部 8MHz 晶振将被启用,经 PLL 倍频后系统时钟可达 72MHz。

3.3 配置 GPIO 引脚

在左侧引脚图中找到 PA0,点击选择 "GPIO_Output"。然后在右侧 "GPIO" 设置中:

  • GPIO output level: High
  • GPIO mode: Output Push Pull
  • GPIO Pull-up/Pull-down: No pull-up and no pull-down
  • Maximum output speed: Low

注意:初始设置为输出模式是因为主机需要主动拉低总线发起通信。实际通信过程中会动态切换输入输出模式。

3.4 配置串口用于调试输出

找到 USART1(PA9 为 TX,PA10 为 RX),点击选择 "Asynchronous"。在右侧配置中:

  • Baud Rate: 115200
  • Word Length: 8 Bits
  • Parity: None
  • Stop Bits: 1
  • Over Sampling: 16 Samples

3.5 生成工程代码

点击 "Project Manager" 标签页,设置项目名称和存储路径,在 "Toolchain / IDE" 中选择 "MDK-ARM V5"。在 "Code Generator" 中勾选 "Generate peripheral initialization as a pair of '.c/.h' files per peripheral"。

最后点击 "Generate Code",STM32CubeMX 将生成完整的 Keil 工程文件。

4. 编写 DHT11 驱动代码

4.1 创建 DHT11 头文件

在 Inc 文件夹下创建dht11.h,定义数据类型和函数接口:

#ifndef __DHT11_H #define __DHT11_H #include "main.h" #include "stdint.h" // DHT11 数据类型定义 typedef struct { uint8_t humidity_int; // 湿度整数部分 uint8_t humidity_frac; // 湿度小数部分(DHT11 始终为 0) uint8_t temp_int; // 温度整数部分 uint8_t temp_frac; // 温度小数部分(DHT11 始终为 0) uint8_t checksum; // 校验和 } DHT11_Data; // 函数声明 void DHT11_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint8_t DHT11_Read(DHT11_Data* data); void DHT11_Delay_us(uint16_t us); #endif

4.2 实现微秒级延时函数

由于 DHT11 的时序要求精确到微秒级别,而 HAL 库的HAL_Delay()最小单位是毫秒,需要实现一个微秒级延时函数。在dht11.c中添加:

#include "dht11.h" // 基于 SysTick 的微秒延时函数 void DHT11_Delay_us(uint16_t us) { uint32_t ticks = us * (SystemCoreClock / 1000000) / 8; uint32_t start_tick = SysTick->VAL; uint32_t target_tick = start_tick - ticks; if (target_tick > start_tick) { // 处理计数器下溢 while (SysTick->VAL > start_tick || SysTick->VAL > target_tick); } else { while (SysTick->VAL > target_tick && SysTick->VAL <= start_tick); } }

4.3 实现 DHT11 通信核心逻辑

在dht11.c中继续添加通信函数:

static GPIO_TypeDef* DHT11_GPIO; static uint16_t DHT11_PIN; // 初始化 DHT11 引脚 void DHT11_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { DHT11_GPIO = GPIOx; DHT11_PIN = GPIO_Pin; } // 设置引脚为输出模式 static void DHT11_Set_Output(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DHT11_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DHT11_GPIO, &GPIO_InitStruct); } // 设置引脚为输入模式 static void DHT11_Set_Input(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = DHT11_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(DHT11_GPIO, &GPIO_InitStruct); } // 读取一个比特 static uint8_t DHT11_Read_Bit(void) { uint8_t result = 0; // 等待 50μs 低电平开始信号结束 while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_RESET); // 延时 40μs 后检测高电平持续时间 DHT11_Delay_us(40); if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET) { result = 1; // 等待高电平结束 while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET); } return result; } // 读取一个字节(8 个比特) static uint8_t DHT11_Read_Byte(void) { uint8_t byte = 0; for (int i = 0; i < 8; i++) { byte <<= 1; byte |= DHT11_Read_Bit(); } return byte; } // 读取完整的 DHT11 数据 uint8_t DHT11_Read(DHT11_Data* data) { if (data == NULL) return 0; // 禁用中断,保证时序严格性 __disable_irq(); // 主机发起起始信号 DHT11_Set_Output(); HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_RESET); DHT11_Delay_us(18000); // 拉低至少 18ms HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_SET); DHT11_Delay_us(30); // 主机释放总线 // 切换为输入模式等待 DHT11 响应 DHT11_Set_Input(); // 等待 DHT11 拉低响应信号 uint32_t timeout = 10000; // 超时计数器 while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET) { if (timeout-- == 0) { __enable_irq(); return 0; // 响应超时 } DHT11_Delay_us(1); } // 等待 80μs 低电平结束 timeout = 10000; while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_RESET) { if (timeout-- == 0) { __enable_irq(); return 0; // 低电平超时 } DHT11_Delay_us(1); } // 等待 80μs 高电平结束 timeout = 10000; while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET) { if (timeout-- == 0) { __enable_irq(); return 0; // 高电平超时 } DHT11_Delay_us(1); } // 读取 40 位数据 >#include "dht11.h" #include "stdio.h" // 重定向 printf 到串口 #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } DHT11_Data dht11_data; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 初始化 DHT11(连接到 PA0) DHT11_Init(GPIOA, GPIO_PIN_0); printf("DHT11 Temperature & Humidity Sensor Test\r\n"); while (1) { if (DHT11_Read(&dht11_data)) { printf("Humidity: %d.%d%% Temperature: %d.%d°C\r\n", dht11_data.humidity_int, dht11_data.humidity_frac, dht11_data.temp_int, dht11_data.temp_frac); } else { printf("DHT11 read failed!\r\n"); } HAL_Delay(2000); // 每 2 秒读取一次 } }

5.2 编译和下载

在 Keil 中点击 "Build"(F7)编译工程,确认无错误后连接 ST-Link,点击 "Download"(F8)将程序烧录到开发板。

5.3 运行和验证

打开串口调试助手,设置波特率为 115200,数据位 8,停止位 1,无校验位。给开发板上电后,应该能看到类似以下输出:

DHT11 Temperature & Humidity Sensor Test Humidity: 45.0% Temperature: 25.0°C Humidity: 45.0% Temperature: 25.0°C

用手触摸 DHT11 传感器,几分钟后应该能看到温度值缓慢上升,证明传感器工作正常。

6. 常见问题排查和解决方案

6.1 数据读取失败或全为 0

现象可能原因检查方式解决方案
始终读取失败接线错误或接触不良用万用表检查 VCC、GND、DATA 电平重新插拔杜邦线,确认 3.3V 供电稳定
数据全为 0时序控制不精确检查延时函数精度,用逻辑分析仪抓取波形优化微秒延时函数,关闭全局中断
偶尔读取失败响应超时增加超时判断和重试机制在读取函数中加入重试逻辑,最多尝试 3 次

6.2 数据明显异常(如湿度 99%)

现象可能原因检查方式解决方案
湿度值固定为 99%传感器损坏或数据引脚短路检查 DATA 引脚是否对 VCC 短路更换传感器,检查 PCB 焊接
温度值明显偏高/偏低传感器靠近热源或处于通风口将传感器移至标准环境测试避免阳光直射、空调出风口等位置
数据跳变剧烈电源噪声或电磁干扰在 VCC 和 GND 之间加 100nF 电容增加电源去耦电容,缩短接线长度

6.3 通信稳定性优化

在实际项目中,可以增加以下稳定性措施:

// 带重试的读取函数 uint8_t DHT11_Read_With_Retry(DHT11_Data* data, uint8_t max_retry) { for (int i = 0; i < max_retry; i++) { if (DHT11_Read(data)) { return 1; // 读取成功 } HAL_Delay(100); // 失败后延时 100ms 再重试 } return 0; // 所有重试都失败 }

7. 生产环境注意事项和扩展方向

7.1 从学习环境到生产环境的改进

学习环境中的代码侧重于功能验证,生产环境还需要考虑:

  1. 错误处理机制:不是简单打印失败信息,而要记录错误日志或触发告警。
  2. 数据滤波算法:连续读取 5 次数据,去掉最大最小值后取平均,避免偶发误差。
  3. 低功耗设计:电池供电场景下,读取间隔可延长至 1-5 分钟,其余时间 MCU 进入睡眠模式。
  4. 看门狗保护:防止程序卡死在传感器读取循环中。

7.2 传感器选型建议

当 DHT11 的精度无法满足需求时,可以考虑:

传感器温度精度湿度精度接口适用场景
DHT22±0.5°C±2%RH单总线一般室内监测
AHT20±0.3°C±2%RHI2C智能家居设备
SHT4x±0.2°C±1.8%RHI2C高精度环境监测

7.3 项目扩展思路

基于 DHT11 的温湿度读取能力,可以扩展为:

  1. 智能温湿度计:增加 OLED 显示屏,实时显示数值和变化趋势。
  2. 环境监测节点:结合 ESP8266/ESP32,将数据上传到云平台。
  3. 智能通风系统:当湿度超过阈值时自动控制风扇或除湿器。
  4. 数据记录仪:添加 SD 卡模块,长期记录环境数据。

驱动 DHT11 的关键在于理解单总线协议的时序要求,并在代码中精确控制时间间隔。实际项目中,建议先用逻辑分析仪验证通信波形,确认时序正确后再进行软件调试。对于需要更高精度的场景,I2C 接口的 AHT20、SHT4x 等传感器是更好的选择,它们虽然价格稍高,但通信更稳定、数据更可靠。

相关新闻

  • Spring Gateway+Sa-Token+Nacos构建微服务统一认证方案
  • 江诗丹顿中国官方售后服务中心|服务电话和详细网点地址权威信息声明(2026年7月更新) - 江诗丹顿服务中心
  • Java工程师进阶指南:从基础到高级的技术成长路径

最新新闻

  • 亨得利官方名表服务中心|详细地址与官方热线权威信息通知(2026年7月更新) - 亨得利官方
  • HLW8112芯片软件实现代码(17)
  • MCP协议如何优化长上下文AI开发流程
  • 技术博客写作指南:编程语言、框架与开发工具实践
  • AI技术如何革新传统网络爬虫
  • 无人机生态环境监测、图像处理与GIS数据分析综合实践技术应用

日新闻

  • 宝珀中国官方售后服务中心|官方热线和维修地址权威信息声明(2026年7月更新) - 宝珀官方售后服务中心
  • # 2026年北京知识产权律师推荐怎么选?看这五点关键不踩雷 - 本地品牌推荐
  • 2026实测教程:生成的拼豆图纸不满意怎么修改才省事 - 省事研究所

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号