一、温湿度传感器简介

        DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性和卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点,如图1所示。

图1  DHT11温湿度传感器实物图

        其中每个DHT11传感器的校准系数以程序的形式存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数,这也就导致了每个传感器的系数不一样,因此可能测得的温湿度数据值也不一样,在实际使用过程中需要加入温湿度补偿值以便让其可以适应大多数应用场合。DHT11的引脚说明见下图2所示。

图2  DHT11温湿度传感器引脚说明

        DHT11温湿度传感器采用单线制串行接口,使系统集成变得简易快捷。其拥有的超小体积、极低功耗,信号传输距离可达 60 米以上,使其成为该类应用中,在苛刻应用场合的最佳选择。

二、工作原理

       单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 4.7kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫传感器时,传感器才会应答,因此主机访问传感器都必须严格遵循单总线序列,如果出现序列混乱,传感器将不响应主机。

        DHT11温湿度传感器的功耗很低,在5V电源电压下,工作平均最大电流 0.5mA。其工作参数如下图3所示。

图3  DHT11传感器工作参数

        DHT11 数字湿温度传感器采用单总线数据格式,即单个数据引脚端口完成输入输出双向传输。DHT11上电后,需要等待1s以越过不稳定状态。其数据包由 5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。DHT11的数据格式为:

8bit湿度整数数据 8bit湿度小数数据 8bit温度整数数据 8bit温度小数数据 8bit校验和

        其中8bit校验和的值为前四个字节相加。传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。例如,某次从DHT11读到的数据如图4所示。

图4  读取DHT11温湿度数据

        由以上数据就可得到湿度和温度的值,计算方法如下:

湿度 = byte4 . byte3 45.0 (RH)

温度 = byte2 . byte1 28.0 ( )

校验 = byte4 + byte3 + byte2 + byte1 73 (湿度+温度)   --->   (校验正确)

        DHT11 和 MCU 的一次通信最大为 3ms 左右,因此在使用MCU主机连续读取温湿度传感器数据的时间间隔不要小于100ms。DHT11 的数据通信流程如图5所示。

图5  DHT11数据通信流程

        上图中每个信号的详细说明如下:t1时刻:主机(MCU)发送开始信号,即:拉低数据线,保持至少18ms;t2时刻:主机(MCU)拉高数据线20~40us,然后判断DHT11是否响应;t3时刻:DHT11拉低数据线40~50us,作为响应信号;t4时刻:DHT11 拉高数据线,保持40~50us后,开始输出数据。

        DHT11 输出数据0的时序如下图6所示。

图6  DHT11输出数据0

        DHT11 输出数据1的时序如下图7所示。

图7  DHT11 输出数据1

三、STM32驱动源码

        由于上述已经对传感器原理进行了详细描述,这里以STM32的驱动源码实现。dht11.c文件的代码如下:

#include "dht11.h"
#include "delay.h"

static STRUCT_DHT11_TYPEDEF dht11; /* 定义结构体用于保存传感器获取的数据 */

/*
 * 函数名称 : dht11_init
 * 函数功能 : 初始化DHT11的IO口(DQ)
 * 参    数 : 无
 * 返 回 值 : 无
 */
void dht11_init(void)
{
    /* ----- 1. dht11 IO口初始化 ----- */
    RCC_APB2PeriphClockCmd(DHT11_GPIO_CLK, ENABLE);     /* 使能端口时钟 */
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = DHT11_GPIO_PIN;     /* 端口配置 */
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;   /* 推挽输出 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);    /* 初始化IO口 */
    
    /* ----- 2. dht11结构体初始化 ----- */
    dht11.temperature = 0.f;
    dht11.humidity = 0.f;
    dht11.temp_offset = 0.f;
    dht11.humi_offset = 0.f;
}

/*
 * 函数名称 : dht11_io_inout
 * 函数功能 : 配置DQ引脚为输入/输出模式
 * 参    数 : dir io方向 0-输入 1-输出
 * 返 回 值 : 无
 */
static void dht11_io_inout(uint8_t dir)
{
    RCC_APB2PeriphClockCmd(DHT11_GPIO_CLK, ENABLE);         /* 使能端口时钟 */
    
    GPIO_InitTypeDef  GPIO_InitStructure;
    if( !dir ) {
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;/* 浮空输入 */
    } else {
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    /* 推挽输出 */
    }
    GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;           /* 端口配置 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);        /* 初始化IO口 */
}

/*
 * 函数名称 : dht11_start
 * 函数功能 : 向DHT11发送起始信号
 * 参    数 : 无
 * 返 回 值 : 无
 */
static void dht11_start(void)
{
    dht11_io_inout(1);  /* 配置为输出模式 */
    DHT11_DQ(0);        /* 拉低DQ引脚 */
    delay_ms(20);       /* 拉低至少18ms */
    DHT11_DQ(1);        /* 拉高DQ引脚 */
    delay_us(30);       /* 主机拉高20~40us */
}

/*
 * 函数名称 : dht11_check
 * 函数功能 : 等待DHT11的回应
 * 参    数 : 无
 * 返 回 值 : 1 -- 通信失败; 0 -- 通信成功
 */
static uint8_t dht11_check(void)
{
    uint8_t retry = 0;
    dht11_io_inout(0);  /* 配置为输入模式 */
    
    while(!DHT11_DQ_IN) {/* DHT11会拉低40~80us */
        retry ++;
        delay_us(1);
        if(retry >= 100)
            return 1;
    }
    retry = 0;
    while(DHT11_DQ_IN){/* DHT11拉低后会再次拉高40~80us */
        retry ++;
        delay_us(1);
        if(retry >= 100)
            return 1;
    }
    return 0;
}

/*
 * 函数名称 : dht11_readBit
 * 函数功能 : 从DHT11读取一个比特
 * 参    数 : 无
 * 返 回 值 : 1 -- 读取数据为1; 0 -- 读取数据为0
 */
static uint8_t dht11_readBit(void)
{
    uint8_t retry = 0;
    while(DHT11_DQ_IN && retry<100){ /* 等待变为低电平 */
        retry ++;
        delay_us(1);
    }
    retry = 0;
    while(!DHT11_DQ_IN && retry<100) { /* 等待变高电平 */
        retry ++;
        delay_us(1);
    }
    delay_us(40);   /* 等待40us 用于判断高低电平,即数据1或0 */
    if(DHT11_DQ_IN)
        return 1;
    return 0;
}

/*
 * 函数名称 : dht11_readByte
 * 函数功能 : 从DHT11读取一个字节
 * 参    数 : 无
 * 返 回 值 : 读到的数据
 */
static uint8_t dht11_readByte(void)
{
    uint8_t i,data=0;
    for(i=0;i<8;i++) {
        data <<= 1;
        data  |= dht11_readBit();
    }
    return data;
}

/* -------------------------------------- 接口函数 --------------------------------- */
/*
 * 函数名称 : dht11_measure
 * 函数功能 : dht11测量数据(温度、湿度)
 * 参    数 : 无
 * 返 回 值 : 无
 */
void dht11_measure(void)
{
    int i;
    uint8_t buf[5];
    dht11_start();
    if(!dht11_check()) {
        for(i=0;i<5;i++)//读取数据 数据格式为 湿度整数 + 湿度小数 + 温度整数 + 温度小数 + 校验和(前四位之和)
            buf[i] = dht11_readByte();
        if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) {
            dht11.temperature = buf[0] * 1.f + buf[1] * 0.1f;
            dht11.humidity    = buf[2] * 1.f + buf[3] * 0.1f;
        }
    }
}

/*
 * 函数名称 : dht11_getTemperature
 * 函数功能 : 获取dht11的温度数据
 * 参    数 : 无
 * 返 回 值 : 温度值(范围:0~50°)
 */
float dht11_getTemperature(void)
{
    return (dht11.temperature + dht11.temp_offset);
}

/*
 * 函数名称 : dht11_getHumidity
 * 函数功能 : 获取dht11的湿度数据
 * 参    数 : 无
 * 返 回 值 : 湿度值(范围:20%~90%)
 */
float dht11_getHumidity(void)
{
    return (dht11.humidity + dht11.humi_offset);
}

/*
 * 函数名称 : dht11_set_temperature_offset
 * 函数功能 : 设置dht11温度数据补偿值
 * 参    数 : 温度数据补偿值
 * 返 回 值 : 无
 */
void dht11_set_temperature_offset(float offset)
{
    dht11.temp_offset = offset;
}

/*
 * 函数名称 : dht11_set_humidity_offset
 * 函数功能 : 设置dht11湿度数据补偿值
 * 参    数 : 湿度数据补偿值
 * 返 回 值 : 无
 */
void dht11_set_humidity_offset(float offset)
{
    dht11.humi_offset = offset;
}

        dht11.h文件实现如下:

#ifndef __DHT11_H
#define __DHT11_H 

#include "stm32f10x.h"  

typedef struct {
    float temperature;  /* 温度数据 */
    float humidity;     /* 湿度数据 */
    float temp_offset;  /* 温度补偿 */
    float humi_offset;  /* 湿度补偿 */
} STRUCT_DHT11_TYPEDEF;

/* 定义DQ端口 可以直接修改该宏定义更改DQ端口引脚定义 */
#define     DHT11_GPIO_CLK      RCC_APB2Periph_GPIOC
#define     DHT11_GPIO_PORT     GPIOC
#define     DHT11_GPIO_PIN      GPIO_Pin_7

#define     DHT11_DQ(x)         GPIO_WriteBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN, (BitAction)x)
#define     DHT11_DQ_IN         GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN)

/* ----------------------------- DHT11操作函数 ----------------------------- */
void        dht11_init(void);           /* 初始化DHT11 */
void        dht11_measure(void);        /* 采集一次温湿度传感器数据 只有调用该函数后才能获取到温湿度数据 */
float       dht11_getTemperature(void); /* 读取温度数据 */
float       dht11_getHumidity(void);    /* 读取湿度数据 */
void        dht11_set_temperature_offset(float offset); /* 设置温度补偿 */
void        dht11_set_humidity_offset(float offset);    /* 设置湿度补偿 */

#endif

        代码一般驱动和使用流程如下:

1. 在dht11.h文件中修改DQ端口宏定义,使之适配自己的端口引脚;

2. 调用dht11_init()函数用于初始化传感器引脚;

3. 周期性的调用dht11_measure()函数用于测量传感器数据(注意:这里的测量/采集数据并没有返回值进行数据返回);

4. 在需要获取温度和湿度数据的地方调用dht11_getTemperature()和dht11_getHumidity()获取传感器的数据;

5. 若传感器数据与本地的温湿度数据有差异的话,可以调用dht11_set_temperature_offset()或dht11_set_humidity_offset()设置温湿度的补偿值。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐