传感器驱动系列之DHT11温湿度传感器

本文详细介绍了DHT11数字温湿度传感器的工作原理、单总线接口、通信流程以及在STM32平台上的驱动源码实现,包括初始化、数据读取和补偿值设置等内容。
摘要由CSDN通过智能技术生成

一、温湿度传感器简介

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

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

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

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

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

1.1 工作原理

       单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 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 (湿度+温度)   --->   (校验正确)

1.2 部分时序说明        

        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"
#include "stdbool.h"

static STRUCT_DHT11_TYPEDEF dht11;


/* 定义DQ端口 可以直接修改该宏定义更改DQ端口引脚定义 */
#define     DHT11_GPIO_RCLK         RCC_APB2Periph_GPIOA
#define     DHT11_GPIO_PORT         GPIOA
#define     DHT11_GPIO_PIN          GPIO_Pin_11

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

/**
 * @brief  配置DQ引脚为输入/输出模式
 * @param  out true-输出 false-输入
 * @retval 无
 */
static void dht11_set_inout(bool out)
{
    RCC_APB2PeriphClockCmd(DHT11_GPIO_RCLK, ENABLE);     /* 使能端口时钟 */
    
    GPIO_InitTypeDef  GPIO_InitStructure;
    if(out == true) {
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出 */
    }
    else {
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    /* 上拉输入 */
    }
    GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN;       /* 端口配置 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure);    /* 初始化IO口 */
}

/**
 * @brief  初始化DHT11的引脚
 * @param  无
 * @retval 无
 */
static void dht11_gpio_config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(DHT11_GPIO_RCLK, ENABLE);    /* 使能端口时钟 */
    
    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口 */
}

/**
 * @brief  初始化DHT11的结构体
 * @param  无
 * @retval 无
 */
static void dht11_struct_config(void)
{
    dht11.temperature = 0.f;
    dht11.temperature_offset = -0.f;
    
    dht11.humidity = 0.f;
    dht11.humidity_offset = -0.f;
}


/* 功能:向DHT11发送开始信号 */
static void dht11_start(void)
{
    dht11_set_inout(true);  /* 配置为输出模式 */
    DHT11_DQ(0);            /* 拉低DQ引脚 */
    delay_ms(20);           /* 拉低至少18ms */
    DHT11_DQ(1);            /* DQ = 1 */
    delay_us(30);           /* 主机拉高20~40us */
}

/*
 * 功能: 等待DHT11的回应
 * 返回值:
 *         返回1:未检测到DHT11的存在
 *         返回0:存在
 */
static unsigned char dht11_waitAck(void)
{   
    unsigned char retry = 0;
    
    dht11_set_inout(false);  /* 配置为输入模式 */
    while(!DHT11_DQ_STATUS && retry < 100) {/* DHT11会拉低80us -- 通知主机传感器正常 */
        retry ++;
        delay_us(1);
    }
    
    if(retry >= 100) return 1;
    else             retry = 0;
    
    while(DHT11_DQ_STATUS && retry < 100) {/* DHT11会拉高80us -- 通知主机准备接收数据 */
        retry ++;
        delay_us(1);
    }
    
    if(retry >= 100)    return 1;
    return 0;
}

/*
 * 功能:从DHT11读取一个位
 * 返回值:
 *        1  读取数据为1
 *        0  读取数据为0
 */
static unsigned char dht11_readBit(void)
{
    unsigned char retry = 0;
    
    dht11_set_inout(false);
    while(DHT11_DQ_STATUS && retry < 100) { /* 拉高延时准备输出 -- 等待变为低电平 */
        retry ++;
        delay_us(1);
    }
    
    retry = 0;
    while(!DHT11_DQ_STATUS && retry < 100) {  /* 先拉低50us -- 等待变为高电平 */
        retry ++;
        delay_us(1);
    }
    delay_us(30);   /* 用于判断高电平持续时间,即接收数据为1或0 */
    if(DHT11_DQ_STATUS) return 1;
    return 0;
}

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

/**
 * @brief  初始化DHT11
 * @param  无
 * @retval 无
 */
void dht11_init(void)
{
    dht11_struct_config();
    dht11_gpio_config();
}

/**
 * @brief  DHT11数据采集函数
 * @param  无
 * @retval 无
 * @note   函数执行的基准时间10us
 */
void dht11_measure(void *priv)
{
    unsigned char buf[5];
    dht11_start();
    if( !dht11_waitAck() ) {
        /* 读取数据 数据格式为 湿度整数 + 湿度小数 + 温度整数 + 温度小数 + 校验和(前四位之和) */
        for(unsigned char i=0;i<5;i++)
            buf[i] = dht11_readByte();
        
        if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) { /* 校验接收到的数据是否正确 */
            dht11.humidity = buf[0] + buf[1] * 0.1f;
            dht11.temperature = buf[2] + buf[3] * 0.1f;
        }
    }
}

/**
 * @brief  获取dht11采集的温度数据
 * @param  无
 * @retval 温度数据 0℃-50℃
 */
float dht11_get_temperature(void)
{
    float temperature = dht11.temperature + dht11.temperature_offset;
    if(temperature > 50.f) { temperature = 50.f; }
    if(temperature < 0.f)  { temperature =  0.f; }
    return (temperature);
}

/**
 * @brief  获取dht11采集的湿度数据
 * @param  无
 * @retval 湿度数据 20%RH-90%RH
 */
float dht11_get_humidity(void)
{
    float humidity = dht11.humidity + dht11.humidity_offset;
    if(humidity > 90.f) { humidity = 90.f; }
    if(humidity < 20.f) { humidity = 20.f; }
    return (humidity);
}

/**
 * @brief  设置dht11的温度补偿值
 * @param  offset - 补偿值
 * @retval 无
 */
void dht11_set_temperature_offset(float offset)
{
    dht11.temperature_offset = offset;
}

/**
 * @brief  设置dht11的湿度补偿值
 * @param  offset - 补偿值
 * @retval 无
 */
void dht11_set_humidity_offset(float offset)
{
    dht11.humidity_offset = offset;
}

        dht11.h文件实现如下:

#ifndef __DHT11_H
#define __DHT11_H

#include "stm32f10x.h"

typedef struct {
    float temperature;          /* 获取的温度数据 范围:0~50℃ */
    float temperature_offset;   /* 温度补偿值 */
    float humidity;             /* 获取的湿度数据 范围:20%~90% */
    float humidity_offset;      /* 湿度补偿值 */
} STRUCT_DHT11_TYPEDEF;

/* ------------------------- DHT11操作函数 ------------------------- */
void        dht11_init                  (void);         /* 初始化DHT11 */
void        dht11_measure               (void *priv);   /* 读取温湿度 */
float       dht11_get_temperature       (void);         /* 获取dht11采集的温度数据 */
float       dht11_get_humidity          (void);         /* 获取dht11采集的湿度数据 */
void        dht11_set_temperature_offset(float offset); /* 设置dht11的温度补偿值 */
void        dht11_set_humidity_offset   (float offset); /* 设置dht11的湿度补偿值 */

#endif

三、驱动示例

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

  • 1. 在dht11.c文件中修改DQ端口宏定义,使之适配自己的端口引脚;
  • 2. 调用dht11_init()函数用于初始化传感器引脚;
  • 3. 周期性的调用dht11_measure()函数用于测量传感器数据(注意:这里的测量/采集数据并没有返回值进行数据返回);
  • 4. 在需要获取温度和湿度数据的地方调用dht11_getTemperature()和dht11_getHumidity()获取传感器的数据;
  • 5. 若传感器数据与本地的温湿度数据有差异的话,可以调用dht11_set_temperature_offset()或dht11_set_humidity_offset()设置温湿度的补偿值。

        其具体驱动代码示例如下:

/* 硬件层 */
#include "delay.h"
#include "usart.h"
#include "timer.h"

#include "dht11.h"

/* ---------------- 变量定义区 ---------------- */
static unsigned char dht11_timer_id = 0;
static float temp, humi;

int main(void)
{
    delay_init();
    usart1_init(115200);
    basic_tim_init();
    
    /* 外设初始化 */
    dht11_init();
    /* 定时任务创建 */
    if(dht11_timer_id == 0) {
        dht11_timer_id = timer_add_task(dht11_measure, NULL, 100);
    }
    while (1) {
        temp = dht11_get_temperature();
        humi = dht11_get_humidity();
        printf("当前温度: %.3f  当前湿度: %.3f\n", temp, humi);
        delay_ms(500);
    }
}

        测试并未使用串口将数据打印出来,我这里只是通过Keil自带的Debug去调试,得到的传感器示意如下图所示。

图8   DHT11传感器数据采集示例

END

  • 33
    点赞
  • 126
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值