温湿度传感器 DHT11时序详解

温湿度传感器 DHT11 的时序是理解这个模块的关键。只要搞懂单总线空闲高电平、主机起始信号、传感器响应信号,以及 40 位数据格式,就能更轻松地看懂逻辑分析仪波形,也能理解 ESP32 / MicroPython 代码为什么这样写。

dht11 sensor module overview

ℹ️ 核心结论:DHT11 的 DATA 单总线在空闲状态下保持高电平。主机先拉低总线发起通信,传感器再用低电平和高电平组合进行响应,随后连续发送 40 位温湿度数据。

一、温湿度传感器 DHT11 是什么

DHT11 是常见的数字温湿度传感器,通常采用 3 针或 4 针单排引脚封装。它内部集成了电阻式湿度传感器、NTC 测温元件以及简单的数据处理电路,因此可以直接输出数字量,而不是模拟电压。

DHT11 关键参数

  • 湿度测量范围:20~90%RH
  • 湿度测量精度:±5%RH
  • 温度测量范围:0~50℃
  • 温度测量精度:±2℃
  • 工作电压:DC 3.3V / 5V

DHT11 使用 单总线通信,也就是只用一根 DATA 线完成数据收发。一次完整通信大约持续 4ms,发送的数据总共 40 位,格式如下:

  • 8bit 湿度整数部分
  • 8bit 湿度小数部分
  • 8bit 温度整数部分
  • 8bit 温度小数部分
  • 8bit 校验位

二、DHT11时序中的单总线规则

理解 DHT11时序时,第一件事就是记住总线空闲状态。

DHT11 的 DATA 线在空闲时应保持为高电平,这个高电平通常由上拉电阻提供。主机或传感器需要发送信号时,都会先主动把总线拉低。拉低结束后,线路会在上拉作用下重新回到高电平。

这也是为什么在代码里经常会看到 Pin.PULL_UP。它不是可有可无的配置,而是为了让单总线在释放后回到正确的空闲状态。

💡 记忆方法

DHT11 单总线的默认状态 = 高电平空闲

谁要开始说话,谁就先把线 拉低

DHT11时序与 40 位数据格式

dht11 overall timing diagram

名称 单总线格式定义
起始信号 主机把数据总线(DATA)拉低至少 18ms,最大不超过 30ms,通知传感器准备数据。
响应信号 传感器先把数据总线(DATA)拉低约 80μs,再拉高约 80μs,以响应主机起始信号。
数据格式 传感器响应完成后,一次性从数据总线输出 40 位数据,高位先出。
湿度 高 8 位是湿度整数部分,低 8 位是湿度小数部分。
温度 高 8 位是温度整数部分,低 8 位是温度小数部分。
校验位 校验位 = 湿度高位 + 湿度低位 + 温度高位 + 温度低位。
📋 40 位数据速记

  • Byte1:湿度整数
  • Byte2:湿度小数
  • Byte3:温度整数
  • Byte4:温度小数
  • Byte5:校验位

DHT11时序的起始信号与响应信号

主机先把总线拉低至少 18ms,然后释放总线,并等待 20~40μs。这个过程就是起始信号,也可以理解成主机发出的复位信号。

dht11 host start signal timing

DHT11 检测到起始信号后,会先把总线拉低约 80μs,表示已经收到请求;然后再拉高约 80μs,表示即将开始发送数据。

dht11 response signal timing

⚠️ 注意:这里最容易混淆的点,不是“谁拉高总线”,而是“谁释放总线”。DHT11 单总线回到高电平,通常是因为器件释放线路后由上拉电阻恢复,而不是主动强推高电平。

DHT11时序里的数据位怎么判断 0 和 1

DHT11 在真正发数据时,每 1 位都从一段约 50μs 的低电平开始。低电平结束后进入高电平,高电平的持续时间决定当前位到底是 0 还是 1。

  • 高电平持续约 26~28μs,表示数据位 0
  • 高电平持续约 70μs,表示数据位 1

dht11 response signal timing

当最后 1 位传输完毕后,DHT11 会再拉低总线约 50μs,随后释放总线,线路重新回到高电平空闲状态。

dht11 micropython output example


用 MicroPython 读取 DHT11时序

下面这段代码采用“尽量贴近时序图”的写法。先发主机起始信号,再等待传感器响应,最后按 40 位数据的节奏逐位读取高电平宽度。


from machine import Pin, time_pulse_us
import dht
import time

def read_dht11_manual(pin_no=5, bit_threshold_us=50):
    """手动读取 DHT11 原始时序数据(按时序图步骤写法)。"""
    pin = Pin(pin_no, Pin.OUT, value=1)
    time.sleep_ms(1200)  # DHT11 两次采样间隔建议 >= 1s

    # 1) 主机起始信号:拉低 >=18ms,再拉高 20~40us
    pin.value(0)
    time.sleep_ms(20)
    pin.value(1)
    pin.init(Pin.IN, Pin.PULL_UP)  # 释放总线
    time.sleep_us(20)

    # 2) 传感器响应:先低约80us,再高约80us
    # 先等待响应低电平开始
    t0 = time.ticks_us()
    while pin.value() == 1:
        if time.ticks_diff(time.ticks_us(), t0) > 120:
            raise OSError("DHT11 no response")

    resp_low = time_pulse_us(pin, 0, 300)
    resp_high = time_pulse_us(pin, 1, 300)

    highs = []
    bits = []

    # 3) 读取 40 位数据
    # 每位:先 50us 低电平,再进入高电平(26~28us=0,约70us=1)
    for _ in range(40):
        time_pulse_us(pin, 0, 120)
        high_us = time_pulse_us(pin, 1, 150)
        highs.append(high_us)

        if high_us < 0:
            bits.append("?")
        elif high_us > bit_threshold_us:
            bits.append("1")
        else:
            bits.append("0")

    return resp_low, resp_high, highs, bits

def print_pulse_data(resp_low, resp_high, highs, bits):
    """打印响应脉冲与 40 位数据(按字节分组)。"""
    print("response low  =", resp_low, "us")
    print("response high =", resp_high, "us")
    print("-" * 40)

    for i in range(0, 40, 8):
        print("high%02d-%02d:" % (i, i + 7), highs[i:i + 8])

    for i in range(0, 40, 8):
        print("bit %02d-%02d:" % (i, i + 7), " ".join(bits[i:i + 8]))

def read_dht11_lib(pin_no=5):
    """使用 dht 库读取传感器数据。"""
    sensor = dht.DHT11(Pin(pin_no))
    sensor.measure()
    return sensor.temperature(), sensor.humidity()

# 主程序入口
if __name__ == "__main__":
    # 手动读取原始数据
    try:
        resp_low, resp_high, highs, bits = read_dht11_manual(5)
        print_pulse_data(resp_low, resp_high, highs, bits)
    except OSError as e:
        print("manual read error:", e)

    # 与库函数对比
    time.sleep_ms(1500)
    temp, humi = read_dht11_lib(5)

    print("-" * 40)
    print("dht lib temp  =", temp, "C")
    print("dht lib humi  =", humi, "%")
    print("expected bytes=", [humi, 0, temp, 0, (humi + temp) & 0xFF])

调试 DHT11时序时重点看什么

如果你在逻辑分析仪或串口输出里看到读数不稳定,可以优先检查下面几个点:

  1. 总线空闲时是否为高电平
  2. 主机起始信号是否真的拉低了至少 18ms
  3. 传感器响应是否出现“低约 80μs + 高约 80μs”
  4. 数据位高电平是否明显分成两档:一档接近 26~28μs,一档接近 70μs
  5. 上拉电阻是否合适,GPIO 是否已经正确切换到输入上拉模式
💡 调试思路

如果波形正常但代码总丢位,优先检查 释放总线的时机读取超时参数引脚模式切换

参考资料

延伸阅读