DHT11详细介绍(内含基于51和STM32平台的驱动代码)

一、DHT11基础储备

DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,内部由一个 8 位单片机控制一个电阻式感湿元件和一个 NTC 测温元件。DHT11 虽然也是采用单总线协议,但是该协议与 DS18B20 的单总线协议稍微有些不同之处。

相比于 DS18B20 只能测量温度,DHT11 既能检测温度又能检测湿度,不过 DHT11 的精度和测量范围都要低于 DS18B20,其温度测量范围为 0~50℃,误差在±2℃;湿度的测量范围为 20%~90%RH(Relative Humidity 相对湿度—指空气中水汽压与饱和水汽压的百分比),误差在±5%RH。DHT11 电路很简单,只需要将 Dout 引脚连接单片机的一个 I/O 即可,不过该引脚需要上拉一个 ==5K== 的电阻,DHT11 的供电电压为 ==3~5.5V==

image.png

二、协议及数据格式

DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。==一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输==

数据格式为:==8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和==,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。

==DHT11 只有在接收到开始信号后才触发一次温湿度采集==,如果没有接收到==主机发送复位信号==,DHT11 不主动进行温湿度采集。当数据采集完毕且无开始信号后,DHT11 自动切换到低速模式。

<font color=red>注意:由于 DHT11 时序要求非常严格,所以在操作时序的时候,为了防止中断干扰总线时序,先关闭总中断,操作完毕后再打开总中断。

三、操作时序

1、 主机发送复位信号

DHT11 的初始化过程同样分为==复位信号和响应信号。==
首先主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,取中间值 30us,此时复位信号发送完毕。

image.png

2、DHT11 发送响应信号

DHT11 检测到复位信号后,触发一次采样,并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了;然后 DHT11 拉高总线 80us,之后开始传输数据。如果检测到响应信号为高电平,则 DHT11 初始化失败,请检查线路是否连接正常。

当复位信号发送完毕后,如果检测到总线被拉低,就每隔 1us 计数一次,直至总线拉高,计算低电平时间;当总线被拉高后重新计数检测 80us 的高电平。如果检测到响应信号之后的80us 高电平,就准备开始接收数据。实际上 DHT11 的响应时间并不是标准的 80us,往往存在误差,当响应时间处于 ==20~100us== 之间时就可以认定响应成功。

3、数据传输

DHT11 在拉高总线 80us 后开始传输数据。每 1bit 数据都以 50us 低电平时隙开始,告诉主机开始传输一位数据了。DHT11 以高电平的长短定义数据位是 0 还是 1,当 50us 低电平时隙过后拉高总线,==高电平持续 26~28us 表示数据“0”==;==持续 70us 表示数据“1”。==

最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。

image.png

4、区分数据0/1的巧法

还是像检测响应时间那样计算高电平持续时间那就太麻烦了!!!

数据“0”的高电平持续 26~28us,数据“1”的高电平持续70us,每一位数据前都有 50us 的起始时隙。如果我们取一个中间值 40us 来区分数据“0”和数据“1”的时隙。

当数据位之前的 50us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 70us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 50us 的开始时隙,那么上一位数据肯定是“0”。

为什么延时 40us?
由于误差的原因,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长。
当数据“0”时隙大于 26~28us 时,
如果延时太短,无法判断当前处于数据“0”的时隙还是数据“1”的时隙;
如果延时太长,则会错过下一位数据前的开始时隙,导致检测不到后面的数据

四、51及STM32例程

51对应的

dht11.c

#include "config.h"
#include "delay.h"

//1-检测到响应信号 0-未检测到
u8 DHT11RstAndCheck(void)
{
    u8 timer = 0;
    
    EA = 0;
    DHT11 = 0;
    Delay20ms();
    DHT11 = 1;
    Delay30us();
    while(!DHT11)
    {
        timer++;
        Delay1us();
    }
    if(timer>100 || timer<20)
    {
        EA = 1;
        return 0;
    }
    timer = 0;
    while(DHT11)
    {
        timer++;
        Delay1us();
    }
    EA = 1;
    if(timer>100 || timer<20)
    {
        return 0;
    }
    return 1;
}

//读一字节数据
u8 DHT11ReadByte(void)
{
    u8 i;
    u8 byt = 0;
    
    EA = 0;
    for(i=0; i<8; i++)
    {
        while(DHT11);
        while(!DHT11);
        Delay40us();
        byt <<= 1;
        if(DHT11)
        {
            byt |= 0x01;
        }
    }
    EA = 1;
    return byt;
}

//读取一次数据(整数) 0-读取失败 1-读取成功
u8 DHT11ReadData(u8 *Humi, u8 *Temp)
{
    u8 sta = 0;
    u8 i;
    u8 buf[5];
    
    if(DHT11RstAndCheck())
    {
        for(i=0; i<5; i++)
        {
            buf[i] = DHT11ReadByte();
        }
        if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4])
        {
            *Humi = buf[0];
            *Temp = buf[2];
        }
        sta = 1;
    }
    else
    {
        *Humi = 0xFF;
        *Temp = 0xFF;
        sta = 0;
    }
    return sta;
}

dht11.h

#ifndef DHT11_H
#define DHT11_H

u8 DHT11ReadData(u8 *Humi, u8 *Temp);

#endif

STM32对应的

dht11.c

#include "dht11.h"

/*DHT11复位和检测响应函数,返回值:1-检测到响应信号;0-未检测到响应信号*/
u8 DHT11RstAndCheck(void)
{
    u8 timer = 0;

    __set_PRIMASK(1);          //关总中断
    DHT11_OUT = 0;             //输出低电平
    delay_ms(20);              //拉低至少18ms
    DHT11_OUT = 1;             //输出高电平
    delay_us(30);              //拉高20~40us
    while (!DHT11_IN)          //等待总线拉低,DHT11会拉低40~80us作为响应信号
    {
        timer++;               //总线拉低时计数
        delay_us(1);
    }
    if (timer>100 || timer<20) //判断响应时间
    {
        __set_PRIMASK(0);      //开总中断
        return 0;
    }
    timer = 0;
    while (DHT11_IN)           //等待DHT11释放总线,持续时间40~80us
    {
        timer++;               //总线拉高时计数
        delay_us(1);
    }
    __set_PRIMASK(0);          //开总中断
    if (timer>100 || timer<20) //检测响应信号之后的高电平
    {
        return 0;
    }
    return 1;
}

/*读取一字节数据,返回值-读到的数据*/
u8 DHT11ReadByte(void)
{
    u8 i;
    u8 byt = 0;

    __set_PRIMASK(1);      //关总中断
    for (i=0; i<8; i++)
    {
        while (DHT11_IN);  //等待低电平,数据位前都有50us低电平时隙
        while (!DHT11_IN); //等待高电平,开始传输数据位
        delay_us(40);
        byt <<= 1;         //因高位在前,所以左移byt,最低位补0
        if (DHT11_IN)      //将总线电平值读取到byt最低位中
        {
            byt |= 0x01;
        }
    }
    __set_PRIMASK(0);      //开总中断

    return byt;
}

/*读取一次数据,返回值:Humi-湿度整数部分数据,Temp-温度整数部分数据;返回值: -1-失败,1-成功*/
u8 DHT11ReadData(u8 *Humi, u8 *Temp)
{
    s8 sta = 0;
    u8 i;
    u8 buf[5];

    if (DHT11RstAndCheck())         //检测响应信号
    {
        for(i=0;i<5;i++)            //读取40位数据
        {
            buf[i]=DHT11ReadByte(); //读取1字节数据
        }
        if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4]) //校验成功
        {
            *Humi = buf[0];         //保存湿度整数部分数据
            *Temp = buf[2];         //保存温度整数部分数据
        }
        sta = 1;
    }
    else //响应失败返回-1
    {
        *Humi = 0xFF; //响应失败返回255
        *Temp = 0xFF; //响应失败返回255
        sta = -1;
    }
    
    return sta;    
}

/*DHT11初始化函数*/
u8 DHT11Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能GPIOC端口时钟
    GPIO_SetBits(GPIOC,GPIO_Pin_13);                    //设置PC13输出高电平,(先设置引脚电平可以避免IO初始化过程中可能产生的毛刺)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;          //设置DHT11数据引脚->PC13
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;    //设置为开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //设置输出速率为50MHz
    GPIO_Init(GPIOC, &GPIO_InitStructure);              //初始化GPIOC端口

    return DHT11RstAndCheck();                          //返回DHT11状态
}

dht11.h

#ifndef _DHT11_H
#define _DHT11_H
#include "config.h"

#define  DHT11_OUT  PC_OUT(13)
#define  DHT11_IN   PC_IN(13)

u8 DHT11Init(void);
u8 DHT11ReadData(u8 *Humi,u8 *Temp);

#endif


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容