dht11单总线通讯协议
dht11与MCU采用单总线通讯方式。
其时序图如下:

图1 总时序图
其各个部分的时序图参考下图:

图2 主机发送信号且从机响应

图3 数字0的信号表示

图4 数字1的信号表示
因此一个完整的通讯步骤为:主机发送信号 -> 主机接收从机响应 -> 主机接收从机发来的 40bit 数据 -> 结束通讯。
数据格式为:
- 8bit湿度整数数据 + 8bit湿度小数数据 +
- 8bit温度整数数据 + 8bit温度小数数据 +
- 8bit校验和
dht11驱动代码
此处的驱动代码参考 RT-Thead 的 dht11 驱动包
这里仅截取其中硬件驱动的部分,与 RT-Thead 的 sensor 框架对接的部分暂不考虑。
1. 主机发送信号
static void dht11_reset(rt_base_t pin)
{
rt_pin_mode(pin, PIN_MODE_OUTPUT);
rt_pin_write(pin, PIN_LOW);
rt_thread_mdelay(20); /* 20ms */
rt_pin_write(pin, PIN_HIGH);
rt_hw_us_delay(30); /* 30us*/
}
主机MCU将信号线 pin 设为输出模式,拉低 20ms,然后再拉高 30us,对应图2主机部分时序图
2. 主机接收响应信号
static uint8_t dht11_check(rt_base_t pin)
{
uint8_t retry = 0;
rt_pin_mode(pin, PIN_MODE_INPUT);
while (rt_pin_read(pin) && retry < 100)
{
retry++;
rt_hw_us_delay(1);
}
if(retry >= 100)
{
return CONNECT_FAILED;
}
retry = 0;
while (!rt_pin_read(pin) && retry < 100)
{
retry++;
rt_hw_us_delay(1);
};
if(retry >= 100)
{
return CONNECT_FAILED;
}
return CONNECT_SUCCESS;
}
主机MCU将 pin 设为输入模式,然后需要在 100us 内接收到低电平,之后又需要在 100us 内接收到高电平,这样主机就成功接收从机响应信号,否则接收失败。对应图2从机部分时序图。
3. 读取 1bit
static uint8_t dht11_read_bit(rt_base_t pin)
{
uint8_t retry = 0;
while (rt_pin_read(pin) && retry < 100)
{
retry++;
rt_hw_us_delay(1);
}
retry = 0;
while (!rt_pin_read(pin) && retry < 100)
{
retry++;
rt_hw_us_delay(1);
}
rt_hw_us_delay(40);
if(rt_pin_read(pin))
return 1;
return 0;
}
主机MCU先等待读取低电平,然后再等待读取高电平,之后延时 40us,按照图3图4时序图,0 的高电平只持续 26~28us,因此此时若读取高电平则为 1,若读取低电平则为 0。
4. 读取 1byte
static uint8_t dht11_read_byte(rt_base_t pin)
{
uint8_t i, dat = 0;
for (i = 1; i <= 8; i++)
{
dat <<= 1;
dat |= dht11_read_bit(pin);
}
return dat;
}
5. 读取数据流程
整个读取流程参考下列代码:
static uint8_t dht11_read_Data(rt_base_t pin,uint8_t *temp,uint8_t *humi)
{
uint8_t i, buf[5];
dht11_reset(pin);
if(dht11_check(pin) == 0)
{
for(i=0; i<5; i++) /* read 40 bits */
{
buf[i] = dht11_read_byte(pin);
}
if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
*humi = buf[0];
*temp = buf[2];
}
}
else
{
return 1;
}
return 0;
}
其中 humi 为相对湿度,temp 为温度,此处只取了湿度和温度的整数部分。