DHT11 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线 完成。
单总线传送数据位定义
DATA 用于微处理器与 DHT11 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。
数据格式:
8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据 + 8bit 校验位。
注:其中湿度小数部分为 0。
校验位数据定义
“8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据”8bit 校验位等于 所得结果的末 8 位。
示例一:接收到的 40 位数据为:
根据您提供的DHT11数据格式,将40位数据按照5列2行的表格形式重新组织:
| 湿度整数 | 湿度小数 | 温度整数 | 温度小数 | 校验位 |
|---|---|---|---|---|
| 00110101 | 00000000 | 00011000 | 00000100 | 01010001 |
校验位数据计算:
0011 0101+0000 0000+0001 1000+0000 0100= 0101 0001
数据时序图

实现步骤
步骤一
DHT11 上电后要等 1~2 秒,然后开始工作。因此开始前先延时 2 秒。
loop {
delay.delay_millis(2000);
//...
}
步骤二
微处理器的 IO 设置输出低电平,并且这个低电平需要保持时间至少18ms。
let _ = self.dht.pin.set_low();
self.dht.delay.delay_ms(18);
let _ = self.dht.pin.set_high();

步骤三
DHT11 收到上面的低电平后,会等低电平结束,然后再次进入高电平,这时等待时间大概是40到50微秒(有看到C的实现是40微秒,而 Rust 的实现是48微秒)。
self.dht.delay.delay_us(48);
然后输出 83 微秒的低电平作为响应信号。接着再输出87微秒的高电平通知微处理器数据准备好接收数据。

在收数据时,先等”83 微秒的低电平”结束,再等待”87 微秒的高电平”结束。
let _ = self.dht.wait_until_state(PinState::High);
let _ = self.dht.wait_until_state(PinState::Low);
wait_until_state 实现如下:
pub fn wait_until_state(&mut self, state: PinState) -> Result<(), SensorError> {
for _ in 0..DEFAULT_MAX_ATTEMPTS {
let is_state = match state {
PinState::Low => self.pin.is_low(),
PinState::High => self.pin.is_high(),
};
match is_state {
Ok(true) => return Ok(()),
Ok(false) => self.delay.delay_us(1),
Err(_) => return Err(SensorError::PinError),
}
}
Err(SensorError::Timeout)
}
之后进入数据接收阶段。
步骤四
在数据接收阶段,数据的长度为 40 bits,也就是上面列出的5个字节。 其中:
- 位数据”0”的数据表示为:54微秒的低电平和23-27微秒的高电平。
- 位数据”1”的数据表示为:54微秒的低电平和68-74微秒的高电平。
也可以认为54微秒的低电平其实是分隔位。
每一个字节的数据有8位,因此需要8次循环。
let mut byte: u8 = 0;
for n in 0..8 {
// 等待高电平的到来
match self.wait_until_state(PinState::High) {
Ok(_) => {}
Err(err) => return Err(err),
};
// 检测高电平的持续时间
self.delay.delay_us(30);
// 如果此时还是高电平,则该位为1,因为高电平的持续时间大于30微秒
// 如果此时是低电平,则该位为0,因为高电平的持续时间小于30微秒
let is_bit_1 = self.pin.is_high();
if is_bit_1.unwrap() {
// 通过位掩码将该位设置为1
let bit_mask = 1 << (7 - (n % 8));
byte |= bit_mask;
// 等待低电平的到来,准备下一次的bit读取
match self.wait_until_state(PinState::Low) {
Ok(_) => {}
Err(err) => return Err(err),
};
}
}
之前说过,接收的数据有40位,除了最后8位是校验位,其他位都是数据位。 因此需要5次上面的循环,分别读取5个字节的数据。
// read_byte 方法就是上面的逻辑
let humidity_integer = read_byte()?;
let humidity_decimal = read_byte()?;
let temperature_integer = read_byte()?;
let temperature_decimal = read_byte()?;
let checksum = read_byte()?;
数据校验 前32位数据之和,如果和校验位一致,则数据正确。
let sum = humidity_integer
.wrapping_add(humidity_decimal)
.wrapping_add(temperature_integer)
.wrapping_add(temperature_decimal);
if sum != checksum {
return Err(SensorError::ChecksumMismatch);
}
到此,数据接收完成。