DHT11 是一款价格低廉、易于使用的数字温湿度传感器。它能够测量环境的温度和相对湿度,并将数据以数字信号的形式输出。由于其简单性、成本效益和易于集成到微控制器项目中,DHT11 在业余爱好者项目、DIY 智能家居以及一些非关键性物联网 (IoT) 应用中非常流行。

核心思想: DHT11 通过单总线 (Single-Wire) 接口与微控制器通信,提供经过校准的温度和湿度数字读数。


一、DHT11 概述

DHT11 传感器内部包含一个电阻式湿敏元件和一个 NTC 热敏电阻(用于温度测量),并集成了一个 8 位微控制器来处理模拟信号转换为数字信号,并进行校准和输出。

1.1 主要特性

  • 供电电压:3V ~ 5.5V DC
  • 电流消耗
    • 测量时:约 2.5mA
    • 待机时:100uA ~ 150uA
  • 湿度测量范围:20% ~ 90% RH (相对湿度)
    • 精度:±5% RH
    • 分辨率:1% RH
  • 温度测量范围:0°C ~ 50°C
    • 精度:±2°C
    • 分辨率:1°C
  • 输出信号:单总线数字信号 (Single-Wire Digital Signal)
  • 采样周期:最低 1 秒(建议不要少于 2 秒,以保证传感器完全稳定)
  • 封装:通常是 4 引脚或 3 引脚的 SIP (Single In-line Package) 模块。

1.2 优点与缺点

优点:

  • 价格低廉:非常经济实惠。
  • 接口简单:只需一根数据线与微控制器连接。
  • 数字输出:直接输出数字信号,无需额外 ADC 转换。
  • 集成度高:内置校准,方便使用。

缺点:

  • 精度较低:湿度 ±5%RH,温度 ±2°C,对于需要高精度测量的应用可能不够。
  • 响应速度慢:采样周期较长,不适合快速变化的环境。
  • 测量范围窄:尤其是温度和湿度的高端/低端限制。
  • 稳定性一般:长期稳定性不如更高端的传感器。
  • 通信协议比较严格:对时序要求较高,否则可能读取失败。

1.3 引脚定义

标准的 DHT11 模块通常有 3 个或 4 个引脚:

引脚名称 功能描述
VCC 供电电源 (3V ~ 5.5V)
Data 单总线数据输入/输出引脚
NC (Not Connected) 未连接 (4 引脚版本有此引脚)
GND 接地

连接注意事项:

  • 上拉电阻:DHT11 的数据线 (Data Pin) 必须连接一个 4.7KΩ ~ 10KΩ 的上拉电阻到 VCC。这是因为 DHT11 的数据线是开漏 (Open-Drain) 输出,需要外部上拉才能输出高电平。
  • 电源稳定性:确保 VCC 供电稳定。

二、DHT11 通信协议 (单总线)

DHT11 采用一种独特的单总线协议进行通信,微控制器通过控制数据线的电平变化来与传感器进行交互。整个通信过程可以分为以下几个步骤:

  1. 主机发送开始信号 (Start Signal)
  2. DHT11 响应信号 (Response Signal)
  3. DHT11 发送数据 (40 位数据)

整个通信过程对时序要求非常严格,需要微控制器精确控制数据线的电平保持时间。

2.1 协议详解

关键时序点:

  • 主机发送开始信号
    • 拉低数据线至少 18ms (DHT11 内部检测到总线由高到低,并持续 18ms 以上,判断为开始信号)。
    • 拉高数据线 20-40us (释放总线,等待 DHT11 响应)。
  • DHT11 响应信号
    • 拉低数据线 80us。
    • 拉高数据线 80us。
  • DHT11 发送数据 (40 位)
    • 每位数据开始:DHT11 先拉低数据线 50us。
    • 数据 ‘0’:紧接着拉高数据线 26-28us。
    • 数据 ‘1’:紧接着拉高数据线 70us。
    • MCU 采样:微控制器在 DHT11 拉低 50us 后,等待一小段时间(如 20us),然后读取数据线的电平。如果此时为低电平,则为 ‘0’;如果为高电平,则为 ‘1’。

2.2 40 位数据格式

DHT11 每次发送 40 位数据,分为 5 个字节:

字节序号 数据内容 位数 范围
Byte 1 湿度整数部分 8 0~99%RH
Byte 2 湿度小数部分 8 始终为 0
Byte 3 温度整数部分 8 0~50°C
Byte 4 温度小数部分 8 始终为 0
Byte 5 校验和 8 (Byte1+Byte2+Byte3+Byte4) 的低 8 位

校验和验证
读取完 40 位数据后,将前 4 个字节相加,如果其低 8 位与第 5 个字节(校验和)相等,则说明数据传输正确。

三、DHT11 在微控制器中的实现

实现 DHT11 驱动程序需要精确控制 IO 口的时序,通常会用到微控制器内部的定时器或简单的延时函数。

3.1 伪代码示例 (C 语言)

以下是一个简化的伪代码,展示了与 DHT11 交互的核心逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 假设 DATA_PIN 是连接到 DHT11 数据线的微控制器引脚
// 假设 delay_us() 和 delay_ms() 是微秒和毫秒延时函数
// 假设 set_pin_output() 和 set_pin_input() 设置引脚方向
// 假设 write_pin() 和 read_pin() 设置/读取引脚电平

func read_dht11_data() (temperature int, humidity int, err error) {
// 1. MCU 发送开始信号
set_pin_output(DATA_PIN) // 设置为输出模式
write_pin(DATA_PIN, LOW) // 拉低数据线
delay_ms(18) // 保持至少 18ms
write_pin(DATA_PIN, HIGH) // 拉高数据线 (释放总线)
delay_us(40) // 保持 20-40us

// 2. MCU 切换为输入模式,等待 DHT11 响应
set_pin_input(DATA_PIN) // 设置为输入模式

// 等待 DHT11 拉低 (响应开始)
timeout_count := 0
for read_pin(DATA_PIN) == HIGH {
delay_us(1)
timeout_count++
if timeout_count > 100 { // 超过 100us 仍未拉低,表示无响应
return 0, 0, errors.New("DHT11 no response (low)")
}
}

// 等待 DHT11 拉高 (响应结束)
timeout_count = 0
for read_pin(DATA_PIN) == LOW {
delay_us(1)
timeout_count++
if timeout_count > 100 { // 超过 100us 仍未拉高,表示无响应
return 0, 0, errors.New("DHT11 no response (high)")
}
}

// 3. 读取 40 位数据
var data [5]byte
for i := 0; i < 5; i++ { // 循环读取 5 个字节
for j := 0; j < 8; j++ { // 循环读取每个字节的 8 位
// 等待 DHT11 拉低 50us (数据起始信号)
timeout_count = 0
for read_pin(DATA_PIN) == HIGH {
delay_us(1)
timeout_count++
if timeout_count > 100 { return 0, 0, errors.New("DHT11 data start timeout (low)") }
}
timeout_count = 0
for read_pin(DATA_PIN) == LOW { // 持续拉低 50us
delay_us(1)
timeout_count++
if timeout_count > 60 { return 0, 0, errors.New("DHT11 data start timeout (high from low)") } // 50us + 10us 裕量
}

// 采样数据电平 (等待约 20-30us,在拉高期间采样)
// 拉低 50us 结束后,DHT11会拉高数据线
// 如果拉高持续 26-28us 是 '0',如果拉高持续 70us 是 '1'
// 我们在拉高开始后,等待一个合适的时间点 (如 30us) 进行采样
delay_us(30) // 等待进入数据电平高位段的稳定区域

if read_pin(DATA_PIN) == HIGH { // 此时仍为高电平,说明拉高持续时间长,为 '1'
data[i] = (data[i] << 1) | 1 // 写入 '1'
} else { // 此时已变为低电平 (或者在 30us 后依然是低电平,理论上不应该发生),为 '0'
data[i] = (data[i] << 1) | 0 // 写入 '0'
}

// 等待当前数据位完全结束 (对于 '1' 需要等待 70us,'0' 需要等待 28us)
// 这里可以直接等待到下一个拉低信号的开始,或者简单等待一个最大时间
// 更稳健的做法是等待拉低信号出现,表示本位结束
timeout_count = 0
for read_pin(DATA_PIN) == HIGH { // 等待拉高结束,总线变为低电平 (或等待下一个数据位的起始拉低)
delay_us(1)
timeout_count++
if timeout_count > 100 { return 0, 0, errors.New("DHT11 data bit end timeout") } // 防止卡死
}
}
}

// 4. 校验数据
checksum := (data[0] + data[1] + data[2] + data[3]) & 0xFF
if checksum != data[4] {
return 0, 0, errors.New("DHT11 checksum error")
}

// 5. 解析数据
humidity = int(data[0])
temperature = int(data[2])
return temperature, humidity, nil
}

注意事项:

  • 精确延时delay_us() 函数的精度至关重要。在不同的微控制器上,实现精确延时可能需要查阅手册,或者使用定时器中断等机制。
  • 中断禁用:在读取 DHT11 数据时,为了保证时序的精确性,通常建议禁用中断或使用中断安全的方式进行 IO 操作。
  • 多次尝试:由于环境干扰或时序偶然性,偶尔读取失败是正常的。可以在主程序中加入重试机制。
  • 采样间隔:严格遵守 DHT11 建议的至少 1-2 秒的采样间隔。

四、常见问题与排查

  1. 传感器无响应 (始终读高或读低)
    • 检查上拉电阻是否连接,阻值是否合适 (4.7KΩ ~ 10KΩ)。
    • 检查供电 VCC 和 GND 是否正确连接且稳定。
    • 检查微控制器 IO 口是否损坏。
    • DHT11 是否损坏。
  2. 读数错误或校验和错误
    • 时序问题:这是最常见的问题。检查你的延时函数是否精确,是否符合 DHT11 协议的时序要求。确保在读取数据期间没有其他中断或耗时操作干扰。
    • 数据线连接问题:虚焊、接触不良等。
    • 电源波动:电源不稳可能导致传感器工作异常。
    • 采样间隔不足:DHT11 两次采样之间需要至少 1 秒的稳定时间。
  3. 读数固定不变或为 0/负值
    • 通常是传感器未正确初始化或数据传输失败。
    • 检查代码逻辑,确保所有 40 位数据都被正确读取。
    • DHT11 损坏。

五、总结

DHT11 是一款非常适合初学者和非关键性应用的温湿度传感器。尽管其精度和响应速度有限,但其低成本和简单的数字接口使其成为许多 DIY 项目的理想选择。成功驱动 DHT11 的关键在于精确地遵循其单总线通信协议的时序要求。通过理解其工作原理和协议细节,并注意一些常见的坑,你可以轻松地将 DHT11 集成到你的微控制器项目中,实现环境数据的监测。对于需要更高精度、更快响应或更宽测量范围的应用,则应考虑 DHT22 (AM2302)、SHT30 等更高级的传感器。